lombok.javac.JavacAST Maven / Gradle / Ivy
The newest version!
/*
* Copyright (C) 2009-2020 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.javac;
import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.annotation.processing.Messager;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
import com.sun.tools.javac.util.JCDiagnostic;
import lombok.core.AST;
import lombok.core.CleanupRegistry;
import lombok.core.CleanupTask;
import lombok.permit.Permit;
import com.sun.tools.javac.code.Source;
import com.sun.tools.javac.code.Symtab;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.model.JavacTypes;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.JCTree.JCAnnotation;
import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
import com.sun.tools.javac.tree.JCTree.JCBlock;
import com.sun.tools.javac.tree.JCTree.JCCatch;
import com.sun.tools.javac.tree.JCTree.JCClassDecl;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCExpression;
import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
import com.sun.tools.javac.tree.JCTree.JCIdent;
import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
import com.sun.tools.javac.tree.JCTree.JCStatement;
import com.sun.tools.javac.tree.JCTree.JCTry;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.tree.JCTree.JCWildcard;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.Log;
import com.sun.tools.javac.util.Name;
/**
* Wraps around javac's internal AST view to add useful features as well as the ability to visit parents from children,
* something javac's own AST system does not offer.
*/
public class JavacAST extends AST {
private final CleanupRegistry cleanup;
private final JavacElements elements;
private final JavacTreeMaker treeMaker;
private final Symtab symtab;
private final JavacTypes javacTypes;
private final Log log;
private final ErrorLog errorLogger;
private final Context context;
private static final URI NOT_CALCULATED_MARKER = URI.create("https://projectlombok.org/not/calculated");
private URI memoizedAbsoluteFileLocation = NOT_CALCULATED_MARKER;
/**
* Creates a new JavacAST of the provided Compilation Unit.
*
* @param messager A Messager for warning and error reporting.
* @param context A Context object for interfacing with the compiler.
* @param top The compilation unit, which serves as the top level node in the tree to be built.
*/
public JavacAST(Messager messager, Context context, JCCompilationUnit top, CleanupRegistry cleanup) {
super(sourceName(top), PackageName.getPackageName(top), new JavacImportList(top), statementTypes());
setTop(buildCompilationUnit(top));
this.context = context;
this.log = Log.instance(context);
this.errorLogger = ErrorLog.create(messager, log);
this.elements = JavacElements.instance(context);
this.treeMaker = new JavacTreeMaker(TreeMaker.instance(context));
this.symtab = Symtab.instance(context);
this.javacTypes = JavacTypes.instance(context);
this.cleanup = cleanup;
clearChanged();
}
@Override public URI getAbsoluteFileLocation() {
if (memoizedAbsoluteFileLocation == NOT_CALCULATED_MARKER) {
memoizedAbsoluteFileLocation = getAbsoluteFileLocation((JCCompilationUnit) top().get());
}
return memoizedAbsoluteFileLocation;
}
private static Class> wrappedFileObjectClass, sbtJavaFileObjectClass, sbtMappedVirtualFileClass, sbtOptionClass;
private static Field wrappedFileObjectField, sbtJavaFileObjectField, sbtMappedVirtualFilePathField, sbtMappedVirtualFileRootsField, sbtOptionField;
private static Method sbtMapGetMethod;
public static URI getAbsoluteFileLocation(JCCompilationUnit cu) {
try {
URI uri = cu.sourcefile.toUri();
String fn = uri.toString();
if (fn.startsWith("file:")) return uri;
URI sbtUri = tryGetSbtFile(cu.sourcefile);
if (sbtUri != null) return sbtUri;
return uri;
} catch (Exception e) {
return null;
}
}
private static URI tryGetSbtFile(JavaFileObject sourcefile) {
try {
return tryGetSbtFile_(sourcefile);
} catch (Exception e) {
return null;
}
}
private static URI tryGetSbtFile_(JavaFileObject sourcefile) throws Exception {
Class> c = sourcefile.getClass();
String cn;
if (wrappedFileObjectClass == null) {
if (!c.getName().equals("com.sun.tools.javac.api.ClientCodeWrapper$WrappedJavaFileObject")) return null;
wrappedFileObjectClass = c;
}
if (c != wrappedFileObjectClass) return null;
if (wrappedFileObjectField == null) wrappedFileObjectField = Permit.permissiveGetField(wrappedFileObjectClass.getSuperclass(), "clientFileObject");
if (wrappedFileObjectField == null) return null;
Object fileObject = wrappedFileObjectField.get(sourcefile);
c = fileObject.getClass();
if (sbtJavaFileObjectClass == null) {
cn = c.getName();
if (!cn.startsWith("sbt.") || !cn.endsWith("JavaFileObject")) return null;
sbtJavaFileObjectClass = c;
}
if (sbtJavaFileObjectClass != c) return null;
if (sbtJavaFileObjectField == null) sbtJavaFileObjectField = Permit.permissiveGetField(sbtJavaFileObjectClass, "underlying");
if (sbtJavaFileObjectField == null) return null;
Object mappedVirtualFile = sbtJavaFileObjectField.get(fileObject);
c = mappedVirtualFile.getClass();
if (sbtMappedVirtualFileClass == null) {
cn = c.getName();
if (!cn.startsWith("sbt.") || !cn.endsWith("MappedVirtualFile")) return null;
sbtMappedVirtualFileClass = c;
}
if (sbtMappedVirtualFilePathField == null) sbtMappedVirtualFilePathField = Permit.permissiveGetField(sbtMappedVirtualFileClass, "encodedPath");
if (sbtMappedVirtualFilePathField == null) return null;
if (sbtMappedVirtualFileRootsField == null) sbtMappedVirtualFileRootsField = Permit.permissiveGetField(sbtMappedVirtualFileClass, "rootPathsMap");
if (sbtMappedVirtualFileRootsField == null) return null;
String encodedPath = (String) sbtMappedVirtualFilePathField.get(mappedVirtualFile);
if (!encodedPath.startsWith("${")) {
File maybeAbsoluteFile = new File(encodedPath);
if (maybeAbsoluteFile.exists()) {
return maybeAbsoluteFile.toURI();
} else {
return null;
}
}
int idx = encodedPath.indexOf('}');
if (idx == -1) return null;
String base = encodedPath.substring(2, idx);
Object roots = sbtMappedVirtualFileRootsField.get(mappedVirtualFile);
if (sbtMapGetMethod == null) sbtMapGetMethod = Permit.getMethod(roots.getClass(), "get", Object.class);
if (sbtMapGetMethod == null) return null;
Object option = sbtMapGetMethod.invoke(roots, base);
c = option.getClass();
if (sbtOptionClass == null) {
if (c.getName().equals("scala.Some")) sbtOptionClass = c;
}
if (c != sbtOptionClass) return null;
if (sbtOptionField == null) sbtOptionField = Permit.permissiveGetField(sbtOptionClass, "value");
if (sbtOptionField == null) return null;
Object path = sbtOptionField.get(option);
return new File(path.toString() + encodedPath.substring(idx + 1)).toURI();
}
private static String sourceName(JCCompilationUnit cu) {
return cu.sourcefile == null ? null : cu.sourcefile.toString();
}
public Context getContext() {
return context;
}
/**
* 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(JavacASTVisitor visitor) {
top().traverse(visitor);
}
void traverseChildren(JavacASTVisitor visitor, JavacNode node) {
for (JavacNode child : node.down()) child.traverse(visitor);
}
@Override public int getSourceVersion() {
try {
String nm = Source.instance(context).name();
int underscoreIdx = nm.indexOf('_');
if (underscoreIdx > -1) return Integer.parseInt(nm.substring(underscoreIdx + 1));
// assume java9+
return Integer.parseInt(nm.substring(3));
} catch (Exception ignore) {}
return 6;
}
@Override public int getLatestJavaSpecSupported() {
return Javac.getJavaCompilerVersion();
}
public void cleanupTask(String key, JCTree target, CleanupTask task) {
cleanup.registerTask(key, target, task);
}
/** @return A Name object generated for the proper name table belonging to this AST. */
public Name toName(String name) {
return elements.getName(name);
}
/** @return A TreeMaker instance that you can use to create new AST nodes. */
public JavacTreeMaker getTreeMaker() {
treeMaker.at(-1);
return treeMaker;
}
/** @return The symbol table used by this AST for symbols. */
public Symtab getSymbolTable() {
return symtab;
}
/**
* @return The implementation of {@link javax.lang.model.util.Types} of javac. Contains a few extra methods beyond
* the ones listed in the official annotation API interface. */
public JavacTypes getTypesUtil() {
return javacTypes;
}
/** {@inheritDoc} */
@Override protected JavacNode buildTree(JCTree node, Kind kind) {
switch (kind) {
case COMPILATION_UNIT:
return buildCompilationUnit((JCCompilationUnit) node);
case TYPE:
return buildType((JCClassDecl) node);
case FIELD:
return buildField((JCVariableDecl) node);
case INITIALIZER:
return buildInitializer((JCBlock) node);
case METHOD:
return buildMethod((JCMethodDecl) node);
case ARGUMENT:
return buildLocalVar((JCVariableDecl) node, kind);
case LOCAL:
return buildLocalVar((JCVariableDecl) node, kind);
case STATEMENT:
return buildStatementOrExpression(node);
case ANNOTATION:
return buildAnnotation((JCAnnotation) node, false);
case TYPE_USE:
return buildTypeUse(node);
default:
throw new AssertionError("Did not expect: " + kind);
}
}
private JavacNode buildCompilationUnit(JCCompilationUnit top) {
List childNodes = new ArrayList();
for (JCTree s : top.defs) {
if (s instanceof JCClassDecl) {
addIfNotNull(childNodes, buildType((JCClassDecl) s));
} // else they are import statements, which we don't care about. Or Skip objects, whatever those are.
}
return new JavacNode(this, top, childNodes, Kind.COMPILATION_UNIT);
}
private JavacNode buildType(JCClassDecl type) {
if (setAndGetAsHandled(type)) return null;
List childNodes = new ArrayList();
for (JCAnnotation annotation : type.mods.annotations) addIfNotNull(childNodes, buildAnnotation(annotation, false));
for (JCTree def : type.defs) {
/* A def can be:
* JCClassDecl for inner types
* JCMethodDecl for constructors and methods
* JCVariableDecl for fields
* JCBlock for (static) initializers
*/
if (def instanceof JCMethodDecl) addIfNotNull(childNodes, buildMethod((JCMethodDecl) def));
else if (def instanceof JCClassDecl) addIfNotNull(childNodes, buildType((JCClassDecl) def));
else if (def instanceof JCVariableDecl) addIfNotNull(childNodes, buildField((JCVariableDecl) def));
else if (def instanceof JCBlock) addIfNotNull(childNodes, buildInitializer((JCBlock) def));
}
return putInMap(new JavacNode(this, type, childNodes, Kind.TYPE));
}
private JavacNode buildField(JCVariableDecl field) {
if (setAndGetAsHandled(field)) return null;
List childNodes = new ArrayList();
for (JCAnnotation annotation : field.mods.annotations) addIfNotNull(childNodes, buildAnnotation(annotation, true));
addIfNotNull(childNodes, buildTypeUse(field.vartype));
addIfNotNull(childNodes, buildExpression(field.init));
return putInMap(new JavacNode(this, field, childNodes, Kind.FIELD));
}
private JavacNode buildLocalVar(JCVariableDecl local, Kind kind) {
if (setAndGetAsHandled(local)) return null;
List childNodes = new ArrayList();
for (JCAnnotation annotation : local.mods.annotations) addIfNotNull(childNodes, buildAnnotation(annotation, true));
addIfNotNull(childNodes, buildTypeUse(local.vartype));
addIfNotNull(childNodes, buildExpression(local.init));
return putInMap(new JavacNode(this, local, childNodes, kind));
}
private JavacNode buildTypeUse(JCTree typeUse) {
if (setAndGetAsHandled(typeUse)) return null;
if (typeUse == null) return null;
if (typeUse.getClass().getName().equals("com.sun.tools.javac.tree.JCTree$JCAnnotatedType")) {
initJcAnnotatedType(typeUse.getClass());
Collection> anns = Permit.permissiveReadField(Collection.class, JCANNOTATEDTYPE_ANNOTATIONS, typeUse);
JCExpression underlying = Permit.permissiveReadField(JCExpression.class, JCANNOTATEDTYPE_UNDERLYINGTYPE, typeUse);
List childNodes = new ArrayList();
if (anns != null) for (Object annotation : anns) if (annotation instanceof JCAnnotation) addIfNotNull(childNodes, buildAnnotation((JCAnnotation) annotation, true));
addIfNotNull(childNodes, buildTypeUse(underlying));
return putInMap(new JavacNode(this, typeUse, childNodes, Kind.TYPE_USE));
}
if (typeUse instanceof JCWildcard) {
JCTree inner = ((JCWildcard) typeUse).inner;
List childNodes = inner == null ? Collections.emptyList() : new ArrayList();
if (inner != null) addIfNotNull(childNodes, buildTypeUse(inner));
return putInMap(new JavacNode(this, typeUse, childNodes, Kind.TYPE_USE));
}
if (typeUse instanceof JCArrayTypeTree) {
JCTree inner = ((JCArrayTypeTree) typeUse).elemtype;
List childNodes = inner == null ? Collections.emptyList() : new ArrayList();
if (inner != null) addIfNotNull(childNodes, buildTypeUse(inner));
return putInMap(new JavacNode(this, typeUse, childNodes, Kind.TYPE_USE));
}
if (typeUse instanceof JCFieldAccess) {
JCTree inner = ((JCFieldAccess) typeUse).selected;
List childNodes = inner == null ? Collections.emptyList() : new ArrayList();
if (inner != null) addIfNotNull(childNodes, buildTypeUse(inner));
return putInMap(new JavacNode(this, typeUse, childNodes, Kind.TYPE_USE));
}
if (typeUse instanceof JCIdent) {
return putInMap(new JavacNode(this, typeUse, Collections.emptyList(), Kind.TYPE_USE));
}
return null;
}
private static boolean JCTRY_RESOURCES_FIELD_INITIALIZED = false;
private static Field JCTRY_RESOURCES_FIELD;
@SuppressWarnings("unchecked")
private static List getResourcesForTryNode(JCTry tryNode) {
if (!JCTRY_RESOURCES_FIELD_INITIALIZED) {
JCTRY_RESOURCES_FIELD = Permit.permissiveGetField(JCTry.class, "resources");
JCTRY_RESOURCES_FIELD_INITIALIZED = true;
}
if (JCTRY_RESOURCES_FIELD == null) return Collections.emptyList();
Object rv = null;
try {
rv = JCTRY_RESOURCES_FIELD.get(tryNode);
} catch (Exception ignore) {}
if (rv instanceof List) return (List) rv;
return Collections.emptyList();
}
private static boolean JCANNOTATEDTYPE_FIELDS_INITIALIZED = false;
private static Field JCANNOTATEDTYPE_ANNOTATIONS, JCANNOTATEDTYPE_UNDERLYINGTYPE;
private static void initJcAnnotatedType(Class> context) {
if (JCANNOTATEDTYPE_FIELDS_INITIALIZED) return;
JCANNOTATEDTYPE_ANNOTATIONS = Permit.permissiveGetField(context, "annotations");
JCANNOTATEDTYPE_UNDERLYINGTYPE = Permit.permissiveGetField(context, "underlyingType");
JCANNOTATEDTYPE_FIELDS_INITIALIZED = true;
}
private JavacNode buildTry(JCTry tryNode) {
if (setAndGetAsHandled(tryNode)) return null;
List childNodes = new ArrayList();
for (JCTree varDecl : getResourcesForTryNode(tryNode)) {
if (varDecl instanceof JCVariableDecl) {
addIfNotNull(childNodes, buildLocalVar((JCVariableDecl) varDecl, Kind.LOCAL));
}
}
addIfNotNull(childNodes, buildStatement(tryNode.body));
for (JCCatch jcc : tryNode.catchers) addIfNotNull(childNodes, buildTree(jcc, Kind.STATEMENT));
addIfNotNull(childNodes, buildStatement(tryNode.finalizer));
return putInMap(new JavacNode(this, tryNode, childNodes, Kind.STATEMENT));
}
private JavacNode buildInitializer(JCBlock initializer) {
if (setAndGetAsHandled(initializer)) return null;
List childNodes = new ArrayList();
for (JCStatement statement: initializer.stats) addIfNotNull(childNodes, buildStatement(statement));
return putInMap(new JavacNode(this, initializer, childNodes, Kind.INITIALIZER));
}
private JavacNode buildMethod(JCMethodDecl method) {
if (setAndGetAsHandled(method)) return null;
List childNodes = new ArrayList();
for (JCAnnotation annotation : method.mods.annotations) addIfNotNull(childNodes, buildAnnotation(annotation, false));
for (JCVariableDecl param : method.params) addIfNotNull(childNodes, buildLocalVar(param, Kind.ARGUMENT));
if (method.body != null && method.body.stats != null) {
for (JCStatement statement : method.body.stats) addIfNotNull(childNodes, buildStatement(statement));
}
return putInMap(new JavacNode(this, method, childNodes, Kind.METHOD));
}
private JavacNode buildAnnotation(JCAnnotation annotation, boolean varDecl) {
boolean handled = setAndGetAsHandled(annotation);
if (!varDecl && handled) {
// @Foo int x, y; is handled in javac by putting the same annotation node on 2 JCVariableDecls.
return null;
}
return putInMap(new JavacNode(this, annotation, null, Kind.ANNOTATION));
}
private JavacNode buildExpression(JCExpression expression) {
return buildStatementOrExpression(expression);
}
private JavacNode buildStatement(JCStatement statement) {
return buildStatementOrExpression(statement);
}
private JavacNode buildStatementOrExpression(JCTree statement) {
if (statement == null) return null;
if (statement instanceof JCAnnotation) return null;
if (statement instanceof JCClassDecl) return buildType((JCClassDecl) statement);
if (statement instanceof JCVariableDecl) return buildLocalVar((JCVariableDecl) statement, Kind.LOCAL);
if (statement instanceof JCTry) return buildTry((JCTry) statement);
if (statement.getClass().getName().equals("com.sun.tools.javac.tree.JCTree$JCLambda")) return buildLambda(statement);
if (setAndGetAsHandled(statement)) return null;
return drill(statement);
}
private JavacNode buildLambda(JCTree jcTree) {
return buildStatementOrExpression(getBody(jcTree));
}
private JCTree getBody(JCTree jcTree) {
return (JCTree) Permit.invokeSneaky(getBodyMethod(jcTree.getClass()), jcTree);
}
private final static ConcurrentMap, Method> getBodyMethods = new ConcurrentHashMap, Method>();
private Method getBodyMethod(Class> c) {
Method m = getBodyMethods.get(c);
if (m != null) {
return m;
}
try {
m = Permit.getMethod(c, "getBody");
} catch (NoSuchMethodException e) {
throw Javac.sneakyThrow(e);
}
getBodyMethods.putIfAbsent(c, m);
return getBodyMethods.get(c);
}
private JavacNode drill(JCTree statement) {
try {
List childNodes = new ArrayList();
for (FieldAccess fa : fieldsOf(statement.getClass())) childNodes.addAll(buildWithField(JavacNode.class, statement, fa));
return putInMap(new JavacNode(this, statement, childNodes, Kind.STATEMENT));
} catch (OutOfMemoryError oome) {
String msg = oome.getMessage();
if (msg == null) msg = "(no original message)";
OutOfMemoryError newError = new OutOfMemoryError(getFileName() + "@pos" + statement.getPreferredPosition() + ": " + msg);
// We could try to set the stack trace of the new exception to the same one as the old exception, but this costs memory,
// and we're already in an extremely fragile situation in regards to remaining heap space, so let's not do that.
throw newError;
}
}
/* For javac, both JCExpression and JCStatement are considered as valid children types. */
private static Collection> statementTypes() {
Collection> collection = new ArrayList>(3);
collection.add(JCStatement.class);
collection.add(JCExpression.class);
collection.add(JCCatch.class);
return collection;
}
private static void addIfNotNull(Collection nodes, JavacNode node) {
if (node != null) nodes.add(node);
}
/**
* Attempts to remove any compiler errors generated by java whose reporting position is located anywhere between the start and end of the supplied node.
*/
void removeDeferredErrors(JavacNode node) {
DiagnosticPosition pos = node.get().pos();
JCCompilationUnit top = (JCCompilationUnit) top().get();
removeFromDeferredDiagnostics(pos.getStartPosition(), Javac.getEndPosition(pos, top));
}
/** Supply either a position or a node (in that case, position of the node is used) */
void printMessage(Diagnostic.Kind kind, String message, JavacNode node, DiagnosticPosition pos, boolean attemptToRemoveErrorsInRange) {
JavaFileObject oldSource = null;
JavaFileObject newSource = null;
JCTree astObject = node == null ? null : node.get();
JCCompilationUnit top = (JCCompilationUnit) top().get();
newSource = top.sourcefile;
if (newSource != null) {
oldSource = log.useSource(newSource);
if (pos == null) pos = astObject.pos();
}
if (pos != null && node != null && attemptToRemoveErrorsInRange) {
removeFromDeferredDiagnostics(pos.getStartPosition(), node.getEndPosition(pos));
}
try {
switch (kind) {
case ERROR:
errorLogger.error(pos, message);
break;
case MANDATORY_WARNING:
errorLogger.mandatoryWarning(pos, message);
break;
case WARNING:
errorLogger.warning(pos, message);
break;
default:
case NOTE:
errorLogger.note(pos, message);
break;
}
} finally {
if (newSource != null) log.useSource(oldSource);
}
}
public void removeFromDeferredDiagnostics(int startPos, int endPos) {
JCCompilationUnit self = (JCCompilationUnit) top().get();
new CompilerMessageSuppressor(getContext()).removeAllBetween(self.sourcefile, startPos, endPos);
}
/** {@inheritDoc} */
@Override protected void setElementInASTCollection(Field field, Object refField, List> chain, Collection> collection, int idx, JCTree newN) throws IllegalAccessException {
com.sun.tools.javac.util.List> list = setElementInConsList(chain, collection, ((List>)collection).get(idx), newN);
field.set(refField, list);
}
private com.sun.tools.javac.util.List> setElementInConsList(List> chain, Collection> current, Object oldO, Object newO) {
com.sun.tools.javac.util.List> oldL = (com.sun.tools.javac.util.List>) current;
com.sun.tools.javac.util.List> newL = replaceInConsList(oldL, oldO, newO);
if (chain.isEmpty()) return newL;
List> reducedChain = new ArrayList>(chain);
Collection> newCurrent = reducedChain.remove(reducedChain.size() -1);
return setElementInConsList(reducedChain, newCurrent, oldL, newL);
}
private com.sun.tools.javac.util.List> replaceInConsList(com.sun.tools.javac.util.List> oldL, Object oldO, Object newO) {
boolean repl = false;
Object[] a = oldL.toArray();
for (int i = 0; i < a.length; i++) {
if (a[i] == oldO) {
a[i] = newO;
repl = true;
}
}
if (repl) return com.sun.tools.javac.util.List.
© 2015 - 2025 Weber Informatics LLC | Privacy Policy