From f23ffbac2ff3db526ff4122427fa4285b901f3fe Mon Sep 17 00:00:00 2001 From: David Walluck Date: Wed, 9 Sep 2020 12:09:16 -0400 Subject: [PATCH] GH-30: Handle files that are empty, have an unknown newline, or can't be parsed --- .../code/impsort/EmptyFileException.java | 50 +++++++++++ .../java/net/revelc/code/impsort/ImpSort.java | 30 +++++-- .../impsort/UnknownLineEndingException.java | 50 +++++++++++ .../maven/plugin/AbstractImpSortMojo.java | 6 ++ .../code/impsort/LineEndingEdgeCasesTest.java | 82 +++++++++++++++++++ 5 files changed, 213 insertions(+), 5 deletions(-) create mode 100644 src/main/java/net/revelc/code/impsort/EmptyFileException.java create mode 100644 src/main/java/net/revelc/code/impsort/UnknownLineEndingException.java create mode 100644 src/test/java/net/revelc/code/impsort/LineEndingEdgeCasesTest.java diff --git a/src/main/java/net/revelc/code/impsort/EmptyFileException.java b/src/main/java/net/revelc/code/impsort/EmptyFileException.java new file mode 100644 index 0000000..52c2bd1 --- /dev/null +++ b/src/main/java/net/revelc/code/impsort/EmptyFileException.java @@ -0,0 +1,50 @@ +/* + * 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 net.revelc.code.impsort; + +import java.io.IOException; +import java.nio.file.Path; + +/** + * Signals that the file denoted by this path is empty. + * + *

