<commit_before>/*
 * Copyright 2016 DiffPlug
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.diffplug.gradle.spotless;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.SkipWhenEmpty;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.incremental.IncrementalTaskInputs;

import com.diffplug.spotless.Formatter;
import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.LineEnding;
import com.diffplug.spotless.PaddedCellBulk;

public class SpotlessTask extends DefaultTask {
	// set by SpotlessExtension, but possibly overridden by FormatExtension
	@Input
	protected String encoding = "UTF-8";

	public String getEncoding() {
		return encoding;
	}

	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}

	@Input
	protected LineEnding.Policy lineEndingsPolicy = LineEnding.UNIX.createPolicy();

	public LineEnding.Policy getLineEndingsPolicy() {
		return lineEndingsPolicy;
	}

	public void setLineEndingsPolicy(LineEnding.Policy lineEndingsPolicy) {
		this.lineEndingsPolicy = lineEndingsPolicy;
	}

	// set by FormatExtension
	@Input
	protected boolean paddedCell = false;

	public boolean isPaddedCell() {
		return paddedCell;
	}

	public void setPaddedCell(boolean paddedCell) {
		this.paddedCell = paddedCell;
	}

	@InputFiles
	@SkipWhenEmpty
	protected Iterable<File> target;

	public Iterable<File> getTarget() {
		return target;
	}

	public void setTarget(Iterable<File> target) {
		this.target = target;
	}

	@Input
	protected List<FormatterStep> steps = new ArrayList<>();

	public List<FormatterStep> getSteps() {
		return Collections.unmodifiableList(steps);
	}

	public void setSteps(List<FormatterStep> steps) {
		this.steps = steps;
	}

	public boolean addStep(FormatterStep step) {
		return this.steps.add(step);
	}

	private boolean check = false;
	private boolean apply = false;

	public void setCheck() {
		this.check = true;
	}

	public void setApply() {
		this.apply = true;
	}

	/** Returns the name of this format. */
	String formatName() {
		String name = getName();
		if (name.startsWith(SpotlessPlugin.EXTENSION)) {
			return name.substring(SpotlessPlugin.EXTENSION.length()).toLowerCase(Locale.ROOT);
		} else {
			return name;
		}
	}

	@TaskAction
	public void performAction(IncrementalTaskInputs inputs) throws Exception {
		if (target == null) {
			throw new GradleException("You must specify 'Iterable<File> toFormat'");
		}
		if (!check && !apply) {
			throw new GradleException("Don't call " + getName() + " directly, call " + getName() + SpotlessPlugin.CHECK + " or " + getName() + SpotlessPlugin.APPLY);
		}

		// create the formatter
		Formatter formatter = Formatter.builder()
				.lineEndingsPolicy(lineEndingsPolicy)
				.encoding(Charset.forName(encoding))
				.rootDir(getProject().getProjectDir().toPath())
				.steps(steps)
				.build();
		// find the outOfDate files
		List<File> outOfDate = new ArrayList<>();
		inputs.outOfDate(inputDetails -> {
			File file = inputDetails.getFile();
			if (file.isFile()) {
				outOfDate.add(file);
			}
		});

		if (apply) {
			apply(formatter, outOfDate);
		}
		if (check) {
			check(formatter, outOfDate);
		}
	}

	private void apply(Formatter formatter, List<File> outOfDate) throws Exception {
		if (isPaddedCell()) {
			for (File file : outOfDate) {
				getLogger().debug("Applying format to " + file);
				PaddedCellBulk.apply(formatter, file);
			}
		} else {
			boolean anyMisbehave = false;
			for (File file : outOfDate) {
				getLogger().debug("Applying format to " + file);
				String unixResultIfDirty = formatter.applyToAndReturnResultIfDirty(file);
				// because apply will count as up-to-date, it's important
				// that every call to apply will get a PaddedCell check
				if (!anyMisbehave && unixResultIfDirty != null) {
					String onceMore = formatter.compute(unixResultIfDirty, file);
					//  f(f(input) == f(input) for an idempotent function
					if (!onceMore.equals(unixResultIfDirty)) {
						anyMisbehave = true;
					}
				}
			}
			if (anyMisbehave) {
				throw PaddedCellGradle.youShouldTurnOnPaddedCell(this);
			}
		}
	}<commit_msg>It's somewhat common for a rule to be non-idempotent, but converge to a stable value after a few applications.  Rather than griefing the user for trivial glitches, if spotless can solve the problem on its own, we'll just let it do so silently.<commit_after>/*
 * Copyright 2016 DiffPlug
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.diffplug.gradle.spotless;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

import org.gradle.api.DefaultTask;
import org.gradle.api.GradleException;
import org.gradle.api.tasks.Input;
import org.gradle.api.tasks.InputFiles;
import org.gradle.api.tasks.SkipWhenEmpty;
import org.gradle.api.tasks.TaskAction;
import org.gradle.api.tasks.incremental.IncrementalTaskInputs;

import com.diffplug.spotless.Formatter;
import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.LineEnding;
import com.diffplug.spotless.PaddedCell;
import com.diffplug.spotless.PaddedCellBulk;

public class SpotlessTask extends DefaultTask {
	// set by SpotlessExtension, but possibly overridden by FormatExtension
	@Input
	protected String encoding = "UTF-8";

	public String getEncoding() {
		return encoding;
	}

	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}

	@Input
	protected LineEnding.Policy lineEndingsPolicy = LineEnding.UNIX.createPolicy();

	public LineEnding.Policy getLineEndingsPolicy() {
		return lineEndingsPolicy;
	}

	public void setLineEndingsPolicy(LineEnding.Policy lineEndingsPolicy) {
		this.lineEndingsPolicy = lineEndingsPolicy;
	}

	// set by FormatExtension
	@Input
	protected boolean paddedCell = false;

	public boolean isPaddedCell() {
		return paddedCell;
	}

	public void setPaddedCell(boolean paddedCell) {
		this.paddedCell = paddedCell;
	}

	@InputFiles
	@SkipWhenEmpty
	protected Iterable<File> target;

	public Iterable<File> getTarget() {
		return target;
	}

	public void setTarget(Iterable<File> target) {
		this.target = target;
	}

	@Input
	protected List<FormatterStep> steps = new ArrayList<>();

	public List<FormatterStep> getSteps() {
		return Collections.unmodifiableList(steps);
	}

	public void setSteps(List<FormatterStep> steps) {
		this.steps = steps;
	}

	public boolean addStep(FormatterStep step) {
		return this.steps.add(step);
	}

	private boolean check = false;
	private boolean apply = false;

	public void setCheck() {
		this.check = true;
	}

	public void setApply() {
		this.apply = true;
	}

	/** Returns the name of this format. */
	String formatName() {
		String name = getName();
		if (name.startsWith(SpotlessPlugin.EXTENSION)) {
			return name.substring(SpotlessPlugin.EXTENSION.length()).toLowerCase(Locale.ROOT);
		} else {
			return name;
		}
	}

	@TaskAction
	public void performAction(IncrementalTaskInputs inputs) throws Exception {
		if (target == null) {
			throw new GradleException("You must specify 'Iterable<File> toFormat'");
		}
		if (!check && !apply) {
			throw new GradleException("Don't call " + getName() + " directly, call " + getName() + SpotlessPlugin.CHECK + " or " + getName() + SpotlessPlugin.APPLY);
		}

		// create the formatter
		Formatter formatter = Formatter.builder()
				.lineEndingsPolicy(lineEndingsPolicy)
				.encoding(Charset.forName(encoding))
				.rootDir(getProject().getProjectDir().toPath())
				.steps(steps)
				.build();
		// find the outOfDate files
		List<File> outOfDate = new ArrayList<>();
		inputs.outOfDate(inputDetails -> {
			File file = inputDetails.getFile();
			if (file.isFile()) {
				outOfDate.add(file);
			}
		});

		if (apply) {
			apply(formatter, outOfDate);
		}
		if (check) {
			check(formatter, outOfDate);
		}
	}

	private void apply(Formatter formatter, List<File> outOfDate) throws Exception {
		if (isPaddedCell()) {
			for (File file : outOfDate) {
				getLogger().debug("Applying format to " + file);
				PaddedCellBulk.apply(formatter, file);
			}
		} else {
			boolean anyMisbehave = false;
			for (File file : outOfDate) {
				getLogger().debug("Applying format to " + file);
				String unixResultIfDirty = formatter.applyToAndReturnResultIfDirty(file);
				// because apply will count as up-to-date, it's important
				// that every call to apply will get a PaddedCell check
				if (!anyMisbehave && unixResultIfDirty != null) {
					String onceMore = formatter.compute(unixResultIfDirty, file);
					//  f(f(input) == f(input) for an idempotent function
					if (!onceMore.equals(unixResultIfDirty)) {
						// it's not idempotent.  but, if it converges, then it's likely a glitch that won't reoccur,
						// so there's no need to make a bunch of noise for the user
						PaddedCell result = PaddedCell.check(formatter, file, onceMore);
						if (result.type() == PaddedCell.Type.CONVERGE) {
							String finalResult = formatter.computeLineEndings(result.canonical(), file);
							Files.write(file.toPath(), finalResult.getBytes(formatter.getEncoding()), StandardOpenOption.TRUNCATE_EXISTING);
						} else {
							// it didn't converge, so the user is going to need padded cell mode
							anyMisbehave = true;
						}
					}
				}
			}
			if (anyMisbehave) {
				throw PaddedCellGradle.youShouldTurnOnPaddedCell(this);
			}
		}
	}
{'fraction_non_alphanumeric': 0.10657177188279877, 'fraction_numerical': 0.0016637397171642481, 'mean_word_length': 4.075046904315197, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7152763', 'n_tokens_mistral': 3741, 'n_tokens_neox': 3320, 'n_words': 905}