All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openrewrite.java.style.DeclarationOrderStyle Maven / Gradle / Ivy

/*
 * Copyright 2020 the original author or authors.
 * 

* 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 *

* https://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 org.openrewrite.java.style; import com.fasterxml.jackson.annotation.JsonProperty; import org.openrewrite.Validated; import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaStyle; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.Statement; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import static java.util.Collections.*; import static java.util.stream.Collectors.toList; import static org.openrewrite.Validated.test; public class DeclarationOrderStyle implements JavaStyle { private Map layout; @JsonProperty("layout") public void setLayout(Map layout) { this.layout = layout; } public Layout getLayout() { Layout.Builder builder = Layout.builder(); //noinspection unchecked for (String block : (List) layout.get("blocks")) { block = block.trim(); if (block.equals("")) { builder = builder.blankLine(); } else if (block.endsWith("fields")) { if (block.equals("all other fields")) { builder = builder.allOtherFields(); continue; } String[] modifiers = block.substring(0, block.length() - "fields".length()).split("\\s+"); builder = builder.fields(modifiers); } else if (block.endsWith("classes")) { if (block.equals("all other classes")) { builder = builder.allOtherClasses(); continue; } String[] modifiers = block.substring(0, block.length() - "classes".length()).split("\\s+"); builder = builder.classes(modifiers); } else if (block.endsWith("methods")) { if (block.equals("all other methods")) { builder = builder.allOtherMethods(); continue; } String[] modifiers = block.substring(0, block.length() - "methods".length()).split("\\s+"); builder = builder.methods(modifiers); } else if (block.endsWith("constructors")) { if (block.equals("all other constructors")) { builder = builder.allOtherConstructors(); continue; } String[] modifiers = block.substring(0, block.length() - "constructors".length()).split("\\s+"); builder = builder.constructors(modifiers); } else if (block.equals("all getters")) { builder = builder.allGetters(); } else if (block.equals("all setters")) { builder = builder.allSetters(); } else if (block.equals("all withs")) { builder = builder.allWiths(); } else if (block.equals("all accessors")) { builder = builder.allAccessors(); } else if (block.equals("equals")) { builder = builder.equals(); } else if (block.equals("hashCode")) { builder = builder.hashCoding(); } else if (block.equals("toString")) { builder = builder.toStringing(); } } return builder.build(); } public static class Layout { public static Layout DEFAULT = Layout.builder() .fields("public", "static").blankLine() .fields("private", "static").blankLine() .fields("final").blankLine() .allOtherFields().blankLine() .allOtherConstructors().blankLine() .allOtherMethods().blankLine() .allAccessors().blankLine() .equals().blankLine() .hashCoding().blankLine() .toStringing().blankLine() .allOtherClasses() .build(); private final List> blocks; /** * Placed in precedence order, not the order in which the blocks appear in * the final result. */ private final List> blocksByPrecedence; Layout(List> blocks) { this.blocks = blocks; this.blocksByPrecedence = new ArrayList<>(); blocks.stream().filter(b -> !(b instanceof Block.BlankLines)) .forEach(blocksByPrecedence::add); blocksByPrecedence.sort(new Comparator>() { @Override public int compare(Block b1, Block b2) { if (b1 instanceof Block.Methods) { if (b2 instanceof Block.Methods) { return methodTypeRank(b1) - methodTypeRank(b2); } return 1; } return ModifierComparator.BY_STRING.compare(b1.requiredModifiers, b2.requiredModifiers); } private int methodTypeRank(Block b) { if (b instanceof Block.Getters) { return 1; } if (b instanceof Block.Setters) { return 2; } if (b instanceof Block.Withs) { return 3; } if (b instanceof Block.Accessors) { return 4; } if (b instanceof Block.ToString) { return 5; } if (b instanceof Block.HashCode) { return 6; } if (b instanceof Block.Equals) { return 7; } return 8; } }); } public void reset() { for (Block block : blocks) { block.reset(); } } public void accept(J declaration) { for (Block block : blocksByPrecedence) { if (block.accept(declaration)) { return; } } } public List orderedDeclarations() { List orderedDeclarations = new ArrayList<>(); AtomicInteger blankLines = new AtomicInteger(0); for (Block block : blocks) { System.out.println(block); if (block instanceof Block.BlankLines) { blankLines.addAndGet(((Block.BlankLines) block).count); } else { AtomicBoolean first = new AtomicBoolean(true); orderedDeclarations.addAll(block.orderedDeclarations().stream() .peek(declaration -> System.out.println(" " + declaration.toString())) .map(declaration -> { if (first.getAndSet(false)) { return declaration.withFormatting(declaration.getFormatting() .withMinimumBlankLines(blankLines.getAndSet(0))); } if (declaration instanceof J.MethodDecl) { return declaration.withFormatting(declaration.getFormatting() .withMinimumBlankLines(1)); } return declaration; }) .collect(toList())); blankLines.set(0); } } return orderedDeclarations; } public Validated validate() { return test("accessors", "'all accessors' can only be used when 'all getters', 'all setters', or 'all with' are not", blocks, blocks2 -> blocks2.stream() .filter(b -> b instanceof Block.Getters || b instanceof Block.Setters || b instanceof Block.Withs) .findAny() .map(conflicting -> blocks.stream().noneMatch(b -> b instanceof Block.Accessors)) .orElse(true) ).and( test("accessors", "only one 'all accessors' permitted", blocks, blocks2 -> blocks2.stream().filter(b -> b instanceof Block.Accessors).count() == 1) ).and( test("getters", "only one 'all getters' permitted", blocks, blocks2 -> blocks2.stream().filter(b -> b instanceof Block.Getters).count() == 1) ).and( test("setters", "only one 'all setters' permitted", blocks, blocks2 -> blocks2.stream().filter(b -> b instanceof Block.Setters).count() == 1) ).and( test("withs", "only one 'all withs' permitted", blocks, blocks2 -> blocks2.stream().filter(b -> b instanceof Block.Withs).count() == 1) ); } private static abstract class Block { final List requiredModifiers; @Nullable final Comparator comparator; final List matched = new ArrayList<>(); protected Block(List requiredModifiers, @Nullable Comparator comparator) { this.requiredModifiers = requiredModifiers; this.comparator = comparator; } abstract boolean accept(J declaration); final void reset() { matched.clear(); } List orderedDeclarations() { if (comparator != null) { matched.sort(comparator); } return matched; } static class BlankLines extends Block { private int count = 1; BlankLines() { super(emptyList(), null); } @Override public boolean accept(J declaration) { return false; } @Override public String toString() { return ""; } } static class Fields extends Block { public Fields(List requiredModifiers) { super(requiredModifiers, (f1, f2) -> { int modComp = ModifierComparator.BY_AST.compare(f1.getModifiers(), f2.getModifiers()); if (modComp != 0) { return modComp; } return f1.getVars().get(0).getSimpleName().compareTo(f2.getVars().get(0).getSimpleName()); }); } @Override public boolean accept(J declaration) { if (declaration instanceof J.VariableDecls && requiredModifiers.stream().allMatch(((J.VariableDecls) declaration)::hasModifier)) { matched.add((J.VariableDecls) declaration); return true; } return false; } @Override public String toString() { return (requiredModifiers.isEmpty() ? "all other" : String.join(" ", requiredModifiers)) + " fields"; } } static class Classes extends Block { public Classes(List requiredModifiers) { super(requiredModifiers, (c1, c2) -> { int modComp = ModifierComparator.BY_AST.compare(c1.getModifiers(), c2.getModifiers()); if (modComp != 0) { return modComp; } return c1.getSimpleName().compareTo(c2.getSimpleName()); }); } @Override public boolean accept(J declaration) { if (declaration instanceof J.ClassDecl && requiredModifiers.stream().allMatch(((J.ClassDecl) declaration)::hasModifier)) { matched.add((J.ClassDecl) declaration); return true; } return false; } @Override public String toString() { return (requiredModifiers.isEmpty() ? "all other" : String.join(" ", requiredModifiers)) + " classes"; } } static class Methods extends Block { public Methods(List requiredModifiers) { super(requiredModifiers, (m1, m2) -> { int modComp = ModifierComparator.BY_AST.compare(m1.getModifiers(), m2.getModifiers()); if (modComp != 0) { return modComp; } return m1.getSimpleName().compareTo(m2.getSimpleName()); }); } @Override public boolean accept(J declaration) { if (test(declaration)) { matched.add((J.MethodDecl) declaration); return true; } return false; } public boolean test(J declaration) { return declaration instanceof J.MethodDecl && !((J.MethodDecl) declaration).isConstructor() && requiredModifiers.stream().allMatch(((J.MethodDecl) declaration)::hasModifier); } @Override public String toString() { return (requiredModifiers.isEmpty() ? "all other" : String.join(" ", requiredModifiers)) + " methods"; } } static class Constructors extends Block { public Constructors(List requiredModifiers) { super(requiredModifiers, (m1, m2) -> { int modComp = ModifierComparator.BY_AST.compare(m1.getModifiers(), m2.getModifiers()); if (modComp != 0) { return modComp; } return m1.getSimpleName().compareTo(m2.getSimpleName()); }); } @Override public boolean accept(J declaration) { if (declaration instanceof J.MethodDecl && requiredModifiers.stream().allMatch(((J.MethodDecl) declaration)::hasModifier) && ((J.MethodDecl) declaration).isConstructor()) { matched.add((J.MethodDecl) declaration); return true; } return false; } @Override public String toString() { return (requiredModifiers.isEmpty() ? "all other" : String.join(" ", requiredModifiers)) + " constructors"; } } static class Getters extends Methods { public Getters() { super(singletonList("public")); } @Override public boolean test(J declaration) { return super.test(declaration) && ((J.MethodDecl) declaration) .getSimpleName().startsWith("get"); } @Override public String toString() { return "all getters"; } } static class Setters extends Methods { public Setters() { super(singletonList("public")); } @Override public boolean test(J declaration) { return super.test(declaration) && ((J.MethodDecl) declaration) .getSimpleName().startsWith("set"); } @Override public String toString() { return "all setters"; } } static class Withs extends Methods { public Withs() { super(singletonList("public")); } @Override public boolean test(J declaration) { return super.test(declaration) && ((J.MethodDecl) declaration) .getSimpleName().startsWith("with"); } @Override public String toString() { return "all withs"; } } static class Accessors extends Methods { private final Getters getters; private final Setters setters; private final Withs withs; public Accessors() { super(emptyList()); this.getters = new Getters(); this.setters = new Setters(); this.withs = new Withs(); } @Override public boolean test(J declaration) { return getters.test(declaration) || setters.test(declaration) || withs.test(declaration); } @Override public String toString() { return "all accessors"; } } static class Equals extends Methods { public Equals() { super(singletonList("public")); } @Override public boolean test(J declaration) { if (!super.test(declaration)) { return false; } J.MethodDecl method = (J.MethodDecl) declaration; if (!method.getSimpleName().equals("equals")) { return false; } List params = method.getParams().getParams(); return params.size() == 1; } @Override List orderedDeclarations() { return super.orderedDeclarations(); } @Override public String toString() { return "equals"; } } static class ToString extends Methods { public ToString() { super(singletonList("public")); } @Override public boolean test(J declaration) { if (!super.test(declaration)) { return false; } J.MethodDecl method = (J.MethodDecl) declaration; return method.getSimpleName().equals("toString") && method.getParams().isEmpty(); } @Override public String toString() { return "toString"; } } static class HashCode extends Methods { public HashCode() { super(singletonList("public")); } @Override public boolean test(J declaration) { if (!super.test(declaration)) { return false; } J.MethodDecl method = (J.MethodDecl) declaration; return method.getSimpleName().equals("hashCode") && method.getParams().isEmpty(); } @Override public String toString() { return "hashCode"; } } } public static Builder builder() { return new Builder(); } public static class Builder { private final List> blocks = new ArrayList<>(); public Builder fields(String... modifiers) { blocks.add(new Block.Fields(Arrays.asList(modifiers))); return this; } public Builder allOtherFields() { blocks.add(new Block.Fields(emptyList())); return this; } public Builder classes(String... modifiers) { blocks.add(new Block.Classes(Arrays.asList(modifiers))); return this; } public Builder allOtherClasses() { blocks.add(new Block.Classes(emptyList())); return this; } public Builder methods(String... modifiers) { blocks.add(new Block.Methods(Arrays.asList(modifiers))); return this; } public Builder allOtherMethods() { blocks.add(new Block.Methods(emptyList())); return this; } public Builder constructors(String... modifiers) { blocks.add(new Block.Constructors(Arrays.asList(modifiers))); return this; } public Builder allOtherConstructors() { blocks.add(new Block.Constructors(emptyList())); return this; } public Builder allGetters() { blocks.add(new Block.Getters()); return this; } public Builder allSetters() { blocks.add(new Block.Setters()); return this; } public Builder allWiths() { blocks.add(new Block.Withs()); return this; } public Builder allAccessors() { blocks.add(new Block.Accessors()); return this; } public Builder equals() { blocks.add(new Block.Equals()); return this; } public Builder hashCoding() { blocks.add(new Block.HashCode()); return this; } public Builder toStringing() { blocks.add(new Block.ToString()); return this; } public Builder blankLine() { if (!blocks.isEmpty() && blocks.get(blocks.size() - 1) instanceof Layout.Block.BlankLines) { ((Layout.Block.BlankLines) blocks.get(blocks.size() - 1)).count++; } else { blocks.add(new Layout.Block.BlankLines()); } return this; } public Layout build() { return new Layout(blocks); } } } } class ModifierComparator implements Comparator> { static final Comparator> BY_STRING = new ModifierComparator(); static final Comparator> BY_AST = (mods1, mods2) -> BY_STRING.compare( mods1.stream().map(mod -> mod.getClass().getSimpleName().toLowerCase()).collect(toList()), mods2.stream().map(mod -> mod.getClass().getSimpleName().toLowerCase()).collect(toList()) ); @Override public int compare(List mods1, List mods2) { boolean mods1Static = mods1.contains("static"); boolean mods2Static = mods2.contains("static"); if (mods1Static && !mods2Static) { return -1; } else if (!mods1Static && mods2Static) { return 1; } boolean mods1Final = mods1.contains("final"); boolean mods2Final = mods2.contains("final"); if (mods1Final && !mods2Final) { return -1; } else if (!mods1Final && mods2Final) { return 1; } int visibility = visibilityRank(mods1) - visibilityRank(mods2); if (visibility != 0) { return visibility; } for (int i = 0, mods1Size = mods1.size(); i < mods1Size; i++) { if (mods2.size() <= i) { return -1; } int comp = mods1.get(i).compareTo(mods2.get(i)); if (comp != 0) { return comp; } } return 0; } private int visibilityRank(List modifiers) { if (modifiers.contains("public")) { return 0; } else if (modifiers.contains("protected")) { return 1; } else if (modifiers.contains("private")) { return 3; } else { // package visibility return 2; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy