Merge remote-tracking branch 'jp/master' into issue_1359
diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/body/VariableDeclarator.java b/javaparser-core/src/main/java/com/github/javaparser/ast/body/VariableDeclarator.java
index 45b4959..de728cd 100644
--- a/javaparser-core/src/main/java/com/github/javaparser/ast/body/VariableDeclarator.java
+++ b/javaparser-core/src/main/java/com/github/javaparser/ast/body/VariableDeclarator.java
@@ -118,7 +118,7 @@
                     if (vd.getParentNode().isPresent() && vd.getParentNode().get() instanceof NodeWithVariables) {
                         NodeWithVariables nodeWithVariables = (NodeWithVariables) vd.getParentNode().get();
                         // We calculate the value the property will assume after the change will be completed
-                        Type currentMaxCommonType = nodeWithVariables.getMaximumCommonType();
+                        Optional<Type> currentMaxCommonType = nodeWithVariables.getMaximumCommonType();
                         List<Type> types = new LinkedList<>();
                         int index = nodeWithVariables.getVariables().indexOf(vd);
                         for (int i = 0; i < nodeWithVariables.getVariables().size(); i++) {
@@ -128,8 +128,8 @@
                                 types.add(nodeWithVariables.getVariable(i).getType());
                             }
                         }
-                        Type newMaxCommonType = NodeWithVariables.calculateMaximumCommonType(types);
-                        ((Node) nodeWithVariables).notifyPropertyChange(ObservableProperty.MAXIMUM_COMMON_TYPE, currentMaxCommonType, newMaxCommonType);
+                        Optional<Type> newMaxCommonType = NodeWithVariables.calculateMaximumCommonType(types);
+                        ((Node) nodeWithVariables).notifyPropertyChange(ObservableProperty.MAXIMUM_COMMON_TYPE, currentMaxCommonType.orElse(null), newMaxCommonType.orElse(null));
                     }
                 }
             }
diff --git a/javaparser-core/src/main/java/com/github/javaparser/ast/nodeTypes/NodeWithVariables.java b/javaparser-core/src/main/java/com/github/javaparser/ast/nodeTypes/NodeWithVariables.java
index 5cce28f..5daf38a 100644
--- a/javaparser-core/src/main/java/com/github/javaparser/ast/nodeTypes/NodeWithVariables.java
+++ b/javaparser-core/src/main/java/com/github/javaparser/ast/nodeTypes/NodeWithVariables.java
@@ -29,6 +29,7 @@
 import com.github.javaparser.metamodel.DerivedProperty;
 
 import java.util.List;
+import java.util.Optional;
 import java.util.stream.Collectors;
 
 /**
@@ -109,28 +110,28 @@
      * <br/>For <code>int[] a[][],b[],c[][];</code> this is int[][].
      */
     @DerivedProperty
