diff options
| author | Reinier Zwitserloot <reinier@tipit.to> | 2009-11-25 07:32:49 +0100 |
|---|---|---|
| committer | Reinier Zwitserloot <reinier@tipit.to> | 2009-11-25 07:32:49 +0100 |
| commit | 1a0e611a9c5e1ee518670647ce1a44beae559b44 (patch) | |
| tree | e5ef8f671bc6688f486e874d4e2e1a7813e4f0b2 /src/core/lombok/eclipse | |
| parent | 7fd947ea40c25dad9ee543ebc4b92de9a2e05efc (diff) | |
| download | lombok-1a0e611a9c5e1ee518670647ce1a44beae559b44.tar.gz lombok-1a0e611a9c5e1ee518670647ce1a44beae559b44.tar.bz2 lombok-1a0e611a9c5e1ee518670647ce1a44beae559b44.zip | |
Refactored the source folders.
Diffstat (limited to 'src/core/lombok/eclipse')
20 files changed, 4507 insertions, 0 deletions
diff --git a/src/core/lombok/eclipse/Eclipse.java b/src/core/lombok/eclipse/Eclipse.java new file mode 100644 index 00000000..41d9300f --- /dev/null +++ b/src/core/lombok/eclipse/Eclipse.java @@ -0,0 +1,479 @@ +/* + * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * + * 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.eclipse; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import lombok.Lombok; +import lombok.core.AnnotationValues; +import lombok.core.TypeLibrary; +import lombok.core.TypeResolver; +import lombok.core.AST.Kind; +import lombok.core.AnnotationValues.AnnotationValue; + +import org.eclipse.core.runtime.ILog; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.ArrayInitializer; +import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference; +import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Expression; +import org.eclipse.jdt.internal.compiler.ast.Literal; +import org.eclipse.jdt.internal.compiler.ast.MarkerAnnotation; +import org.eclipse.jdt.internal.compiler.ast.MemberValuePair; +import org.eclipse.jdt.internal.compiler.ast.NormalAnnotation; +import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; +import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; +import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; +import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; +import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; +import org.eclipse.jdt.internal.compiler.ast.TypeParameter; +import org.eclipse.jdt.internal.compiler.ast.TypeReference; +import org.eclipse.jdt.internal.compiler.ast.Wildcard; +import org.eclipse.jdt.internal.compiler.lookup.TypeIds; +import org.osgi.framework.Bundle; + +public class Eclipse { + /** + * Eclipse's Parser class is instrumented to not attempt to fill in the body of any method or initializer + * or field initialization if this flag is set. Set it on the flag field of + * any method, field, or initializer you create! + */ + public static final int ECLIPSE_DO_NOT_TOUCH_FLAG = ASTNode.Bit24; + + private Eclipse() { + //Prevent instantiation + } + + private static final String DEFAULT_BUNDLE = "org.eclipse.jdt.core"; + + /** + * Generates an error in the Eclipse error log. Note that most people never look at it! + */ + public static void error(CompilationUnitDeclaration cud, String message) { + error(cud, message, DEFAULT_BUNDLE, null); + } + + /** + * Generates an error in the Eclipse error log. Note that most people never look at it! + */ + public static void error(CompilationUnitDeclaration cud, String message, Throwable error) { + error(cud, message, DEFAULT_BUNDLE, error); + } + + /** + * Generates an error in the Eclipse error log. Note that most people never look at it! + */ + public static void error(CompilationUnitDeclaration cud, String message, String bundleName) { + error(cud, message, bundleName, null); + } + + /** + * Generates an error in the Eclipse error log. Note that most people never look at it! + */ + public static void error(CompilationUnitDeclaration cud, String message, String bundleName, Throwable error) { + Bundle bundle = Platform.getBundle(bundleName); + if (bundle == null) { + System.err.printf("Can't find bundle %s while trying to report error:\n%s\n", bundleName, message); + return; + } + + ILog log = Platform.getLog(bundle); + + log.log(new Status(IStatus.ERROR, bundleName, message, error)); + if (cud != null) EclipseAST.addProblemToCompilationResult(cud, false, message + " - See error log.", 0, 0); + } + + /** + * For 'speed' reasons, Eclipse works a lot with char arrays. I have my doubts this was a fruitful exercise, + * but we need to deal with it. This turns [[java][lang][String]] into "java.lang.String". + */ + public static String toQualifiedName(char[][] typeName) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (char[] c : typeName) { + sb.append(first ? "" : ".").append(c); + first = false; + } + return sb.toString(); + } + + public static char[][] fromQualifiedName(String typeName) { + String[] split = typeName.split("\\."); + char[][] result = new char[split.length][]; + for (int i = 0; i < split.length; i++) { + result[i] = split[i].toCharArray(); + } + return result; + } + + + /** + * You can't share TypeParameter objects or bad things happen; for example, one 'T' resolves differently + * from another 'T', even for the same T in a single class file. Unfortunately the TypeParameter type hierarchy + * is complicated and there's no clone method on TypeParameter itself. This method can clone them. + */ + public static TypeParameter[] copyTypeParams(TypeParameter[] params, ASTNode source) { + if (params == null) return null; + TypeParameter[] out = new TypeParameter[params.length]; + int idx = 0; + for (TypeParameter param : params) { + TypeParameter o = new TypeParameter(); + setGeneratedBy(o, source); + o.annotations = param.annotations; + o.bits = param.bits; + o.modifiers = param.modifiers; + o.name = param.name; + o.type = copyType(param.type, source); + o.sourceStart = param.sourceStart; + o.sourceEnd = param.sourceEnd; + o.declarationEnd = param.declarationEnd; + o.declarationSourceStart = param.declarationSourceStart; + o.declarationSourceEnd = param.declarationSourceEnd; + if (param.bounds != null) { + TypeReference[] b = new TypeReference[param.bounds.length]; + int idx2 = 0; + for (TypeReference ref : param.bounds) b[idx2++] = copyType(ref, source); + o.bounds = b; + } + out[idx++] = o; + } + return out; + } + + /** + * Convenience method that creates a new array and copies each TypeReference in the source array via + * {@link #copyType(TypeReference, ASTNode)}. + */ + public static TypeReference[] copyTypes(TypeReference[] refs, ASTNode source) { + if (refs == null) return null; + TypeReference[] outs = new TypeReference[refs.length]; + int idx = 0; + for (TypeReference ref : refs) { + outs[idx++] = copyType(ref, source); + } + return outs; + } + + /** + * You can't share TypeReference objects or subtle errors start happening. + * Unfortunately the TypeReference type hierarchy is complicated and there's no clone + * method on TypeReference itself. This method can clone them. + */ + public static TypeReference copyType(TypeReference ref, ASTNode source) { + if (ref instanceof ParameterizedQualifiedTypeReference) { + ParameterizedQualifiedTypeReference iRef = (ParameterizedQualifiedTypeReference) ref; + TypeReference[][] args = null; + if (iRef.typeArguments != null) { + args = new TypeReference[iRef.typeArguments.length][]; + int idx = 0; + for (TypeReference[] inRefArray : iRef.typeArguments) { + if (inRefArray == null) args[idx++] = null; + else { + TypeReference[] outRefArray = new TypeReference[inRefArray.length]; + int idx2 = 0; + for (TypeReference inRef : inRefArray) { + outRefArray[idx2++] = copyType(inRef, source); + } + args[idx++] = outRefArray; + } + } + } + TypeReference typeRef = new ParameterizedQualifiedTypeReference(iRef.tokens, args, iRef.dimensions(), iRef.sourcePositions); + setGeneratedBy(typeRef, source); + return typeRef; + } + + if (ref instanceof ArrayQualifiedTypeReference) { + ArrayQualifiedTypeReference iRef = (ArrayQualifiedTypeReference) ref; + TypeReference typeRef = new ArrayQualifiedTypeReference(iRef.tokens, iRef.dimensions(), iRef.sourcePositions); + setGeneratedBy(typeRef, source); + return typeRef; + } + + if (ref instanceof QualifiedTypeReference) { + QualifiedTypeReference iRef = (QualifiedTypeReference) ref; + TypeReference typeRef = new QualifiedTypeReference(iRef.tokens, iRef.sourcePositions); + setGeneratedBy(typeRef, source); + return typeRef; + } + + if (ref instanceof ParameterizedSingleTypeReference) { + ParameterizedSingleTypeReference iRef = (ParameterizedSingleTypeReference) ref; + TypeReference[] args = null; + if (iRef.typeArguments != null) { + args = new TypeReference[iRef.typeArguments.length]; + int idx = 0; + for (TypeReference inRef : iRef.typeArguments) { + if (inRef == null) args[idx++] = null; + else args[idx++] = copyType(inRef, source); + } + } + + TypeReference typeRef = new ParameterizedSingleTypeReference(iRef.token, args, iRef.dimensions(), (long)iRef.sourceStart << 32 | iRef.sourceEnd); + setGeneratedBy(typeRef, source); + return typeRef; + } + + if (ref instanceof ArrayTypeReference) { + ArrayTypeReference iRef = (ArrayTypeReference) ref; + TypeReference typeRef = new ArrayTypeReference(iRef.token, iRef.dimensions(), (long)iRef.sourceStart << 32 | iRef.sourceEnd); + setGeneratedBy(typeRef, source); + return typeRef; + } + + if (ref instanceof Wildcard) { + Wildcard wildcard = new Wildcard(((Wildcard)ref).kind); + wildcard.sourceStart = ref.sourceStart; + wildcard.sourceEnd = ref.sourceEnd; + setGeneratedBy(wildcard, source); + return wildcard; + } + + if (ref instanceof SingleTypeReference) { + SingleTypeReference iRef = (SingleTypeReference) ref; + TypeReference typeRef = new SingleTypeReference(iRef.token, (long)iRef.sourceStart << 32 | iRef.sourceEnd); + setGeneratedBy(typeRef, source); + return typeRef; + } + + return ref; + } + + public static Annotation[] copyAnnotations(Annotation[] annotations, ASTNode source) { + return copyAnnotations(annotations, null, source); + } + + public static Annotation[] copyAnnotations(Annotation[] annotations1, Annotation[] annotations2, ASTNode source) { + if (annotations1 == null && annotations2 == null) return null; + if (annotations1 == null) annotations1 = new Annotation[0]; + if (annotations2 == null) annotations2 = new Annotation[0]; + Annotation[] outs = new Annotation[annotations1.length + annotations2.length]; + int idx = 0; + for (Annotation annotation : annotations1) { + outs[idx++] = copyAnnotation(annotation, source); + } + for (Annotation annotation : annotations2) { + outs[idx++] = copyAnnotation(annotation, source); + } + return outs; + } + + public static Annotation copyAnnotation(Annotation annotation, ASTNode source) { + int pS = source.sourceStart, pE = source.sourceEnd; + + if (annotation instanceof MarkerAnnotation) { + MarkerAnnotation ann = new MarkerAnnotation(copyType(annotation.type, source), pS); + setGeneratedBy(ann, source); + ann.declarationSourceEnd = ann.sourceEnd = ann.statementEnd = pE; + return ann; + } + + if (annotation instanceof SingleMemberAnnotation) { + SingleMemberAnnotation ann = new SingleMemberAnnotation(copyType(annotation.type, source), pS); + setGeneratedBy(ann, source); + ann.declarationSourceEnd = ann.sourceEnd = ann.statementEnd = pE; + //TODO memberValue(s) need to be copied as well (same for copying a NormalAnnotation as below). + ann.memberValue = ((SingleMemberAnnotation)annotation).memberValue; + return ann; + } + + if (annotation instanceof NormalAnnotation) { + NormalAnnotation ann = new NormalAnnotation(copyType(annotation.type, source), pS); + setGeneratedBy(ann, source); + ann.declarationSourceEnd = ann.statementEnd = ann.sourceEnd = pE; + ann.memberValuePairs = ((NormalAnnotation)annotation).memberValuePairs; + return ann; + } + + return annotation; + } + + /** + * Checks if the provided annotation type is likely to be the intended type for the given annotation node. + * + * This is a guess, but a decent one. + */ + public static boolean annotationTypeMatches(Class<? extends java.lang.annotation.Annotation> type, EclipseNode node) { + if (node.getKind() != Kind.ANNOTATION) return false; + TypeReference typeRef = ((Annotation)node.get()).type; + if (typeRef == null || typeRef.getTypeName() == null) return false; + String typeName = toQualifiedName(typeRef.getTypeName()); + + TypeLibrary library = new TypeLibrary(); + library.addType(type.getName()); + TypeResolver resolver = new TypeResolver(library, node.getPackageDeclaration(), node.getImportStatements()); + Collection<String> typeMatches = resolver.findTypeMatches(node, typeName); + + for (String match : typeMatches) { + if (match.equals(type.getName())) return true; + } + + return false; + } + + /** + * Provides AnnotationValues with the data it needs to do its thing. + */ + public static <A extends java.lang.annotation.Annotation> AnnotationValues<A> + createAnnotation(Class<A> type, final EclipseNode annotationNode) { + final Annotation annotation = (Annotation) annotationNode.get(); + Map<String, AnnotationValue> values = new HashMap<String, AnnotationValue>(); + + final MemberValuePair[] pairs = annotation.memberValuePairs(); + for (Method m : type.getDeclaredMethods()) { + if (!Modifier.isPublic(m.getModifiers())) continue; + String name = m.getName(); + List<String> raws = new ArrayList<String>(); + List<Object> guesses = new ArrayList<Object>(); + Expression fullExpression = null; + Expression[] expressions = null; + + if (pairs != null) for (MemberValuePair pair : pairs) { + char[] n = pair.name; + String mName = n == null ? "value" : new String(pair.name); + if (mName.equals(name)) fullExpression = pair.value; + } + + boolean isExplicit = fullExpression != null; + + if (isExplicit) { + if (fullExpression instanceof ArrayInitializer) { + expressions = ((ArrayInitializer)fullExpression).expressions; + } else expressions = new Expression[] { fullExpression }; + if (expressions != null) for (Expression ex : expressions) { + StringBuffer sb = new StringBuffer(); + ex.print(0, sb); + raws.add(sb.toString()); + guesses.add(calculateValue(ex)); + } + } + + final Expression fullExpr = fullExpression; + final Expression[] exprs = expressions; + + values.put(name, new AnnotationValue(annotationNode, raws, guesses, isExplicit) { + @Override public void setError(String message, int valueIdx) { + Expression ex; + if (valueIdx == -1) ex = fullExpr; + else ex = exprs != null ? exprs[valueIdx] : null; + + if (ex == null) ex = annotation; + + int sourceStart = ex.sourceStart; + int sourceEnd = ex.sourceEnd; + + annotationNode.addError(message, sourceStart, sourceEnd); + } + + @Override public void setWarning(String message, int valueIdx) { + Expression ex; + if (valueIdx == -1) ex = fullExpr; + else ex = exprs != null ? exprs[valueIdx] : null; + + if (ex == null) ex = annotation; + + int sourceStart = ex.sourceStart; + int sourceEnd = ex.sourceEnd; + + annotationNode.addWarning(message, sourceStart, sourceEnd); + } + }); + } + + return new AnnotationValues<A>(type, values, annotationNode); + } + + private static Object calculateValue(Expression e) { + if (e instanceof Literal) { + ((Literal)e).computeConstant(); + switch (e.constant.typeID()) { + case TypeIds.T_int: return e.constant.intValue(); + case TypeIds.T_byte: return e.constant.byteValue(); + case TypeIds.T_short: return e.constant.shortValue(); + case TypeIds.T_char: return e.constant.charValue(); + case TypeIds.T_float: return e.constant.floatValue(); + case TypeIds.T_double: return e.constant.doubleValue(); + case TypeIds.T_boolean: return e.constant.booleanValue(); + case TypeIds.T_long: return e.constant.longValue(); + case TypeIds.T_JavaLangString: return e.constant.stringValue(); + default: return null; + } + } else if (e instanceof ClassLiteralAccess) { + return Eclipse.toQualifiedName(((ClassLiteralAccess)e).type.getTypeName()); + } else if (e instanceof SingleNameReference) { + return new String(((SingleNameReference)e).token); + } else if (e instanceof QualifiedNameReference) { + String qName = Eclipse.toQualifiedName(((QualifiedNameReference)e).tokens); + int idx = qName.lastIndexOf('.'); + return idx == -1 ? qName : qName.substring(idx+1); + } + + return null; + } + + private static Field generatedByField; + + static { + try { + generatedByField = ASTNode.class.getDeclaredField("$generatedBy"); + } catch (Throwable t) { + throw Lombok.sneakyThrow(t); + } + } + + public static ASTNode getGeneratedBy(ASTNode node) { + try { + return (ASTNode) generatedByField.get(node); + } catch (Exception t) { + throw Lombok.sneakyThrow(t); + } + } + + public static boolean isGenerated(ASTNode node) { + return getGeneratedBy(node) != null; + } + + public static ASTNode setGeneratedBy(ASTNode node, ASTNode source) { + try { + generatedByField.set(node, source); + } catch (Exception t) { + throw Lombok.sneakyThrow(t); + } + + return node; + } +} diff --git a/src/core/lombok/eclipse/EclipseAST.java b/src/core/lombok/eclipse/EclipseAST.java new file mode 100644 index 00000000..e42e5de2 --- /dev/null +++ b/src/core/lombok/eclipse/EclipseAST.java @@ -0,0 +1,366 @@ +/* + * Copyright © 2009 Reinier Zwitserloot and Roel Spilker. + * + * 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.eclipse; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import lombok.core.AST; + +import org.eclipse.jdt.core.compiler.CategorizedProblem; +import org.eclipse.jdt.internal.compiler.CompilationResult; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Annotation; +import org.eclipse.jdt.internal.compiler.ast.Argument; +import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; +import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; +import org.eclipse.jdt.internal.compiler.ast.ImportReference; +import org.eclipse.jdt.internal.compiler.ast.Initializer; +import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; +import org.eclipse.jdt.internal.compiler.ast.Statement; +import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.problem.DefaultProblem; +import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; +import org.eclipse.jdt.internal.compiler.util.Util; + +/** + * Wraps around Eclipse's internal AST view to add useful features as well as the ability to visit parents from children, + * something Eclipse own AST system does not offer. + */ +public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> { + /** + * Creates a new EclipseAST of the provided Compilation Unit. + * + * @param ast The compilation unit, which serves as the top level node in the tree to be built. + */ + public EclipseAST(CompilationUnitDeclaration ast) { + super(toFileName(ast)); + this.compilationUnitDeclaration = ast; + setTop(buildCompilationUnit(ast)); + this.completeParse = isComplete(ast); + } + + /** {@inheritDoc} */ + @Override public String getPackageDeclaration() { + CompilationUnitDeclaration cud = (CompilationUnitDeclaration) top().get(); + ImportReference pkg = cud.currentPackage; + return pkg == null ? null : Eclipse.toQualifiedName(pkg.getImportName()); + } + + /** {@inheritDoc} */ + @Override public Collection<String> getImportStatements() { + List<String> imports = new ArrayList<String>(); + CompilationUnitDeclaration cud = (CompilationUnitDeclaration) top().get(); + if (cud.imports == null) return imports; + for (ImportReference imp : cud.imports) { + if (imp == null) continue; + imports.add(Eclipse.toQualifiedName(imp.getImportName())); + } + + return imports; + } + + /** + * Runs through the entire AST, starting at the compilation unit, calling the provided visitor's visit methods + * for each node, depth first. + */ + public void traverse(EclipseASTVisitor visitor) { + top().traverse(visitor); + } + + void traverseChildren(EclipseASTVisitor visitor, EclipseNode node) { + for (EclipseNode child : node.down()) { + child.traverse(visitor); + } + } + + /** + * Eclipse starts off with a 'diet' parse which leaves method bodies blank, amongst other shortcuts. + * + * For such diet parses, this method returns false, otherwise it returns true. Any lombok processor + * that needs the contents of methods should just do nothing (and return false so it gets another shot later!) + * when this is false. + */ + public boolean isCompleteParse() { + return completeParse; + } + + class ParseProblem { + final boolean isWarning; + final String message; + final int sourceStart; + final int sourceEnd; + + ParseProblem(boolean isWarning, String message, int sourceStart, int sourceEnd) { + this.isWarning = isWarning; + this.message = message; + this.sourceStart = sourceStart; + this.sourceEnd = sourceEnd; + } + + void addToCompilationResult() { + addProblemToCompilationResult((CompilationUnitDeclaration) top().get(), + isWarning, message, sourceStart, sourceEnd); + } + } + + private void propagateProblems() { + if (queuedProblems.isEmpty()) return; + CompilationUnitDeclaration cud = (CompilationUnitDeclaration) top().get(); + if (cud.compilationResult == null) return; + for (ParseProblem problem : queuedProblems) problem.addToCompilationResult(); + queuedProblems.clear(); + } + + private final List<ParseProblem> queuedProblems = new ArrayList<ParseProblem>(); + + void addProblem(ParseProblem problem) { + queuedProblems.add(problem); + propagateProblems(); + } + + /** + * Adds a problem to the provided CompilationResult object so that it will show up + * in the Problems/Warnings view. + */ + static void addProblemToCompilationResult(CompilationUnitDeclaration ast, + boolean isWarning, String message, int sourceStart, int sourceEnd) { + if (ast.compilationResult == null) return; + char[] fileNameArray = ast.getFileName(); + if (fileNameArray == null) fileNameArray = "(unknown).java".toCharArray(); + int lineNumber = 0; + int columnNumber = 1; + CompilationResult result = ast.compilationResult; + int[] lineEnds = null; + lineNumber = sourceStart >= 0 + ? Util.getLineNumber(sourceStart, lineEnds = result.getLineSeparatorPositions(), 0, lineEnds.length-1) + : 0; + columnNumber = sourceStart >= 0 + ? Util.searchColumnNumber(result.getLineSeparatorPositions(), lineNumber,sourceStart) + : 0; + + CategorizedProblem ecProblem = new LombokProblem( + fileNameArray, message, 0, new String[0], + isWarning ? ProblemSeverities.Warning : ProblemSeverities.Error, + sourceStart, sourceEnd, lineNumber, columnNumber); + ast.compilationResult.record(ecProblem, null); + } + + private static class LombokProblem extends DefaultProblem { + private static final String MARKER_ID = "org.eclipse.jdt.apt.pluggable.core.compileProblem"; //$NON-NLS-1$ + + public LombokProblem(char[] originatingFileName, String message, int id, + String[] stringArguments, int severity, + int startPosition, int endPosition, int line, int column) { + super(originatingFileName, message, id, stringArguments, severity, startPosition, endPosition, line, column); + } + + @Override public int getCategoryID() { + return CAT_UNSPECIFIED; + } + + @Override public String getMarkerType() { + return MARKER_ID; + } + } + + private final CompilationUnitDeclaration compilationUnitDeclaration; + private boolean completeParse; + + private static String toFileName(CompilationUnitDeclaration ast) { + return ast.compilationResult.fileName == null ? null : new String(ast.compilationResult.fileName); + } + + /** + * Call this method to move an EclipseAST generated for a diet parse to rebuild itself for the full parse - + * with filled in method bodies and such. Also propagates problems and errors, which in diet parse + * mode can't be reliably added to the problems/warnings view. + */ + public void reparse() { + propagateProblems(); + if (completeParse) return; + boolean newCompleteParse = isComplete(compilationUnitDeclaration); + if (!newCompleteParse) return; + + top().rebuild(); + + this.completeParse = true; + } + + private static boolean isComplete(CompilationUnitDeclaration unit) { + return (unit.bits & ASTNode.HasAllMethodBodies) != 0; + } + + /** {@inheritDoc} */ + @Override protected EclipseNode buildTree(ASTNode node, Kind kind) { + switch (kind) { + case COMPILATION_UNIT: + return buildCompilationUnit((CompilationUnitDeclaration) node); + case TYPE: + return buildType((TypeDeclaration) node); + case FIELD: + return buildField((FieldDeclaration) node); + case INITIALIZER: + return buildInitializer((Initializer) node); + case METHOD: + return buildMethod((AbstractMethodDeclaration) node); + case ARGUMENT: + return buildLocal((Argument) node, kind); + case LOCAL: + return buildLocal((LocalDeclaration) node, kind); + case STATEMENT: + return buildStatement((Statement) node); + case ANNOTATION: + return buildAnnotation((Annotation) node); + default: + throw new AssertionError("Did not expect to arrive here: " + kind); + } + } + + private EclipseNode buildCompilationUnit(CompilationUnitDeclaration top) { + if (setAndGetAsHandled(top)) return null; + List<EclipseNode> children = buildTypes(top.types); + return putInMap(new EclipseNode(this, top, children, Kind.COMPILATION_UNIT)); + } + + private void addIfNotNull(Collection<EclipseNode> collection, EclipseNode n) { + if (n != null) collection.add(n); + } + + private List<EclipseNode> buildTypes(TypeDeclaration[] children) { + List<EclipseNode> childNodes = new ArrayList<EclipseNode>(); + if (children != null) for (TypeDeclaration type : children) addIfNotNull(childNodes, buildType(type)); + return childNodes; + } + + private EclipseNode buildType(TypeDeclaration type) { + if (setAndGetAsHandled(type)) return null; + List<EclipseNode> childNodes = new ArrayList<EclipseNode>(); + childNodes.addAll(buildFields(type.fields)); + childNodes.addAll(buildTypes(type.memberTypes)); + childNodes.addAll(buildMethods(type.methods)); + childNodes.addAll(buildAnnotations(type.annotations)); + return putInMap(new EclipseNode(this, type, childNodes, Kind.TYPE)); + } + + private Collection<EclipseNode> buildFields(FieldDeclaration[] children) { + List<EclipseNode> childNodes = new ArrayList<EclipseNode>(); + if (children != null) for (FieldDeclaration child : children) addIfNotNull(childNodes, buildField(child)); + return childNodes; + } + + private static <T> List<T> singleton(T item) { + List<T> list = new ArrayList<T>(); + if (item != null) list.add(item); + return list; + } + + private EclipseNode buildField(FieldDeclaration field) { + if (field instanceof Initializer) return buildInitializer((Initializer)field); + if (setAndGetAsHandled(field)) return null; + List<EclipseNode> childNodes = new ArrayList<EclipseNode>(); + addIfNotNull(childNodes, buildStatement(field.initialization)); + childNodes.addAll(buildAnnotations(field.annotations)); + return putInMap(new EclipseNode(this, field, childNodes, Kind.FIELD)); + } + + private EclipseNode buildInitializer(Initializer initializer) { + if (setAndGetAsHandled(initializer)) return null; + return putInMap(new EclipseNode(this, initializer, singleton(buildStatement(initializer.block)), Kind.INITIALIZER)); + } + + private Collection<EclipseNode> buildMethods(AbstractMethodDeclaration[] children) { + List<EclipseNode> childNodes = new ArrayList<EclipseNode>(); + if (children != null) for (AbstractMethodDeclaration method : children) addIfNotNull(childNodes, buildMethod(method)); + return childNodes; + } + + private EclipseNode buildMethod(AbstractMethodDeclaration method) { + if (setAndGetAsHandled(method)) return null; + List<Ec |
