Skip to content

Commit

Permalink
Merge pull request #72 from nedtwigg/feature/strictExceptions
Browse files Browse the repository at this point in the history
Any exception should break the build
  • Loading branch information
nedtwigg authored Jan 20, 2017
2 parents c8bbac4 + 7e88858 commit 23de01a
Show file tree
Hide file tree
Showing 29 changed files with 481 additions and 158 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

import com.diffplug.spotless.FormatterStep;
import com.diffplug.spotless.ResourceHarness;
import com.diffplug.spotless.StepEqualityTester;
import com.diffplug.spotless.SerializableEqualityTester;
import com.diffplug.spotless.StepHarness;
import com.diffplug.spotless.TestProvisioner;

Expand Down Expand Up @@ -54,17 +54,15 @@ public void longLiteralProblem() throws Throwable {
public void equality() throws IOException {
File xmlFile = createTestFile("java/eclipse/format/formatter.xml");
File propFile = createTestFile("java/eclipse/format/formatter.properties");
new StepEqualityTester() {
new SerializableEqualityTester() {
File settingsFile;

@Override
protected void setupTest(API api) {
settingsFile = xmlFile;
api.assertThisEqualToThis();
api.areDifferentThan();

settingsFile = propFile;
api.assertThisEqualToThis();
api.areDifferentThan();
}

Expand Down
39 changes: 39 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/FormatExceptionPolicy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* 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.spotless;

import java.io.Serializable;

/** A policy for handling exceptions in the format. */
public interface FormatExceptionPolicy extends Serializable, NoLambda {
/** Called for every error in the formatter. */
void handleError(Throwable e, FormatterStep step, String relativePath);

/**
* Returns a byte array representation of everything inside this `FormatExceptionPolicy`.
*
* The main purpose of this method is to ensure one can't instantiate this class with lambda
* expressions, which are notoriously difficult to serialize and deserialize properly.
*/
public byte[] toBytes();

/**
* A policy which rethrows subclasses of `Error` and logs other kinds of Exception.
*/
public static FormatExceptionPolicy failOnlyOnError() {
return new FormatExceptionPolicyLegacy();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* 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.spotless;

import java.util.logging.Level;
import java.util.logging.Logger;

class FormatExceptionPolicyLegacy extends NoLambda.EqualityBasedOnSerialization implements FormatExceptionPolicy {
private static final long serialVersionUID = 1L;

private static final Logger logger = Logger.getLogger(Formatter.class.getName());

@Override
public void handleError(Throwable e, FormatterStep step, String relativePath) {
if (e instanceof Error) {
error(e, step, relativePath);
throw ((Error) e);
} else {
warning(e, step, relativePath);
}
}

static void error(Throwable e, FormatterStep step, String relativePath) {
logger.log(Level.SEVERE, "Step '" + step.getName() + "' found problem in '" + relativePath + "':\n" + e.getMessage(), e);
}

static void warning(Throwable e, FormatterStep step, String relativePath) {
logger.log(Level.WARNING, "Unable to apply step '" + step.getName() + "' to '" + relativePath + "'", e);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* 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.spotless;

import java.util.Set;
import java.util.TreeSet;

/**
* A policy for handling exceptions in the format. Any exceptions will
* halt the build except for a specifically excluded path or step.
*/
public class FormatExceptionPolicyStrict extends NoLambda.EqualityBasedOnSerialization implements FormatExceptionPolicy {
private static final long serialVersionUID = 1L;

private final Set<String> excludeSteps = new TreeSet<>();
private final Set<String> excludePaths = new TreeSet<>();

/** Adds a step name to exclude. */
public void excludeStep(String stepName) {
excludeSteps.add(stepName);
}

/** Adds a realtive pathx to exclude. */
public void excludePath(String relativePath) {
excludePaths.add(relativePath);
}

@Override
public void handleError(Throwable e, FormatterStep step, String relativePath) {
if (excludeSteps.contains(step.getName())) {
FormatExceptionPolicyLegacy.warning(e, step, relativePath);
} else {
if (excludePaths.contains(relativePath)) {
FormatExceptionPolicyLegacy.warning(e, step, relativePath);
} else {
FormatExceptionPolicyLegacy.error(e, step, relativePath);
throw ThrowingEx.asRuntimeRethrowError(e);
}
}
}
}
71 changes: 56 additions & 15 deletions lib/src/main/java/com/diffplug/spotless/Formatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,63 @@

import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.annotation.Nullable;

/** Formatter which performs the full formatting. */
public final class Formatter {
private final LineEnding.Policy lineEndingsPolicy;
private final Charset encoding;
private final Path rootDir;
private final List<FormatterStep> steps;
public final class Formatter implements Serializable {
private static final long serialVersionUID = 1L;

private static final Logger logger = Logger.getLogger(Formatter.class.getName());
private LineEnding.Policy lineEndingsPolicy;
private Charset encoding;
private Path rootDir;
private List<FormatterStep> steps;
private FormatExceptionPolicy exceptionPolicy;

private Formatter(LineEnding.Policy lineEndingsPolicy, Charset encoding, Path rootDirectory, List<FormatterStep> steps) {
private Formatter(LineEnding.Policy lineEndingsPolicy, Charset encoding, Path rootDirectory, List<FormatterStep> steps, FormatExceptionPolicy exceptionPolicy) {
this.lineEndingsPolicy = Objects.requireNonNull(lineEndingsPolicy, "lineEndingsPolicy");
this.encoding = Objects.requireNonNull(encoding, "encoding");
this.rootDir = Objects.requireNonNull(rootDirectory, "rootDir");
this.steps = new ArrayList<>(Objects.requireNonNull(steps, "steps"));
this.exceptionPolicy = Objects.requireNonNull(exceptionPolicy, "exceptionPolicy");
}

// override serialize output
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeObject(lineEndingsPolicy);
out.writeObject(encoding.name());
out.writeObject(rootDir.toString());
out.writeObject(steps);
out.writeObject(exceptionPolicy);
}

// override serialize input
@SuppressWarnings("unchecked")
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
lineEndingsPolicy = (LineEnding.Policy) in.readObject();
encoding = Charset.forName((String) in.readObject());
rootDir = Paths.get((String) in.readObject());
steps = (List<FormatterStep>) in.readObject();
exceptionPolicy = (FormatExceptionPolicy) in.readObject();
}

// override serialize input
@SuppressWarnings("unused")
private void readObjectNoData() throws ObjectStreamException {
throw new UnsupportedOperationException();
}

public LineEnding.Policy getLineEndingsPolicy() {
Expand All @@ -62,6 +92,10 @@ public List<FormatterStep> getSteps() {
return steps;
}

public FormatExceptionPolicy getExceptionPolicy() {
return exceptionPolicy;
}

public static Formatter.Builder builder() {
return new Formatter.Builder();
}
Expand All @@ -72,6 +106,7 @@ public static class Builder {
private Charset encoding;
private Path rootDir;
private List<FormatterStep> steps;
private FormatExceptionPolicy exceptionPolicy;

private Builder() {}

Expand All @@ -95,8 +130,14 @@ public Builder steps(List<FormatterStep> steps) {
return this;
}

public Builder exceptionPolicy(FormatExceptionPolicy exceptionPolicy) {
this.exceptionPolicy = exceptionPolicy;
return this;
}

public Formatter build() {
return new Formatter(lineEndingsPolicy, encoding, rootDir, steps);
return new Formatter(lineEndingsPolicy, encoding, rootDir, steps,
exceptionPolicy == null ? FormatExceptionPolicy.failOnlyOnError() : exceptionPolicy);
}
}

Expand Down Expand Up @@ -183,11 +224,9 @@ public String compute(String unix, File file) throws Error {
// Should already be unix-only, but some steps might misbehave.
unix = LineEnding.toUnix(formatted);
}
} catch (Error e) {
logger.severe("Step '" + step.getName() + "' found problem in '" + rootDir.relativize(file.toPath()) + "':\n" + e.getMessage());
throw e;
} catch (Throwable e) {
logger.log(Level.WARNING, "Unable to apply step '" + step.getName() + "' to '" + rootDir.relativize(file.toPath()), e);
String relativePath = rootDir.relativize(file.toPath()).toString();
exceptionPolicy.handleError(e, step, relativePath);
}
}
return unix;
Expand All @@ -201,6 +240,7 @@ public int hashCode() {
result = prime * result + lineEndingsPolicy.hashCode();
result = prime * result + rootDir.hashCode();
result = prime * result + steps.hashCode();
result = prime * result + exceptionPolicy.hashCode();
return result;
}

Expand All @@ -219,6 +259,7 @@ public boolean equals(Object obj) {
return encoding.equals(other.encoding) &&
lineEndingsPolicy.equals(other.lineEndingsPolicy) &&
rootDir.equals(other.rootDir) &&
steps.equals(other.steps);
steps.equals(other.steps) &&
exceptionPolicy.equals(other.exceptionPolicy);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
* of lazily-computed state. The state's serialized form is used to implement
* equals() and hashCode(), so you don't have to.
*/
public abstract class LazyForwardingEquality<T extends Serializable> implements Serializable {
public abstract class LazyForwardingEquality<T extends Serializable> implements Serializable, NoLambda {
private static final long serialVersionUID = 1L;

/** Lazily initialized - null indicates that the state has not yet been set. */
Expand Down Expand Up @@ -80,21 +80,26 @@ private void readObjectNoData() throws ObjectStreamException {
throw new UnsupportedOperationException();
}

@Override
public byte[] toBytes() {
return toBytes(state());
}

@Override
public final boolean equals(Object other) {
if (other == null) {
return false;
} else if (getClass().equals(other.getClass())) {
Serializable otherState = ((LazyForwardingEquality<?>) other).state();
return Arrays.equals(toBytes(otherState), toBytes(state()));
LazyForwardingEquality<?> otherCast = (LazyForwardingEquality<?>) other;
return Arrays.equals(otherCast.toBytes(), toBytes());
} else {
return false;
}
}

@Override
public final int hashCode() {
return Arrays.hashCode(toBytes(state()));
return Arrays.hashCode(toBytes());
}

static byte[] toBytes(Serializable obj) {
Expand Down
23 changes: 19 additions & 4 deletions lib/src/main/java/com/diffplug/spotless/LineEnding.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,25 @@ public Policy createPolicy() {
}
}

private static final Policy WINDOWS_POLICY = file -> WINDOWS.str();
private static final Policy UNIX_POLICY = file -> UNIX.str();
static class ConstantLineEndingPolicy extends NoLambda.EqualityBasedOnSerialization implements Policy {
private static final long serialVersionUID = 1L;

final String lineEnding;

ConstantLineEndingPolicy(String lineEnding) {
this.lineEnding = lineEnding;
}

@Override
public String getEndingFor(File file) {
return lineEnding;
}
}

private static final Policy WINDOWS_POLICY = new ConstantLineEndingPolicy(WINDOWS.str());
private static final Policy UNIX_POLICY = new ConstantLineEndingPolicy(UNIX.str());
private static final String _platformNative = System.getProperty("line.separator");
private static final Policy _platformNativePolicy = file -> _platformNative;
private static final Policy _platformNativePolicy = new ConstantLineEndingPolicy(_platformNative);

/** Returns the standard line ending for this policy. */
public String str() {
Expand All @@ -93,7 +108,7 @@ public String str() {
// @formatter:on

/** A policy for line endings which can vary based on the specific file being requested. */
public interface Policy extends Serializable {
public interface Policy extends Serializable, NoLambda {
/** Returns the line ending appropriate for the given file. */
String getEndingFor(File file);

Expand Down
Loading

0 comments on commit 23de01a

Please sign in to comment.