/** The license that we'd like enforced. */ private LicenseHeaderStep(String licenseHeader, String delimiter, String yearSeparator) { if (delimiter.contains("\n")) { throw new IllegalArgumentException("The delimiter must not contain any newlines."); } // sanitize the input license licenseHeader = LineEnding.toUnix(licenseHeader); if (!licenseHeader.endsWith("\n")) { licenseHeader = licenseHeader + "\n"; } this.licenseHeader = licenseHeader; this.delimiterPattern = Pattern.compile('^' + delimiter, Pattern.UNIX_LINES | Pattern.MULTILINE); this.hasYearToken = licenseHeader.contains("$YEAR"); if (this.hasYearToken) { int yearTokenIndex = licenseHeader.indexOf("$YEAR"); this.licenseHeaderBeforeYearToken = licenseHeader.substring(0, yearTokenIndex); this.licenseHeaderAfterYearToken = licenseHeader.substring(yearTokenIndex + 5, licenseHeader.length()); this.licenseHeaderWithYearTokenReplaced = licenseHeader.replace("$YEAR", String.valueOf(YearMonth.now().getYear())); this.yearMatcherPattern = Pattern.compile("[0-9]{4}(" + Pattern.quote(yearSeparator) + "[0-9]{4})?"); } }
/** .gitattributes is path-specific, so you must use {@link LineEnding#createPolicy(File, Supplier)}. */ @Override @Deprecated public Policy createPolicy() { return super.createPolicy(); } },
private static String convertEolToLineEnding(String eol, File file) { switch (eol.toLowerCase(Locale.ROOT)) { case "lf": return LineEnding.UNIX.str(); case "crlf": return LineEnding.WINDOWS.str(); default: System.err.println(".gitattributes file has unspecified eol value: " + eol + " for " + file + ", defaulting to platform native"); return LineEnding.PLATFORM_NATIVE.str(); } }
/** * Applies the given formatter to the given file, checking that * F(F(input)) == F(input). * * If it meets this test, {@link #misbehaved()} will return false. * * If it fails the test, {@link #misbehaved()} will return true, and you can find * out more about the misbehavior based on its {@link Type}. * */ public static PaddedCell check(Formatter formatter, File file) { Objects.requireNonNull(formatter, "formatter"); Objects.requireNonNull(file, "file"); byte[] rawBytes = ThrowingEx.get(() -> Files.readAllBytes(file.toPath())); String raw = new String(rawBytes, formatter.getEncoding()); String original = LineEnding.toUnix(raw); return check(formatter, file, original, MAX_CYCLE); }
/** Returns a {@link Policy} appropriate for files which are contained within the given rootFolder. */ public Policy createPolicy(File projectDir, Supplier<Iterable<File>> toFormat) { Objects.requireNonNull(projectDir, "projectDir"); Objects.requireNonNull(toFormat, "toFormat"); if (this != GIT_ATTRIBUTES) { return createPolicy(); } else { if (gitAttributesPolicyCreator == null) { try { Class<?> clazz = Class.forName("com.diffplug.spotless.extra.GitAttributesLineEndings"); Method method = clazz.getMethod("create", File.class, Supplier.class); gitAttributesPolicyCreator = (proj, target) -> ThrowingEx.get(() -> (Policy) method.invoke(null, proj, target)); } catch (ClassNotFoundException | NoSuchMethodException | SecurityException e) { throw new IllegalStateException("LineEnding.GIT_ATTRIBUTES requires the spotless-lib-extra library, but it is not on the classpath", e); } } // gitAttributesPolicyCreator will always be nonnull at this point return gitAttributesPolicyCreator.apply(projectDir, toFormat); } }
/** Applies the appropriate line endings to the given unix content. */ public String computeLineEndings(String unix, File file) { Objects.requireNonNull(unix, "unix"); Objects.requireNonNull(file, "file"); String ending = lineEndingsPolicy.getEndingFor(file); if (!ending.equals(LineEnding.UNIX.str())) { return unix.replace(LineEnding.UNIX.str(), ending); } else { return unix; } }
/** * Applies formatting to the given file. * * Returns null if the file was already clean, or the * formatted result with unix newlines if it was not. */ public @Nullable String applyToAndReturnResultIfDirty(File file) throws IOException { Objects.requireNonNull(file); byte[] rawBytes = Files.readAllBytes(file.toPath()); String raw = new String(rawBytes, encoding); String rawUnix = LineEnding.toUnix(raw); // enforce the format String formattedUnix = compute(rawUnix, file); // enforce the line endings String formatted = computeLineEndings(formattedUnix, file); // write out the file iff it has changed byte[] formattedBytes = formatted.getBytes(encoding); if (!Arrays.equals(rawBytes, formattedBytes)) { Files.write(file.toPath(), formattedBytes, StandardOpenOption.TRUNCATE_EXISTING); return formattedUnix; } else { return null; } }
public final Formatter newFormatter(List<File> filesToFormat, FormatterConfig config) { Charset formatterEncoding = encoding(config); LineEnding formatterLineEndings = lineEndings(config); LineEnding.Policy formatterLineEndingPolicy = formatterLineEndings.createPolicy(config.getBaseDir(), () -> filesToFormat); FormatterStepConfig stepConfig = stepConfig(formatterEncoding, config); List<FormatterStepFactory> factories = gatherStepFactories(config.getGlobalStepFactories(), stepFactories); List<FormatterStep> formatterSteps = factories.stream() .filter(Objects::nonNull) // all unrecognized steps from XML config appear as nulls in the list .map(factory -> factory.newFormatterStep(stepConfig)) .collect(toList()); return Formatter.builder() .encoding(formatterEncoding) .lineEndingsPolicy(formatterLineEndingPolicy) .exceptionPolicy(new FormatExceptionPolicyStrict()) .steps(formatterSteps) .rootDir(config.getBaseDir().toPath()) .build(); }
/** Returns true iff this file has unix line endings. */ public default boolean isUnix(File file) { Objects.requireNonNull(file); String ending = getEndingFor(file); return ending.equals(UNIX.str()); } }
/** * Returns the result of calling all of the FormatterSteps. * The input must have unix line endings, and the output * is guaranteed to also have unix line endings. */ public String compute(String unix, File file) { Objects.requireNonNull(unix, "unix"); Objects.requireNonNull(file, "file"); for (FormatterStep step : steps) { try { String formatted = step.format(unix, file); if (formatted == null) { // This probably means it was a step that only checks // for errors and doesn't actually have any fixes. // No exception was thrown so we can just continue. } else { // Should already be unix-only, but some steps might misbehave. unix = LineEnding.toUnix(formatted); } } catch (Throwable e) { String relativePath = rootDir.relativize(file.toPath()).toString(); exceptionPolicy.handleError(e, step, relativePath); } } return unix; }
private SpotlessTask createCheckTask(String name, FormatterStep step) { SpotlessTask task = project.getTasks().create("spotless" + SpotlessPlugin.capitalize(name), SpotlessTask.class); task.setCheck(); task.addStep(step); task.setLineEndingsPolicy(LineEnding.UNIX.createPolicy()); task.setTarget(Collections.singletonList(file)); return task; }
private Runtime(List<AttributesRule> infoRules, @Nullable File workTree, Config config, List<AttributesRule> globalRules) { this.infoRules = Objects.requireNonNull(infoRules); this.workTree = workTree; this.defaultEnding = fromEol(config.getEnum(ConfigConstants.CONFIG_CORE_SECTION, null, ConfigConstants.CONFIG_KEY_EOL, EOL.NATIVE)).str(); this.globalRules = Objects.requireNonNull(globalRules); }
/** Reads the given resource from "before", applies the step, and makes sure the result is "after". */ protected void assertOnResources(FormatterFunc step, String unformattedPath, String expectedPath) throws Throwable { String unformatted = LineEnding.toUnix(getTestResource(unformattedPath)); // unix-ified input String formatted = step.apply(unformatted); // no windows newlines Assert.assertEquals(-1, formatted.indexOf('\r')); // unix-ify the test resource output in case git screwed it up String expected = LineEnding.toUnix(getTestResource(expectedPath)); // unix-ified output Assert.assertEquals(expected, formatted); }
private SpotlessTask createApplyTask(String name, FormatterStep step) { SpotlessTask task = project.getTasks().create("spotless" + SpotlessPlugin.capitalize(name) + SpotlessPlugin.APPLY, SpotlessTask.class); task.setApply(); task.addStep(step); task.setLineEndingsPolicy(LineEnding.UNIX.createPolicy()); task.setTarget(Collections.singletonList(file)); return task; }
@Test public void nonExistingFile() throws IOException { boolean exceptionCaught = false; String filePath = "does/not/exist.properties"; boolean isWin = LineEnding.PLATFORM_NATIVE.str().equals(LineEnding.WINDOWS.str()); if (isWin) { filePath = filePath.replace('/', '\\'); } try { FormatterProperties.from(new File(filePath)); } catch (IllegalArgumentException ex) { exceptionCaught = true; assertThat(ex.getMessage()) .as("IllegalArgumentException does not contain path of non-existing file.").contains(filePath); } assertThat(exceptionCaught) .as("No IllegalArgumentException thrown for non-existing file.").isTrue(); }
/** Returns true iff the given file's formatting is up-to-date. */ public boolean isClean(File file) throws IOException { Objects.requireNonNull(file); String raw = new String(Files.readAllBytes(file.toPath()), encoding); String unix = LineEnding.toUnix(raw); // check the newlines (we can find these problems without even running the steps) int totalNewLines = (int) unix.codePoints().filter(val -> val == '\n').count(); int windowsNewLines = raw.length() - unix.length(); if (lineEndingsPolicy.isUnix(file)) { if (windowsNewLines != 0) { return false; } } else { if (windowsNewLines != totalNewLines) { return false; } } // check the other formats String formatted = compute(unix, file); // return true iff the formatted string equals the unix one return formatted.equals(unix); }
/** Sets up a format task according to the values in this extension. */ protected void setupTask(SpotlessTask task) { task.setPaddedCell(paddedCell); task.setEncoding(getEncoding().name()); task.setExceptionPolicy(exceptionPolicy); task.setTarget(target); task.setSteps(steps); task.setLineEndingsPolicy(getLineEndings().createPolicy(getProject().getProjectDir(), () -> task.target)); }
private static String convertEolToLineEnding(String eol, File file) { switch (eol.toLowerCase(Locale.ROOT)) { case "lf": return LineEnding.UNIX.str(); case "crlf": return LineEnding.WINDOWS.str(); default: System.err.println(".gitattributes file has unspecified eol value: " + eol + " for " + file + ", defaulting to platform native"); return LineEnding.PLATFORM_NATIVE.str(); } }
/** Creates a harness for testing steps which don't depend on the file. */ public static StepHarness forStep(FormatterStep step) { // We don't care if an individual FormatterStep is misbehaving on line-endings, because // Formatter fixes that. No reason to care in tests either. It's likely to pop up when // running tests on Windows from time-to-time return new StepHarness(FormatterFunc.Closeable.of( () -> { if (step instanceof FormatterStepImpl.Standard) { ((FormatterStepImpl.Standard<?>) step).cleanupFormatterFunc(); } }, input -> LineEnding.toUnix(step.format(input, new File(""))))); }
private SpotlessTask create(List<File> files) throws IOException { Project project = ProjectBuilder.builder().withProjectDir(rootFolder()).build(); SpotlessTask task = project.getTasks().create("underTest", SpotlessTask.class); task.setLineEndingsPolicy(LineEnding.UNIX.createPolicy()); task.setTarget(files); task.setCheck(); return task; }