/*
* Copyright (C) 2010 The Project Lombok Authors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package lombok.ast.syntaxChecks;
import static lombok.ast.syntaxChecks.MessageKey.*;
import static lombok.ast.Message.*;
import java.util.Map;
import com.google.common.collect.Maps;
import lombok.ast.ForwardingAstVisitor;
import lombok.ast.Node;
/**
* The base class of {@code SyntacticValidityVisitor}, which is generated. Don't use this class, use
* the generated {@code SyntacticValidityVisitor}.
*/
public class SyntacticValidityVisitorBase extends ForwardingAstVisitor {
final Map<Class<?>, Object> checkerObjectStore = Maps.newHashMap();
final boolean recursing;
SyntacticValidityVisitorBase(boolean recursing) {
this.recursing = recursing;
}
@SuppressWarnings("unchecked")
<T> T getCheckerObject(Class<T> clazz) {
Object o = checkerObjectStore.get(clazz);
if (o != null) return (T)o;
try {
o = clazz.newInstance();
} catch (Exception e) {
throw new IllegalStateException("Class " + clazz.getName() + " could not be constructed. Does it have a public no-args constructort?", e);
}
checkerObjectStore.put(clazz, o);
return (T)o;
}
/**
* Checks if a given child is syntactically valid; you specify exactly what is required for this to hold in the parameters.
* This method will recursively call {@link #checkSyntacticValidity()} on the child.
*
* @param node The node responsible for the check.
* @param child The actual child node object that will be checked.
* @param name The name of this node. For example, in a binary operation, {@code "left side"}.
* @param mandatory If this node not being there (being {@code null}) is a problem or acceptable.
* @param typeAssertion If the node exists, it must be an instance of this type.
*/
void checkChildValidity(Node node, Node child, String name, boolean mandatory, Class<?> typeAssertion) {
verifyNodeRelation(node, child, name, mandatory, typeAssertion);
}
public static boolean verifyNodeRelation(Node parent, Node child, String name, boolean mandatory, Class<?> typeAssertion) {
String typeAssertionName = typeAssertion.getSimpleName().toLowerCase();
boolean typeAssertionVowel = startsWithVowel(typeAssertionName);
if (child == null) {
if (mandatory) {
parent.addMessage(error(NODE_MISSING_MANDATORY_CHILD, String.format("Missing %s %s",
name, typeAssertion.getSimpleName().toLowerCase())));
return false;
}
} else {
if (!typeAssertion.isInstance(child)) {
String actualName = child.getClass().getSimpleName();
child.addMessage(error(NODE_CHILD_TYPE_INCORRECT, String.format(
"%s isn't a%s %s but a%s %s",
name,
typeAssertionVowel ? "n" : "", typeAssertionName,
startsWithVowel(actualName) ? "n" : "", actualName)));
return false;
}
}
return true;
}
private static boolean startsWithVowel(String typeAssertionName) {
boolean typeAssertionVowel = typeAssertionName.isEmpty();
if (!typeAssertionVowel) {
char c = typeAssertionName.charAt(0);
typeAssertionVowel = (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u');
}
return typeAssertionVowel;
}
@Override public boolean visitParseArtefact(Node node) {
StringBuilder errorName = new StringBuilder();
boolean first = true;
for (char c : node.getClass().getSimpleName().toCharArray()) {
if (first) {
errorName.append(c);
first = false;
continue;
}
if (Character.isUpperCase(c)) errorName.append(" ").append(Character.toLowerCase(c));
else errorName.append(c);
}
node.addMessage(error(PARSEARTEFACT, "parse artefact remained in node tree"));
return !recursing;
}
}