+ * This exception will be thrown by the {@link ImpSort#parseFile} when it encounters an empty file. + */ +public class EmptyFileException extends IOException { + private static final long serialVersionUID = 1L; + + private final Path path; + + /** + * Constructs a {@code EmptyFileException} with the specified path. + * + * @param path the path + */ + public EmptyFileException(final Path path) { + super("Empty file " + path); + this.path = path; + } + + /** + * Returns the path. + * + * @return the path + */ + public Path getPath() { + return path; + } + +} diff --git a/src/main/java/net/revelc/code/impsort/ImpSort.java b/src/main/java/net/revelc/code/impsort/ImpSort.java index f1c2e6a..590ae0f 100644 --- a/src/main/java/net/revelc/code/impsort/ImpSort.java +++ b/src/main/java/net/revelc/code/impsort/ImpSort.java @@ -45,7 +45,6 @@ import com.github.javaparser.ast.Node; import com.github.javaparser.ast.NodeList; import com.github.javaparser.ast.PackageDeclaration; -import com.github.javaparser.ast.body.TypeDeclaration; import com.github.javaparser.ast.comments.Comment; import com.github.javaparser.ast.comments.JavadocComment; import com.github.javaparser.javadoc.Javadoc; @@ -73,9 +72,27 @@ public ImpSort(final Charset sourceEncoding, final Grouper grouper, final boolea this.lineEnding = lineEnding; } - public Result parseFile(final Path path) throws IOException { - String file = new String(Files.readAllBytes(path), sourceEncoding); + /** + * Parses the file denoted by this path and returns the result. + * + * @param path the path + * @return the result + * @throws IOException if the Java file denoted by this path can't be parsed + * @throws EmptyFileException if the Java file denoted by this path is empty (zero bytes) + * @throws UnknownLineEndingException if the Java file denoted by this path has an unknown line + * ending + */ + public Result parseFile(final Path path) + throws IOException, EmptyFileException, UnknownLineEndingException { + byte[] buf = Files.readAllBytes(path); + if (buf.length == 0) { + throw new EmptyFileException(path); + } + String file = new String(buf, sourceEncoding); LineEnding fileLineEnding = LineEnding.determineLineEnding(file); + if (fileLineEnding == LineEnding.UNKNOWN) { + throw new UnknownLineEndingException(path); + } LineEnding impLineEnding; if (lineEnding == LineEnding.KEEP) { impLineEnding = fileLineEnding; @@ -84,8 +101,11 @@ public Result parseFile(final Path path) throws IOException { } List fileLines = Arrays.asList(file.split(fileLineEnding.getChars())); ParseResult parseResult = new JavaParser().parse(file); - CompilationUnit unit = - parseResult.getResult().orElseThrow(() -> new IOException("Unable to parse " + path)); + Optional unitOptional = parseResult.getResult(); + if (!parseResult.isSuccessful() || !unitOptional.isPresent()) { + throw new IOException("Unable to parse " + path); + } + CompilationUnit unit = unitOptional.get(); Position packagePosition = unit.getPackageDeclaration().map(p -> p.getEnd().get()).orElse(unit.getBegin().get()); NodeList importDeclarations = unit.getImports(); diff --git a/src/main/java/net/revelc/code/impsort/UnknownLineEndingException.java b/src/main/java/net/revelc/code/impsort/UnknownLineEndingException.java new file mode 100644 index 0000000..92d913b --- /dev/null +++ b/src/main/java/net/revelc/code/impsort/UnknownLineEndingException.java @@ -0,0 +1,50 @@ +/* + * 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 net.revelc.code.impsort; + +import java.io.IOException; +import java.nio.file.Path; + +/** + * Signals that the file denoted by this path has an unknown line ending. + * + *

+ * This exception will be thrown by the {@link ImpSort#parseFile} when it encounters a file with an + * unknown line ending. + */ +public class UnknownLineEndingException extends IOException { + private static final long serialVersionUID = 1L; + + private final Path path; + + /** + * Constructs a {@code UnknownLineEndingException} with the specified path. + * + * @param path the path + */ + public UnknownLineEndingException(final Path path) { + super("Unknown line ending for file " + path); + this.path = path; + } + + /** + * Returns the path. + * + * @return the path + */ + public Path getPath() { + return path; + } +} diff --git a/src/main/java/net/revelc/code/impsort/maven/plugin/AbstractImpSortMojo.java b/src/main/java/net/revelc/code/impsort/maven/plugin/AbstractImpSortMojo.java index a3a42b7..7c6c73e 100644 --- a/src/main/java/net/revelc/code/impsort/maven/plugin/AbstractImpSortMojo.java +++ b/src/main/java/net/revelc/code/impsort/maven/plugin/AbstractImpSortMojo.java @@ -34,10 +34,12 @@ import org.apache.maven.project.MavenProject; import org.codehaus.plexus.util.DirectoryScanner; +import net.revelc.code.impsort.EmptyFileException; import net.revelc.code.impsort.Grouper; import net.revelc.code.impsort.ImpSort; import net.revelc.code.impsort.LineEnding; import net.revelc.code.impsort.Result; +import net.revelc.code.impsort.UnknownLineEndingException; abstract class AbstractImpSortMojo extends AbstractMojo { @@ -233,6 +235,10 @@ public final void execute() throws MojoExecutionException, MojoFailureException numProcessed.getAndIncrement(); } processResult(path, result); + } catch (EmptyFileException e) { + getLog().warn("Skipping empty file " + e.getPath()); + } catch (UnknownLineEndingException e) { + getLog().warn("Skipping file with unknown line ending " + e.getPath()); } catch (IOException e) { fail("Error reading file " + path, e); } diff --git a/src/test/java/net/revelc/code/impsort/LineEndingEdgeCasesTest.java b/src/test/java/net/revelc/code/impsort/LineEndingEdgeCasesTest.java new file mode 100644 index 0000000..420ed72 --- /dev/null +++ b/src/test/java/net/revelc/code/impsort/LineEndingEdgeCasesTest.java @@ -0,0 +1,82 @@ +/* + * 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 net.revelc.code.impsort; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +/** + * Test class for special file cases. + */ +public class LineEndingEdgeCasesTest { + + private static Grouper eclipseDefaults = + new Grouper("java.,javax.,org.,com.", "", false, false, true); + + @Rule + public TemporaryFolder folder = new TemporaryFolder(); + + /** + * Test successfully parsing empty file (zero bytes). + */ + @Test + public void testEmptyFile() throws IOException { + Path p = folder.newFile("EmptyFile.java").toPath(); + Files.write(p, new byte[0]); + EmptyFileException e = assertThrows(EmptyFileException.class, + () -> new ImpSort(StandardCharsets.UTF_8, eclipseDefaults, true, true, LineEnding.AUTO) + .parseFile(p)); + assertEquals("Empty file " + p, e.getMessage()); + } + + /** + * Test successfully parsing file without any line ending. + */ + @Test + public void testFileWithoutLineEnding() throws IOException { + String s = + "import java.lang.System;public class FileWithoutNewline{public static void main(String[] args){System.out.println(\"Hello, world!\");}}"; + Path p = folder.newFile("FileWithoutLineEnding.java").toPath(); + Files.write(p, s.getBytes(StandardCharsets.UTF_8)); + UnknownLineEndingException e = assertThrows(UnknownLineEndingException.class, + () -> new ImpSort(StandardCharsets.UTF_8, eclipseDefaults, true, true, LineEnding.AUTO) + .parseFile(p)); + assertEquals("Unknown line ending for file " + p, e.getMessage()); + } + + /** + * Test successfully parsing file that can't be parsed. + */ + @Test + public void testInvalidFile() throws IOException { + String s = "public class InvalidFile {\n" + " public static void main(String[] args) {\n" + + " System.out.println(\"Hello, world!\")\n" + " }\n" + "}\n"; + Path p = folder.newFile("InvalidFile.java").toPath(); + Files.write(p, s.getBytes(StandardCharsets.UTF_8)); + IOException e = assertThrows(IOException.class, + () -> new ImpSort(StandardCharsets.UTF_8, eclipseDefaults, true, true, LineEnding.AUTO) + .parseFile(p)); + assertEquals("Unable to parse " + p, e.getMessage()); + } + +}