Skip to content

Commit

Permalink
Handy tool for making encoding formats that don't have any forbidden …
Browse files Browse the repository at this point in the history
…characters.
  • Loading branch information
nedtwigg committed May 30, 2024
1 parent 9347888 commit 9afa6dd
Show file tree
Hide file tree
Showing 12 changed files with 720 additions and 14 deletions.
101 changes: 101 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/ExceptionPerStep.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright 2024 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.spotless;

import java.util.AbstractList;

import javax.annotation.Nullable;

/**
* Fixed-size list which maintains a list of exceptions, one per step of the formatter.
* Usually this list will be empty or have only a single value, so it is optimized for stack allocation in those cases.
*/
class ExceptionPerStep extends AbstractList<Throwable> {
private final int size;
private @Nullable Throwable exception;
private int exceptionIdx;
private @Nullable Throwable[] multipleExceptions = null;

ExceptionPerStep(Formatter formatter) {
this.size = formatter.getSteps().size();
}

@Override
public @Nullable Throwable set(int index, Throwable exception) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
}
if (this.exception == null) {
this.exceptionIdx = index;
this.exception = exception;
return null;
} else if (this.multipleExceptions != null) {
Throwable previousValue = multipleExceptions[index];
multipleExceptions[index] = exception;
return previousValue;
} else {
if (index == exceptionIdx) {
Throwable previousValue = this.exception;
this.exception = exception;
return previousValue;
} else {
multipleExceptions = new Throwable[size];
multipleExceptions[exceptionIdx] = this.exception;
multipleExceptions[index] = exception;
return null;
}
}
}

@Override
public Throwable get(int index) {
if (multipleExceptions != null) {
return multipleExceptions[index];
} else if (exceptionIdx == index) {
return exception;
} else {
return null;
}
}

private int indexOfFirstException() {
if (multipleExceptions != null) {
for (int i = 0; i < multipleExceptions.length; i++) {
if (multipleExceptions[i] != null) {
return i;
}
}
return -1;
} else if (exception != null) {
return exceptionIdx;
} else {
return -1;
}
}

@Override
public int size() {
return size;
}