-    default Type getMaximumCommonType() {
-        return calculateMaximumCommonType(this.getVariables().stream().map(v -> v.getType()).collect(Collectors.toList()));
+    default Optional<Type> getMaximumCommonType() {
+        return calculateMaximumCommonType(getVariables().stream().map(v -> v.getType()).collect(Collectors.toList()));
     }
 
-    static Type calculateMaximumCommonType(List<Type> types) {
+    static Optional<Type> calculateMaximumCommonType(List<Type> types) {
         // we use a local class because we cannot use an helper static method in an interface
         class Helper {
             // Conceptually: given a type we start from the Element Type and get as many array levels as indicated
             // From the implementation point of view we start from the actual type and we remove how many array
             // levels as needed to get the target level of arrays
             // It returns null if the type has less array levels then the desired target
-            private Type toArrayLevel(Type type, int level) {
+            private Optional<Type> toArrayLevel(Type type, int level) {
                 if (level > type.getArrayLevel()) {
-                    return null;
+                    return Optional.empty();
                 }
                 for (int i = type.getArrayLevel(); i > level; i--) {
                     if (!(type instanceof ArrayType)) {
-                        throw new AssertionError("The variables do not have a common type.");
+                        return Optional.empty();
                     }
                     type = ((ArrayType) type).getComponentType();
                 }
-                return type;
+                return Optional.of(type);
             }
         }
 
@@ -145,8 +146,8 @@
             // the pretty-printed string got for a node. We just check all them are the same and if they
             // are we just just is not null
             Object[] values = types.stream().map(v -> {
-                Type t = helper.toArrayLevel(v, currentLevel);
-                return t == null ? null : t.toString();
+                Optional<Type> t = helper.toArrayLevel(v, currentLevel);
+                return t.map(Node::toString).orElse(null);
             }).distinct().toArray();
             if (values.length == 1 && values[0] != null) {
                 level++;
diff --git a/javaparser-core/src/main/java/com/github/javaparser/printer/PrettyPrintVisitor.java b/javaparser-core/src/main/java/com/github/javaparser/printer/PrettyPrintVisitor.java
index 9b8d56b..b771c8e 100644
--- a/javaparser-core/src/main/java/com/github/javaparser/printer/PrettyPrintVisitor.java
+++ b/javaparser-core/src/main/java/com/github/javaparser/printer/PrettyPrintVisitor.java
@@ -482,7 +482,11 @@
         printMemberAnnotations(n.getAnnotations(), arg);
         printModifiers(n.getModifiers());
         if (!n.getVariables().isEmpty()) {
-            n.getMaximumCommonType().accept(this, arg);
+            Optional<Type> maximumCommonType = n.getMaximumCommonType();
+            maximumCommonType.ifPresent(t -> t.accept(this, arg));
+            if(!maximumCommonType.isPresent()){
+                printer.print("???");
+            }
         }
 
         printer.print(" ");
@@ -502,25 +506,25 @@
         printComment(n.getComment(), arg);
         n.getName().accept(this, arg);
 
-        Optional<NodeWithVariables> ancestor = n.getAncestorOfType(NodeWithVariables.class);
-        if (!ancestor.isPresent()) {
-            throw new RuntimeException("Unable to work with VariableDeclarator not owned by a NodeWithVariables");
-        }
-        Type commonType = ancestor.get().getMaximumCommonType();
+        n.getAncestorOfType(NodeWithVariables.class).ifPresent(ancestor -> {
+            Optional<Type> maximumCommonType = ancestor.getMaximumCommonType();
+            maximumCommonType.ifPresent(commonType -> {
 
-        Type type = n.getType();
+                Type type = n.getType();
 
-        ArrayType arrayType = null;
+                ArrayType arrayType = null;
 
-        for (int i = commonType.getArrayLevel(); i < type.getArrayLevel(); i++) {
-            if (arrayType == null) {
-                arrayType = (ArrayType) type;
-            } else {
-                arrayType = (ArrayType) arrayType.getComponentType();
-            }
-            printAnnotations(arrayType.getAnnotations(), true, arg);
-            printer.print("[]");
-        }
+                for (int i = commonType.getArrayLevel(); i < type.getArrayLevel(); i++) {
+                    if (arrayType == null) {
+                        arrayType = (ArrayType) type;
+                    } else {
+                        arrayType = (ArrayType) arrayType.getComponentType();
+                    }
+                    printAnnotations(arrayType.getAnnotations(), true, arg);
+                    printer.print("[]");
+                }
+            });
+        });
 
         if (n.getInitializer().isPresent()) {
             printer.print(" = ");
@@ -913,7 +917,7 @@
         printModifiers(n.getModifiers());
 
         if (!n.getVariables().isEmpty()) {
-            n.getMaximumCommonType().accept(this, arg);
+            n.getMaximumCommonType().ifPresent(t -> t.accept(this, arg));
         }
         printer.print(" ");
 
diff --git a/javaparser-core/src/main/java/com/github/javaparser/printer/lexicalpreservation/LexicalPreservingPrinter.java b/javaparser-core/src/main/java/com/github/javaparser/printer/lexicalpreservation/LexicalPreservingPrinter.java
index c874345..a435dc4 100644
--- a/javaparser-core/src/main/java/com/github/javaparser/printer/lexicalpreservation/LexicalPreservingPrinter.java
+++ b/javaparser-core/src/main/java/com/github/javaparser/printer/lexicalpreservation/LexicalPreservingPrinter.java
@@ -57,7 +57,7 @@
 
 /**
  * A Lexical Preserving Printer is used to capture all the lexical information while parsing, update them when
- * operating on the AST and then used them to reproduce the source code 
+ * operating on the AST and then used them to reproduce the source code
  * in its original formatting including the AST changes.
  */
 public class LexicalPreservingPrinter {
@@ -110,7 +110,7 @@
         });
         return node;
     }
-    
+
     //
     // Constructor and setup
     //
@@ -401,16 +401,17 @@
         // Array brackets are a pain... we do not have a way to represent them explicitly in the AST
         // so they have to be handled in a special way
         if (node instanceof VariableDeclarator) {
-            VariableDeclarator variableDeclarator = (VariableDeclarator)node;
-            if (!variableDeclarator.getParentNode().isPresent()) {
-                throw new RuntimeException("VariableDeclarator without parent: I cannot handle the array levels");
-            }
-            NodeWithVariables<?> nodeWithVariables = (NodeWithVariables)variableDeclarator.getParentNode().get();
-            int extraArrayLevels = variableDeclarator.getType().getArrayLevel() - nodeWithVariables.getMaximumCommonType().getArrayLevel();
-            for (int i=0; i<extraArrayLevels; i++) {
-                nodeText.addElement(new TokenTextElement(LBRACKET));
-                nodeText.addElement(new TokenTextElement(RBRACKET));
-            }
+            VariableDeclarator variableDeclarator = (VariableDeclarator) node;
+            variableDeclarator.getParentNode().ifPresent(parent -> {
+                NodeWithVariables<?> nodeWithVariables = (NodeWithVariables) parent;
+                nodeWithVariables.getMaximumCommonType().ifPresent(mct -> {
+                    int extraArrayLevels = variableDeclarator.getType().getArrayLevel() - mct.getArrayLevel();
+                    for (int i = 0; i < extraArrayLevels; i++) {
+                        nodeText.addElement(new TokenTextElement(LBRACKET));
+                        nodeText.addElement(new TokenTextElement(RBRACKET));
+                    }
+                });
+            });
         }
         return nodeText;
     }
diff --git a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/FieldAccessContext.java b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/FieldAccessContext.java
index 860fe6b..65ccedf 100644
--- a/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/FieldAccessContext.java
+++ b/javaparser-symbol-solver-core/src/main/java/com/github/javaparser/symbolsolver/javaparsermodel/contexts/FieldAccessContext.java
@@ -53,7 +53,7 @@
 
     @Override
     public SymbolReference<? extends ResolvedValueDeclaration> solveSymbol(String name, TypeSolver typeSolver) {
-        if (wrappedNode.getField().toString().equals(name)) {
+        if (wrappedNode.getName().toString().equals(name)) {
             if (wrappedNode.getScope() instanceof ThisExpr) {
                 ResolvedType typeOfThis = JavaParserFacade.get(typeSolver).getTypeOfThisIn(wrappedNode);
                 return new SymbolSolver(typeSolver).solveSymbolInType(typeOfThis.asReferenceType().getTypeDeclaration(), name);
@@ -75,18 +75,14 @@
     @Override
     public Optional<Value> solveSymbolAsValue(String name, TypeSolver typeSolver) {
         Expression scope = wrappedNode.getScope();
-        if (wrappedNode.getField().toString().equals(name)) {
+        if (wrappedNode.getName().toString().equals(name)) {
             ResolvedType typeOfScope = JavaParserFacade.get(typeSolver).getType(scope);
             if (typeOfScope.isArray() && name.equals(ARRAY_LENGTH_FIELD_NAME)) {
                 return Optional.of(new Value(ResolvedPrimitiveType.INT, ARRAY_LENGTH_FIELD_NAME));
             }
             if (typeOfScope.isReferenceType()) {
                 Optional<ResolvedType> typeUsage = typeOfScope.asReferenceType().getFieldType(name);
-                if (typeUsage.isPresent()) {
-                    return Optional.of(new Value(typeUsage.get(), name));
-                } else {
-                    return Optional.empty();
-                }
+                return typeUsage.map(resolvedType -> new Value(resolvedType, name));
             } else {
                 return Optional.empty();
             }
diff --git a/javaparser-testing/src/test/java/com/github/javaparser/printer/PrettyPrintVisitorTest.java b/javaparser-testing/src/test/java/com/github/javaparser/printer/PrettyPrintVisitorTest.java
index b8c95a6..dd8738a 100644
--- a/javaparser-testing/src/test/java/com/github/javaparser/printer/PrettyPrintVisitorTest.java
+++ b/javaparser-testing/src/test/java/com/github/javaparser/printer/PrettyPrintVisitorTest.java
@@ -42,22 +42,22 @@
     @Test
     public void getMaximumCommonTypeWithoutAnnotations() {
         VariableDeclarationExpr vde1 = JavaParser.parseVariableDeclarationExpr("int a[], b[]");
-        assertEquals("int[]", vde1.getMaximumCommonType().toString());
+        assertEquals("int[]", vde1.getMaximumCommonType().get().toString());
 
         VariableDeclarationExpr vde2 = JavaParser.parseVariableDeclarationExpr("int[][] a[], b[]");
-        assertEquals("int[][][]", vde2.getMaximumCommonType().toString());
+        assertEquals("int[][][]", vde2.getMaximumCommonType().get().toString());
 
         VariableDeclarationExpr vde3 = JavaParser.parseVariableDeclarationExpr("int[][] a, b[]");
-        assertEquals("int[][]", vde3.getMaximumCommonType().toString());
+        assertEquals("int[][]", vde3.getMaximumCommonType().get().toString());
     }
 
     @Test
     public void getMaximumCommonTypeWithAnnotations() {
         VariableDeclarationExpr vde1 = JavaParser.parseVariableDeclarationExpr("int a @Foo [], b[]");
-        assertEquals("int", vde1.getMaximumCommonType().toString());
+        assertEquals("int", vde1.getMaximumCommonType().get().toString());
 
         VariableDeclarationExpr vde2 = JavaParser.parseVariableDeclarationExpr("int[]@Foo [] a[], b[]");
-        assertEquals("int[] @Foo [][]", vde2.getMaximumCommonType().toString());
+        assertEquals("int[] @Foo [][]", vde2.getMaximumCommonType().get().toString());
     }
 
     private String print(Node node) {
diff --git a/javaparser-testing/src/test/java/com/github/javaparser/printer/PrettyPrinterTest.java b/javaparser-testing/src/test/java/com/github/javaparser/printer/PrettyPrinterTest.java
index 88bd559..fef3609 100644
--- a/javaparser-testing/src/test/java/com/github/javaparser/printer/PrettyPrinterTest.java
+++ b/javaparser-testing/src/test/java/com/github/javaparser/printer/PrettyPrinterTest.java
@@ -21,25 +21,26 @@
 
 package com.github.javaparser.printer;
 
-import com.github.javaparser.JavaParser;
 import com.github.javaparser.ast.CompilationUnit;
 import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
 import com.github.javaparser.ast.body.FieldDeclaration;
 import com.github.javaparser.ast.expr.VariableDeclarationExpr;
+import com.github.javaparser.ast.type.PrimitiveType;
 import org.junit.Test;
 
+import static com.github.javaparser.JavaParser.*;
 import static com.github.javaparser.utils.TestUtils.assertEqualsNoEol;
 import static org.junit.Assert.assertEquals;
 
 public class PrettyPrinterTest {
 
     private String prettyPrintField(String code) {
-        CompilationUnit cu = JavaParser.parse(code);
+        CompilationUnit cu = parse(code);
         return new PrettyPrinter().print(cu.findFirst(FieldDeclaration.class).get());
     }
 
     private String prettyPrintVar(String code) {
-        CompilationUnit cu = JavaParser.parse(code);
+        CompilationUnit cu = parse(code);
         return new PrettyPrinter().print(cu.findAll(VariableDeclarationExpr.class).get(0));
     }
 
@@ -82,7 +83,7 @@
     }
 
     private String prettyPrintConfigurable(String code) {
-        CompilationUnit cu = JavaParser.parse(code);
+        CompilationUnit cu = parse(code);
         PrettyPrinter printer = new PrettyPrinter(new PrettyPrinterConfiguration().setVisitorFactory(TestVisitor::new));
         return printer.print(cu.findFirst(ClassOrInterfaceDeclaration.class).get());
     }
@@ -115,7 +116,7 @@
                 "}" + EOL +
                 "";
 
-        assertEquals(expected, new PrettyPrinter(config).print(JavaParser.parse(code)));
+        assertEquals(expected, new PrettyPrinter(config).print(parse(code)));
     }
 
     @Test
@@ -132,7 +133,7 @@
                 "}" + EOL +
                 "";
 
-        assertEquals(expected, new PrettyPrinter(config).print(JavaParser.parse(code)));
+        assertEquals(expected, new PrettyPrinter(config).print(parse(code)));
     }
 
     @Test
@@ -156,7 +157,7 @@
                 "}" + EOL +
                 "";
 
-        assertEquals(expected, new PrettyPrinter(config).print(JavaParser.parse(code)));
+        assertEquals(expected, new PrettyPrinter(config).print(parse(code)));
     }
 
     @Test
@@ -173,18 +174,33 @@
                 "}" + EOL +
                 "";
 
-        assertEquals(expected, new PrettyPrinter(config).print(JavaParser.parse(code)));
+        assertEquals(expected, new PrettyPrinter(config).print(parse(code)));
     }
 
     @Test
     public void enumConstantsHorizontally() {
-        CompilationUnit cu = JavaParser.parse("enum X{A, B, C, D, E}");
+        CompilationUnit cu = parse("enum X{A, B, C, D, E}");
         assertEqualsNoEol("enum X {\n\n    A, B, C, D, E\n}\n", new PrettyPrinter().print(cu));
     }
 
     @Test
     public void enumConstantsVertically() {
-        CompilationUnit cu = JavaParser.parse("enum X{A, B, C, D, E, F}");
+        CompilationUnit cu = parse("enum X{A, B, C, D, E, F}");
         assertEqualsNoEol("enum X {\n\n    A,\n    B,\n    C,\n    D,\n    E,\n    F\n}\n", new PrettyPrinter().print(cu));
     }
+
+    @Test
+    public void printingInconsistentVariables() {
+        FieldDeclaration fieldDeclaration = parseBodyDeclaration("int a, b;").asFieldDeclaration();
+
+        assertEquals("int a, b;", fieldDeclaration.toString());
+
+        fieldDeclaration.getVariable(0).setType(PrimitiveType.doubleType());
+
+        assertEquals("??? a, b;", fieldDeclaration.toString());
+
+        fieldDeclaration.getVariable(1).setType(PrimitiveType.doubleType());
+
+        assertEquals("double a, b;", fieldDeclaration.toString());
+    }
 }
diff --git a/javaparser-testing/src/test/java/com/github/javaparser/printer/lexicalpreservation/transformations/ast/body/FieldDeclarationTransformationsTest.java b/javaparser-testing/src/test/java/com/github/javaparser/printer/lexicalpreservation/transformations/ast/body/FieldDeclarationTransformationsTest.java
index eda7190..994f5d8 100644
--- a/javaparser-testing/src/test/java/com/github/javaparser/printer/lexicalpreservation/transformations/ast/body/FieldDeclarationTransformationsTest.java
+++ b/javaparser-testing/src/test/java/com/github/javaparser/printer/lexicalpreservation/transformations/ast/body/FieldDeclarationTransformationsTest.java
@@ -44,24 +44,33 @@
     // Modifiers
 
     @Test
-    public void addingModifiers() throws IOException {
+    public void addingModifiers() {
         FieldDeclaration it = consider("int A;");
         it.setModifiers(EnumSet.of(Modifier.PUBLIC));
         assertTransformedToString("public int A;", it);
     }
 
     @Test
-    public void removingModifiers() throws IOException {
+    public void removingModifiers() {
         FieldDeclaration it = consider("public int A;");
         it.setModifiers(EnumSet.noneOf(Modifier.class));
         assertTransformedToString("int A;", it);
     }
 
     @Test
-    public void replacingModifiers() throws IOException {
+    public void replacingModifiers() {
         FieldDeclaration it = consider("int A;");
         it.setModifiers(EnumSet.of(Modifier.PROTECTED));
         assertTransformedToString("protected int A;", it);
     }
 
+    @Test
+    public void changingTypes() {
+        FieldDeclaration it = consider("int a, b;");
+        assertTransformedToString("int a, b;", it);
+        it.getVariable(0).setType("Xyz");
+        assertTransformedToString(" a, b;", it);
+        it.getVariable(1).setType("Xyz");
+        assertTransformedToString("Xyz a, b;", it);
+    }
 }