/** * Copyright (c) 2006-2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM - Initial API and implementation */ package org.eclipse.emf.codegen.merge.java.facade.ast; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration; import org.eclipse.jdt.core.dom.ArrayType; import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.EnumConstantDeclaration; import org.eclipse.jdt.core.dom.EnumDeclaration; import org.eclipse.jdt.core.dom.FieldDeclaration; import org.eclipse.jdt.core.dom.ImportDeclaration; import org.eclipse.jdt.core.dom.Initializer; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.Name; import org.eclipse.jdt.core.dom.PackageDeclaration; import org.eclipse.jdt.core.dom.ParameterizedType; import org.eclipse.jdt.core.dom.PrimitiveType; import org.eclipse.jdt.core.dom.QualifiedType; import org.eclipse.jdt.core.dom.SimpleType; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.core.dom.rewrite.ListRewrite; import org.eclipse.jdt.core.dom.rewrite.TargetSourceRangeComputer; import org.eclipse.jdt.core.dom.rewrite.TargetSourceRangeComputer.SourceRange; import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; import org.eclipse.emf.codegen.CodeGenPlugin; import org.eclipse.emf.codegen.merge.java.JControlModel; import org.eclipse.emf.codegen.merge.java.JMerger; import org.eclipse.emf.codegen.merge.java.facade.FacadeHelper; import org.eclipse.emf.codegen.merge.java.facade.JCompilationUnit; import org.eclipse.emf.codegen.merge.java.facade.JNode; import org.eclipse.emf.codegen.util.CodeGenUtil; import org.eclipse.emf.common.util.BasicDiagnostic; import org.eclipse.emf.common.util.Diagnostic; import org.eclipse.emf.common.util.DiagnosticException; import org.eclipse.emf.common.util.WrappedException; /** * @since 2.2.0 */ public class ASTFacadeHelper extends FacadeHelper { /** * @deprecated Just use ASTRewrite directly. */ @Deprecated public static class ASTRewriteWithRemove extends ASTRewrite { protected ASTRewriteWithRemove(AST ast) { super(ast); } /** * Disposes this ASTRewriteWithRemove */ @SuppressWarnings("restriction") public void dispose() { getRewriteEventStore().clear(); getNodeStore().clear(); setTargetSourceRangeComputer(null); } /** * Workaround method that removes nodes similar to {@link #remove(ASTNode, org.eclipse.text.edits.TextEditGroup)}, but * it allows removal of newly created and inserted nodes that were not a part of original tree. * <p> * Note that {@link #remove(ASTNode, org.eclipse.text.edits.TextEditGroup)} does not remove newly created nodes that * have been inserted with {@link ListRewrite#insertFirst(ASTNode, org.eclipse.text.edits.TextEditGroup)} or * similar methods. * <p> * Workaround for <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=164862">https://bugs.eclipse.org/bugs/show_bug.cgi?id=164862</a> * * @param parent * @param childProperty * @param node */ @SuppressWarnings("restriction") public void remove(ASTNode parent, ChildListPropertyDescriptor childProperty, ASTNode node) { ListRewrite lrw = getListRewrite(parent, childProperty); if (lrw.getRewrittenList().contains(node) && !lrw.getOriginalList().contains(node)) { org.eclipse.jdt.internal.core.dom.rewrite.ListRewriteEvent listEvent = super.getRewriteEventStore().getListEvent(parent, childProperty, true); int index = listEvent.getIndex(node, org.eclipse.jdt.internal.core.dom.rewrite.ListRewriteEvent.NEW); if (index >= 0) { listEvent.revertChange((org.eclipse.jdt.internal.core.dom.rewrite.NodeRewriteEvent)listEvent.getChildren()[index]); } else { lrw.remove(node, null); } } else { lrw.remove(node, null); } } } /** * Debug output setting */ protected final static boolean DEBUG = JMerger.DEBUG; /** * Converts {@link Name} to string representation. * * @param name * @return fully qualified name or <code>null</code> if name parameter is <code>null</code> */ public static String toString(Name name) { return name == null ? null : name.getFullyQualifiedName(); } public static String getTypeErasure(ArrayType arrayType) { StringBuilder sb = new StringBuilder(ASTFacadeHelper.getTypeErasure(arrayType.getElementType())); for (int i = arrayType.getDimensions(); i > 0; i--) { sb.append("[]"); } return sb.toString(); } public static String getTypeErasure(ParameterizedType parameterizedType) { return getTypeErasure(parameterizedType.getType()); } public static String getTypeErasure(PrimitiveType primitiveType) { return primitiveType.getPrimitiveTypeCode().toString(); } public static String getTypeErasure(SimpleType simpleType) { return ASTFacadeHelper.toString(simpleType.getName()); } public static String getTypeErasure(QualifiedType qualifiedType) { StringBuilder sb = new StringBuilder(ASTFacadeHelper.getTypeErasure(qualifiedType.getQualifier())); sb.append("."); sb.append(ASTFacadeHelper.toString(qualifiedType.getName())); return sb.toString(); } /** * Converts {@link Type} to string representation, erasing type parameters information. * <p> * This method is used to create a method signature, and match methods by signature. * * @param type * @return string representing the type */ public static String getTypeErasure(Type type) { if (type != null) { if (type.isArrayType()) { return getTypeErasure((ArrayType)type); } else if (type.isParameterizedType()) { return getTypeErasure((ParameterizedType)type); } else if (type.isPrimitiveType()) { return getTypeErasure((PrimitiveType)type); } else if (type.isQualifiedType()) { return getTypeErasure((QualifiedType)type); } else if (type.isSimpleType()) { return getTypeErasure((SimpleType)type); } else if (type.isWildcardType()) { return ""; } } return ""; } protected ASTNodeConverter nodeConverter = null; /** * Map of options set by default from <code>JavaCore.getOptions()</code> */ protected Map<?, ?> javaCoreOptions = null; /** * Map of nodes to node contents. Used for caching only. */ protected Map<ASTNode, String> nodeContents = new HashMap<ASTNode, String>(); @Override public void reset() { nodeConverter = null; nodeContents.clear(); super.reset(); } /** * Creates and returns <code>ASTParser</code>. * * @return new ASTParser object */ protected ASTParser createASTParser() { // caching parser does not parse 2nd file in the same way (javadoc of package for example) // hence, new parser is created every time this method is called ASTParser astParser = CodeGenUtil.EclipseUtil.newASTParser(); Map<String, String> javaCoreOptions = new HashMap<String, String>(); for (Map.Entry<?, ?> entry : getJavaCoreOptions().entrySet()) { Object key = entry.getKey(); Object value = entry.getValue(); if (key instanceof String && value instanceof String) { javaCoreOptions.put((String)key, (String)value); } } astParser.setCompilerOptions(javaCoreOptions); return astParser; } /* (non-Javadoc) * @see org.eclipse.emf.codegen.merge.java.facade.FacadeHelper#createCompilationUnit(java.lang.String, java.lang.String) */ @Override public ASTJCompilationUnit createCompilationUnit(String name, String contents) { // set source char[] contentAsCharArray = contents.toCharArray(); ASTParser astParser = createASTParser(); astParser.setSource(contentAsCharArray); // parse CompilationUnit astCompilationUnit = (CompilationUnit)astParser.createAST(null); Diagnostic diagnostic = analyzeCompilationUnit(astCompilationUnit, contents); if (diagnostic != Diagnostic.OK_INSTANCE) { StringBuilder message = new StringBuilder(diagnostic.getMessage()); for (Diagnostic childDiagnostic : diagnostic.getChildren()) { message.append("\n\t").append(childDiagnostic.getMessage()); } message.append(contents); CodeGenPlugin.INSTANCE.log(message.toString()); if (diagnostic.getSeverity() == Diagnostic.ERROR) { throw new WrappedException(new DiagnosticException(diagnostic)); } } // create rewriter to record changes ASTRewrite rewriter = ASTRewrite.create(astCompilationUnit.getAST()); // keep comments between nodes when removing or moving nodes rewriter.setTargetSourceRangeComputer(new CommentAwareSourceRangeComputer(astCompilationUnit, contents)); // set properties astCompilationUnit.setProperty(ASTJCompilationUnit.NAME_PROPERTY, name); // create JNode and set properties ASTJCompilationUnit compilationUnit = (ASTJCompilationUnit)convertToNode(astCompilationUnit); compilationUnit.setOriginalContents(contentAsCharArray); compilationUnit.setRewriter(rewriter); return compilationUnit; } @Override public String getOriginalContents(JCompilationUnit compilationUnit) { return new String(((ASTJCompilationUnit)compilationUnit).getOriginalContents()); } private Diagnostic analyzeCompilationUnit(CompilationUnit compilationUnit, String contents) { if (compilationUnit.getProblems().length == 0) { return Diagnostic.OK_INSTANCE; } else { BasicDiagnostic diagnostic = new BasicDiagnostic( CodeGenPlugin.ID, 0, CodeGenPlugin.INSTANCE.getString("_UI_ParsingProblem_message"), contents == null ? null : new Object[] {new StringBuilder(contents)} ); for (IProblem problem : compilationUnit.getProblems()) { String message = problem.getMessage() != null ? CodeGenPlugin.INSTANCE.getString("_UI_LineNumberAndText_message", new Object[] {problem.getSourceLineNumber(), problem.getMessage()}) : CodeGenPlugin.INSTANCE.getString("_UI_LineNumber_message", new Object[] {problem.getSourceLineNumber()}); BasicDiagnostic childDiagnostic = new BasicDiagnostic( problem.isWarning() ? Diagnostic.WARNING : Diagnostic.ERROR, CodeGenPlugin.ID, 0, message.toString(), contents == null ? new Object[]{problem} : new Object[]{problem, new StringBuilder(contents)} ); diagnostic.add(childDiagnostic); } return diagnostic; } } /** * Accessor for options to be used during parsing and rewriting. * <p> * If options are <code>null</code>, uses options provided by * <code>getDefaultJavaCoreOptions()</code>. * * @return map of options * @see #getDefaultJavaCoreOptions() */ public Map<?, ?> getJavaCoreOptions() { if (javaCoreOptions == null) { javaCoreOptions = getDefaultJavaCoreOptions(); } return javaCoreOptions; } /** * Gets JavaCore options from JavaCore and updates tab and indentation settings from ControlModel. * * @return map of options * * @see #getJavaCoreOptions() * @see JavaCore#getOptions() * @see JControlModel#getLeadingTabReplacement() */ private Map<?, ?> getDefaultJavaCoreOptions() { Map<Object, String> javaCoreOptions = new HashMap<Object, String>(JavaCore.getOptions()); // Set of options that we want to copy from the current definition or use defaults // if (compilerCompliance != null) { javaCoreOptions.put(JavaCore.COMPILER_COMPLIANCE, compilerCompliance); javaCoreOptions.put(JavaCore.COMPILER_SOURCE, compilerCompliance); javaCoreOptions.put(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, compilerCompliance); } else { useCurrentOption(javaCoreOptions, JavaCore.COMPILER_COMPLIANCE, "1.5"); useCurrentOption(javaCoreOptions, JavaCore.COMPILER_SOURCE, "1.5"); useCurrentOption(javaCoreOptions, JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, "1.5"); } useCurrentOption(javaCoreOptions, DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, JavaCore.SPACE); useCurrentOption(javaCoreOptions, DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, "2"); useCurrentOption(javaCoreOptions, DefaultCodeFormatterConstants.FORMATTER_INDENTATION_SIZE, "2"); javaCoreOptions.put(JavaCore.COMPILER_DOC_COMMENT_SUPPORT, "enabled"); if (getControlModel() != null) { String indent = getControlModel().getLeadingTabReplacement(); if (indent != null && indent.length() > 0) { String size = Integer.toString(indent.length()); if (indent.charAt(0) == '\t') { javaCoreOptions.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, JavaCore.TAB); javaCoreOptions.put(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, size); javaCoreOptions.put(DefaultCodeFormatterConstants.FORMATTER_INDENTATION_SIZE, size); } else if (indent.charAt(0) == ' ') { javaCoreOptions.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, JavaCore.SPACE); javaCoreOptions.put(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, size); javaCoreOptions.put(DefaultCodeFormatterConstants.FORMATTER_INDENTATION_SIZE, size); } else { if (DEBUG) { logInfo("Unable to recognize indent string, using java core options."); } } } else { if (DEBUG) { logInfo("Indent is not set, using java core options."); } } } if (DEBUG) { logInfo("Tab char: " + javaCoreOptions.get(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR) + ", Indent size: " + javaCoreOptions.get(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE) + ", Tab size: " + javaCoreOptions.get(DefaultCodeFormatterConstants.FORMATTER_INDENTATION_SIZE)); } // Set of options that we want to control javaCoreOptions.put("org.eclipse.jdt.core.incompleteClasspath", "warning"); javaCoreOptions.put("org.eclipse.jdt.core.circularClasspath", "warning"); // javaCoreOptions.put(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_ENUM_DECLARATION, DefaultCodeFormatterConstants.NEXT_LINE); javaCoreOptions.put(DefaultCodeFormatterConstants.FORMATTER_BRACE_POSITION_FOR_TYPE_DECLARATION, DefaultCodeFormatterConstants.NEXT_LINE); // separate fields with an empty line javaCoreOptions.put(DefaultCodeFormatterConstants.FORMATTER_BLANK_LINES_BEFORE_FIELD, "1"); // make all enum constants to be on separate lines javaCoreOptions.put(DefaultCodeFormatterConstants.FORMATTER_ALIGNMENT_FOR_ENUM_CONSTANTS, DefaultCodeFormatterConstants.createAlignmentValue(true, DefaultCodeFormatterConstants.WRAP_ONE_PER_LINE, DefaultCodeFormatterConstants.INDENT_DEFAULT)); return javaCoreOptions; } protected void useCurrentOption(Map<Object, String> options, String option) { useCurrentOption(options, option, null); } /** * @since 2.8 * @param options * @param option * @param defaultValue */ protected void useCurrentOption(Map<Object, String> options, String option, String defaultValue) { String value = JavaCore.getOption(option); if (value != null) { options.put(option, value); } else if (!options.containsKey(option) && defaultValue != null) { options.put(option, defaultValue); } } @Override public ASTNodeConverter getNodeConverter() { if (nodeConverter == null) { nodeConverter = new ASTNodeConverter(this); } return nodeConverter; } /* (non-Javadoc) * @see org.eclipse.emf.codegen.merge.java.facade.FacadeHelper#getContext(org.eclipse.emf.codegen.merge.java.facade.JNode) */ @Override public Object getContext(JNode node) { return node; } /** * Creates a copy of the node to be inserted in the tree that context node belongs to. * <p> * Note that in this implementation the original and returned cloned node can not be modified. * Calls to <code>get...()</code> methods on the cloned node will not return the original content. * The returned node can only be inserted in the same tree that context node belongs to. * * @param context ASTJNode that belongs to the same tree that cloned node will be inserted to * @param node node that needs to be cloned * @return new node * * @see org.eclipse.emf.codegen.merge.java.facade.FacadeHelper#cloneNode(java.lang.Object, org.eclipse.emf.codegen.merge.java.facade.JNode) */ @Override public ASTJNode<?> cloneNode(Object context, JNode node) { if (node instanceof JCompilationUnit) { String content = applyFormatRules(toString(node.getContents())); return createCompilationUnit(node.getName(), content); } else { ASTJNode<?> newASTJNode = null; ASTNode originalASTNode = ((ASTJNode<?>)node).getASTNode(); ASTJNode<?> contextNode = (ASTJNode<?>)context; ASTRewrite rewriter = contextNode.getRewriter(); // handle field declarations separately if (node instanceof ASTJField) { newASTJNode = cloneField((ASTJField)node, contextNode); } else // create new node and replace it all by original contents { String contents = applyFormatRules(node.getContents()); // note that string place holder adjusts indentation // to correct this trackAndReplace method is used below ASTNode newASTNode = rewriter.createStringPlaceholder(contents, originalASTNode.getNodeType()); newASTJNode = (ASTJNode<?>)convertToNode(newASTNode); newASTJNode.trackAndReplace(newASTNode, contents); } // set rewriter on the new node newASTJNode.setRewriter(contextNode.getRewriter()); return newASTJNode; } } /** * Copies the ASTJField node. * <p> * The copied field should <b>not</b> be modified (using set methods) nor read (using get methods), * and can <b>only</b> be inserted into the same tree that context node belongs to. * <p> * If the source field has only 1 variable, returned field is replaced by contents of original field declaration. * The returned field will have no internal structure. * <p> * If the source field has multiple variables declared in it, the returned field will * contain only 1 variable. Annotations, javadoc and initializer are replaced by original contents * with formatting preserved. Other parts of the declaration is not guaranteed to have formatting preserved. * The copied field will have source ranges for all nodes relative to the source file, hence, get methods might return * incorrect contents. * * @param originalField * @param contextNode */ protected ASTJField cloneField(ASTJField originalField, ASTJNode<?> contextNode) { ASTJField newField = null; FieldDeclaration originalFieldDeclaration = originalField.getOriginalFieldDeclaration(); // if there are multiple variables in the same field declaration, create declaration with only 1 variable if (originalFieldDeclaration.fragments().size() > 1) { AST ast = contextNode.getWrappedObject().getAST(); // note that the copied tree should not be modified by wrapped ASTJField // // the copied tree will have source ranges for all nodes in the source file, // hence, the get methods in the new ASTJField will not return the right contents FieldDeclaration newFieldDeclaration = (FieldDeclaration)ASTNode.copySubtree(ast, originalFieldDeclaration); @SuppressWarnings("unchecked") List<Object> newFragmentsList = newFieldDeclaration.fragments(); newFragmentsList.clear(); ASTNode newASTNode = ASTNode.copySubtree(ast, originalField.getWrappedVariableDeclarationFragment()); newFragmentsList.add(newASTNode); newField = (ASTJField)convertToNode(newASTNode); // rewriter is required for set methods newField.setRewriter(contextNode.getRewriter()); // set comment and initializer as strings newField.setComment(originalField.getComment()); newField.setInitializer(originalField.getInitializer()); // set annotation contents Iterator<JNode> originalChildrenIterator = originalField.getChildren().iterator(); Iterator<JNode> childrenIterator = newField.getChildren().iterator(); for (; childrenIterator.hasNext() && originalChildrenIterator.hasNext();) { ASTJAnnotation originalAnnotation = (ASTJAnnotation)originalChildrenIterator.next(); ASTJAnnotation annotation = (ASTJAnnotation)childrenIterator.next(); annotation.setContents(applyFormatRules(originalAnnotation.getContents())); } } else // create new field and replace it all by original contents { String contents = applyFormatRules(originalField.getContents()); FieldDeclaration fieldDeclaration = (FieldDeclaration)contextNode.getRewriter().createStringPlaceholder( contents, ASTNode.FIELD_DECLARATION); newField = (ASTJField)convertToNode(fieldDeclaration.fragments().get(0)); newField.trackAndReplace(fieldDeclaration, contents); } return newField; } /** * Finds the parent node based on the parent of wrapped AST node. * * @return parent {@link JNode} */ public ASTJNode<?> findParent(ASTNode node) { JNode parent = null; // skip nodes in hierarchy that can not be converted to JNode (i.e. VariableDeclarationFragment) do { node = node.getParent(); parent = convertToNode(node); } while (parent == null && node != null); return (ASTJNode<?>)parent; } /* (non-Javadoc) * @see org.eclipse.emf.codegen.merge.java.facade.FacadeHelper#doConvertToNode(java.lang.Object) */ @Override protected ASTJNode<?> doConvertToNode(Object object) { ASTJNode<?> node = null; if (object instanceof ASTNode) { ASTNode astNode = (ASTNode)object; // get rewriter ASTRewrite rewriter = null; ASTJCompilationUnit compilationUnit = (ASTJCompilationUnit)getCompilationUnit(findParent((ASTNode)object)); if (compilationUnit != null) { rewriter = compilationUnit.getRewriter(); } boolean isCompilationUnit = false; switch (astNode.getNodeType()) { case ASTNode.COMPILATION_UNIT: node = new ASTJCompilationUnit((CompilationUnit)object); isCompilationUnit = true; break; case ASTNode.VARIABLE_DECLARATION_FRAGMENT: node = new ASTJField((VariableDeclarationFragment)object, this, rewriter); break; case ASTNode.IMPORT_DECLARATION: node = new ASTJImport((ImportDeclaration)object); break; case ASTNode.INITIALIZER: node = new ASTJInitializer((Initializer)object); break; case ASTNode.METHOD_DECLARATION: node = new ASTJMethod((MethodDeclaration)object); break; case ASTNode.PACKAGE_DECLARATION: node = new ASTJPackage((PackageDeclaration)object); break; case ASTNode.TYPE_DECLARATION: node = new ASTJType((TypeDeclaration)object); break; case ASTNode.MARKER_ANNOTATION: case ASTNode.NORMAL_ANNOTATION: case ASTNode.SINGLE_MEMBER_ANNOTATION: node = new ASTJAnnotation((Annotation)object); break; case ASTNode.ANNOTATION_TYPE_DECLARATION: node = new ASTJAnnotationType((AnnotationTypeDeclaration)object); break; case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION: node = new ASTJAnnotationTypeMember((AnnotationTypeMemberDeclaration)object); break; case ASTNode.ENUM_DECLARATION: node = new ASTJEnum((EnumDeclaration)object); break; case ASTNode.ENUM_CONSTANT_DECLARATION: node = new ASTJEnumConstant((EnumConstantDeclaration)object); } if (node != null) { node.setFacadeHelper(this); if (!isCompilationUnit) { node.setRewriter(rewriter); } } } return node; } /** * Updates wrapped objects to nodes map. This method must be called * when wrapped ASTNode of ASTJNode is changed. * * @param node */ public void updateObjectToNodeMap(ASTJNode<?> node) { objectToNodeMap.put( node instanceof ASTJField ? ((ASTJField)node).getWrappedVariableDeclarationFragment() : node.getWrappedObject(), node); } /* (non-Javadoc) * @see org.eclipse.emf.codegen.merge.java.facade.FacadeHelper#addChild(org.eclipse.emf.codegen.merge.java.facade.JNode, org.eclipse.emf.codegen.merge.java.facade.JNode) */ @Override public boolean addChild(JNode node, JNode child) { if (node == null) { return false; } return ((ASTJNode<?>)node).addChild((ASTJNode<?>)child); } /** * Removes the given node. * <p> * Most <code>get...()</code> operations on the removed node will not give the correct results * even after insertion of the node. * * @see org.eclipse.emf.codegen.merge.java.facade.FacadeHelper#remove(org.eclipse.emf.codegen.merge.java.facade.JNode) */ @Override public boolean remove(JNode node) { if (node != null) { ASTJNode<?> astjNode = (ASTJNode<?>)node; ASTJNode<?> parent = astjNode.getParent(); if (parent != null) { // update the wrapped object map if (parent.remove(astjNode)) { updateObjectToNodeMap(astjNode); } return true; } } return false; } @Override public void commentOut(JNode node) { ASTJNode<?> astjNode = (ASTJNode<?>)node; astjNode.commentOut(); } /* (non-Javadoc) * @see org.eclipse.emf.codegen.merge.java.facade.FacadeHelper#insertSibling(org.eclipse.emf.codegen.merge.java.facade.JNode, org.eclipse.emf.codegen.merge.java.facade.JNode, boolean) */ @Override public boolean insertSibling(JNode node, JNode newSibling, boolean before) { if (node != null && newSibling != null) { ASTJNode<?> astjNode = (ASTJNode<?>)node; ASTJNode<?> parent = astjNode.getParent(); if (parent != null) { return parent.insertSibling(astjNode, (ASTJNode<?>)newSibling, before); } } return false; } /* (non-Javadoc) * @see org.eclipse.emf.codegen.merge.java.facade.FacadeHelper#toString(java.lang.Object) */ @Override public String toString(Object object) { if (object instanceof ASTJNode<?>) { return toString(((ASTJNode<?>)object).getASTNode()); } if (object instanceof ASTNode) { return toString((ASTNode)object); } else { return super.toString(object); } } /** * Gets the original contents of the node. * <p> * The contents of the node in AST implementation includes only the node itself without * the leading or trailing whitespace. * If the node has a Javadoc comment, it is included in the contents. No other leading or trailing * comments are included in the node contents. * <p> * The given node must be one of the nodes of the AST tree created by {@link #createCompilationUnit(String, String)}. * <p> * Note that contents is cached on the first access. Therefore, returned contents will not reflect any changes to the nodes. * * @param node node to get contents for * @return original contents of the node */ public String toString(ASTNode node) { if (node == null) { return null; } if (nodeContents.containsKey(node)) { return nodeContents.get(node); } JNode root = convertToNode(node.getRoot()); if (root == null || !(root instanceof ASTJCompilationUnit)) { root = getCompilationUnit(convertToNode(node)); } ASTJCompilationUnit compilationUnit = (ASTJCompilationUnit)root; if (compilationUnit != null) { char[] originalContents = compilationUnit.getOriginalContents(); //String resultString = new String(originalContents, compilationUnit.getASTCompilationUnit().getExtendedStartPosition(node), compilationUnit.getASTCompilationUnit().getExtendedLength(node)); int start = node.getStartPosition(); int length = node.getLength(); TargetSourceRangeComputer rangeComputer = compilationUnit.getRewriter().getExtendedSourceRangeComputer(); if (rangeComputer instanceof CommentAwareSourceRangeComputer) { SourceRange sourceRange = ((CommentAwareSourceRangeComputer)rangeComputer).computeDefaultSourceRange(node); length = (sourceRange.getStartPosition() + sourceRange.getLength()) - start; } String resultString = null; if (start >= 0 && start + length <= originalContents.length) { resultString = new String(originalContents, start, length); } nodeContents.put(node, resultString); return resultString; } // this is a fall-back, however, this should never be called return node.toString(); } /** * Adds a new entry to the log using INFO level. * * @param string to add to the log */ public void logInfo(String string) { CodeGenPlugin.INSTANCE.log("INFO " + string); } /** * Adds a new entry to the log using ERROR level. * * @param string to add to the log */ public void logError(String string) { CodeGenPlugin.INSTANCE.log("ERROR " + string); } /** * Adds a new entry to the log using ERROR level. * * @param string to add to the log * @param e exception to record */ public void logError(String string, Exception e) { CodeGenPlugin.INSTANCE.log("ERROR " + string); CodeGenPlugin.INSTANCE.log(e); } }