/** Rethrows the first exception in the list. */
public void rethrowFirstIfPresent() {
int firstException = indexOfFirstException();
if (firstException != -1) {
throw ThrowingEx.asRuntimeRethrowError(get(firstException));
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2023 DiffPlug
* Copyright 2016-2024 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,8 +16,8 @@
package com.diffplug.spotless;

import java.io.File;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Nullable;
Expand All @@ -36,14 +36,24 @@ final class FilterByContentPatternFormatterStep extends DelegateFormatterStep {
public @Nullable String format(String raw, File file) throws Exception {
Objects.requireNonNull(raw, "raw");
Objects.requireNonNull(file, "file");
Matcher matcher = contentPattern.matcher(raw);
if (matcher.find() == (onMatch == OnMatch.INCLUDE)) {
if (contentPattern.matcher(raw).find() == (onMatch == OnMatch.INCLUDE)) {
return delegateStep.format(raw, file);
} else {
return raw;
}
}

@Override
public List<Lint> lint(String raw, File file) throws Exception {
Objects.requireNonNull(raw, "raw");
Objects.requireNonNull(file, "file");
if (contentPattern.matcher(raw).find() == (onMatch == OnMatch.INCLUDE)) {
return delegateStep.lint(raw, file);
} else {
return List.of();
}
}

@Override
public boolean equals(Object o) {
if (this == o) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2016-2022 DiffPlug
* Copyright 2016-2024 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -16,6 +16,7 @@
package com.diffplug.spotless;

import java.io.File;
import java.util.List;
import java.util.Objects;

import javax.annotation.Nullable;
Expand All @@ -39,6 +40,17 @@ final class FilterByFileFormatterStep extends DelegateFormatterStep {
}
}

@Override
public List<Lint> lint(String content, File file) throws Exception {
Objects.requireNonNull(content, "content");
Objects.requireNonNull(file, "file");
if (filter.accept(file)) {
return delegateStep.lint(content, file);
} else {
return List.of();
}
}

@Override
public boolean equals(Object o) {
if (this == o) {
Expand Down
30 changes: 22 additions & 8 deletions lib/src/main/java/com/diffplug/spotless/Formatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.Objects;

/** Formatter which performs the full formatting. */
Expand Down Expand Up @@ -127,12 +128,28 @@ public String computeLineEndings(String unix, File file) {
* is guaranteed to also have unix line endings.
*/
public String compute(String unix, File file) {
ExceptionPerStep exceptionPerStep = new ExceptionPerStep(this);
String result = compute(unix, file, exceptionPerStep);
exceptionPerStep.rethrowFirstIfPresent();
return result;
}

/**
* Returns the result of calling all of the FormatterSteps, while also
* tracking any exceptions which are thrown.
* <p>
* The input must have unix line endings, and the output
* is guaranteed to also have unix line endings.
* <p>
*/
String compute(String unix, File file, ExceptionPerStep exceptionPerStep) {
Objects.requireNonNull(unix, "unix");
Objects.requireNonNull(file, "file");

for (FormatterStep step : steps) {
ListIterator<FormatterStep> iter = steps.listIterator();
while (iter.hasNext()) {
try {
String formatted = step.format(unix, file);
String formatted = iter.next().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.
Expand All @@ -142,12 +159,9 @@ public String compute(String unix, File file) {
unix = LineEnding.toUnix(formatted);
}
} catch (Throwable e) {
// TODO: this is bad, but it won't matter when add support for linting
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
throw new RuntimeException(e);
}
// store the exception which was thrown, and stop execution so we don't alter line numbers
exceptionPerStep.set(iter.previousIndex(), e);
return unix;
}
}
return unix;
Expand Down
26 changes: 26 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/FormatterFunc.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.diffplug.spotless;

import java.io.File;
import java.util.List;
import java.util.Objects;

/**
Expand All @@ -32,6 +33,14 @@ default String apply(String unix, File file) throws Exception {
return apply(unix);
}

/**
* Calculates a list of lints against the given content.
* By default, that's just an throwables thrown by the lint.
*/
default List<Lint> lint(String content, File file) throws Exception {
return List.of();
}

/**
* {@code Function<String, String>} and {@code BiFunction<String, File, String>} whose implementation
* requires a resource which should be released when the function is no longer needed.
Expand Down Expand Up @@ -74,6 +83,14 @@ public String apply(String unix) throws Exception {
@FunctionalInterface
interface ResourceFunc<T extends AutoCloseable> {
String apply(T resource, String unix) throws Exception;

/**
* Calculates a list of lints against the given content.
* By default, that's just an throwables thrown by the lint.
*/
default List<Lint> lint(T resource, String unix) throws Exception {
return List.of();
}
}

/** Creates a {@link FormatterFunc.Closeable} which uses the given resource to execute the format function. */
Expand Down Expand Up @@ -101,6 +118,10 @@ public String apply(String unix) throws Exception {
@FunctionalInterface
interface ResourceFuncNeedsFile<T extends AutoCloseable> {
String apply(T resource, String unix, File file) throws Exception;

default List<Lint> lint(T resource, String content, File file) throws Exception {
return List.of();
}
}

/** Creates a {@link FormatterFunc.Closeable} which uses the given resource to execute the file-dependent format function. */
Expand All @@ -123,6 +144,11 @@ public String apply(String unix, File file) throws Exception {
public String apply(String unix) throws Exception {
return apply(unix, Formatter.NO_FILE_SENTINEL);
}

@Override
public List<Lint> lint(String content, File file) throws Exception {
return function.lint(resource, content, file);
}
};
}
}
Expand Down
17 changes: 17 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/FormatterStep.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.io.File;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;

import javax.annotation.Nullable;
Expand Down Expand Up @@ -46,6 +47,22 @@ public interface FormatterStep extends Serializable, AutoCloseable {
@Nullable
String format(String rawUnix, File file) throws Exception;

/**
* Returns a list of lints against the given file content
*
* @param content
* the content to check
* @param file
* the file which {@code content} was obtained from; never null. Pass an empty file using
* {@code new File("")} if and only if no file is actually associated with {@code content}
* @return a list of lints
* @throws Exception if the formatter step experiences a problem
*/
@Nullable
default List<Lint> lint(String content, File file) throws Exception {
return List.of();
}

/**
* Returns a new {@code FormatterStep} which, observing the value of {@code formatIfMatches},
* will only apply, or not, its changes to files which pass the given filter.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.io.File;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

Expand Down Expand Up @@ -48,6 +49,14 @@ public String format(String rawUnix, File file) throws Exception {
return formatter.apply(rawUnix, file);
}

@Override
public List<Lint> lint(String content, File file) throws Exception {
if (formatter == null) {
formatter = stateToFormatter(state());
}
return formatter.lint(content, file);
}

@Override
public boolean equals(Object o) {
if (o == null) {
Expand Down
Loading

0 comments on commit 9afa6dd

Please # to comment.