/*******************************************************************************
* Copyright (c) 2008, 2013 Institute for Software, HSR Hochschule fuer Technik
* Rapperswil, University of applied sciences 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:
* Institute for Software - initial API and implementation
* Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.ui.refactoring;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.core.runtime.Assert;
import org.eclipse.cdt.core.dom.ast.IASTArrayDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTArrayModifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTDeclarator;
import org.eclipse.cdt.core.dom.ast.IASTLabelStatement;
import org.eclipse.cdt.core.dom.ast.IASTName;
import org.eclipse.cdt.core.dom.ast.IASTNode;
import org.eclipse.cdt.core.dom.ast.IASTNode.CopyStyle;
import org.eclipse.cdt.core.dom.ast.IASTParameterDeclaration;
import org.eclipse.cdt.core.dom.ast.IASTPointerOperator;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclSpecifier;
import org.eclipse.cdt.core.dom.ast.IASTSimpleDeclaration;
import org.eclipse.cdt.core.dom.ast.INodeFactory;
import org.eclipse.cdt.core.dom.ast.IType;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTTranslationUnit;
import org.eclipse.cdt.core.dom.ast.cpp.ICPPNodeFactory;
import org.eclipse.cdt.core.dom.rewrite.DeclarationGenerator;
import org.eclipse.cdt.core.dom.rewrite.TypeHelper;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.internal.core.dom.parser.cpp.semantics.CPPVisitor;
import org.eclipse.cdt.internal.core.dom.rewrite.astwriter.ASTWriterVisitor;
/**
* Additional information about an IASTName in code being refactored.
*/
public class NameInformation {
public static enum Indirection { NONE, POINTER, REFERENCE }
public static final int INDEX_FOR_ADDED = -1;
private final IASTName name;
private IASTName declarationName;
private final List<IASTName> referencesInSelection;
private boolean isOutput;
private boolean mustBeReturnValue;
private boolean isWriteAccess;
private boolean passOutputByPointer;
private boolean isReturnValue;
private String newName;
private int newOrder;
private boolean isDeleted;
private String defaultValue;
private String newTypeName;
private Indirection indirection;
public NameInformation(IASTName name) {
this.name = name;
this.newName = String.valueOf(name.getSimpleID());
referencesInSelection = new ArrayList<>();
}
public static NameInformation createInfoForAddedParameter(String type, String name,
String defaultValue) {
NameInformation info= new NameInformation(null);
info.setTypeName(type);
info.setNewName(name);
info.setDefaultValue(defaultValue);
info.setNewOrder(INDEX_FOR_ADDED);
return info;
}
/**
* For debugging only.
*/
@Override
public String toString() {
return name.toString();
}
public int getNewOrder() {
return newOrder;
}
public void setNewOrder(int newOrder) {
this.newOrder = newOrder;
}
/**
* Returns <code>true</code> if the value of the variable has to propagate to the outside world.
*/
public boolean isOutput() {
return isOutput;
}
void setOutput(boolean isOutput) {
this.isOutput = isOutput;
indirection = null;
}
public boolean isOutputParameter() {
return isOutput() && !isReturnValue();
}
public boolean mustBeReturnValue() {
return mustBeReturnValue;
}
public void setMustBeReturnValue(boolean mustBeReturnValue) {
this.mustBeReturnValue = mustBeReturnValue;
indirection = null;
}
public boolean isReturnValue() {
return mustBeReturnValue || isReturnValue;
}
public void setReturnValue(boolean isReturnValue) {
Assert.isTrue(isReturnValue || !mustBeReturnValue);
this.isReturnValue = isReturnValue;
indirection = null;
}
public String getNewName() {
return newName;
}
public void setNewName(String newName) {
this.newName = newName;
}
public boolean isWriteAccess() {
return isWriteAccess;
}
void setWriteAccess(boolean isWriteAceess) {
this.isWriteAccess = isWriteAceess;
indirection = null;
}
public boolean isDeleted() {
return isDeleted;
}
public void markAsDeleted() {
Assert.isTrue(!isAdded()); // Added parameters should be simply removed from the list
isDeleted= true;
}
public boolean isAdded() {
// TODO(sprigogin): Adding parameters is not supported yet.
return false;
}
public String getDefaultValue() {
return defaultValue;
}
public void setDefaultValue(String value) {
Assert.isNotNull(value);
defaultValue= value;
}
public IASTName getDeclarationName() {
return declarationName;
}
public IASTDeclarator getDeclarator() {
return (IASTDeclarator) declarationName.getParent();
}
public IASTDeclSpecifier getDeclSpecifier() {
IASTNode parent = getDeclarator().getParent();
if (parent instanceof IASTSimpleDeclaration) {
return ((IASTSimpleDeclaration) parent).getDeclSpecifier();
} else if (parent instanceof IASTParameterDeclaration) {
return ((IASTParameterDeclaration) parent).getDeclSpecifier();
}
return null;
}
void setDeclarationName(IASTName declarationName) {
Assert.isTrue(declarationName.getPropertyInParent() == IASTDeclarator.DECLARATOR_NAME ||
declarationName.getPropertyInParent() == IASTLabelStatement.NAME);
this.declarationName = declarationName;
indirection = null;
}
public IASTName getName() {
return name;
}
public boolean isRenamed() {
return name == null ? newName != null : !String.valueOf(name.getSimpleID()).equals(newName);
}
void addReference(IASTName name, int startOffset, int endOffset) {
int nodeOffset = name.getFileLocation().getNodeOffset();
if (nodeOffset >= startOffset && nodeOffset < endOffset) {
referencesInSelection.add(name);
}
}
public String getTypeName() {
if (newTypeName != null)
return newTypeName;
INodeFactory nodeFactory = name.getTranslationUnit().getASTNodeFactory();
IASTParameterDeclaration declaration = getParameterDeclaration(nodeFactory, null);
if (declaration == null)
return null;
ASTWriterVisitor writer = new ASTWriterVisitor();
declaration.accept(writer);
return writer.toString();
}
public void setTypeName(String type) {
Assert.isNotNull(type);
newTypeName= type;
}
public String getReturnType() {
if (!isReturnValue())
return null;
INodeFactory nodeFactory = name.getTranslationUnit().getASTNodeFactory();
IASTDeclarator sourceDeclarator = getDeclarator();
IASTDeclSpecifier declSpec = safeCopy(getDeclSpecifier());
IASTDeclarator declarator = createDeclarator(nodeFactory, sourceDeclarator, null);
IASTParameterDeclaration declaration = nodeFactory.newParameterDeclaration(declSpec, declarator);
ASTWriterVisitor writer = new ASTWriterVisitor();
declaration.accept(writer);
return writer.toString();
}
public List<IASTName> getReferencesInSelection() {
return referencesInSelection;
}
public IASTParameterDeclaration getParameterDeclaration(INodeFactory nodeFactory) {
return getParameterDeclaration(nodeFactory, newName);
}
private IASTParameterDeclaration getParameterDeclaration(INodeFactory nodeFactory, String paramName) {
IASTDeclSpecifier sourceDeclSpec = getDeclSpecifier();
IASTDeclarator sourceDeclarator = getDeclarator();
IASTDeclSpecifier declSpec;
IASTDeclarator declarator;
if (sourceDeclSpec instanceof IASTSimpleDeclSpecifier &&
((IASTSimpleDeclSpecifier) sourceDeclSpec).getType() == IASTSimpleDeclSpecifier.t_auto) {
IType type = CPPVisitor.createType(sourceDeclarator);
DeclarationGenerator generator = DeclarationGenerator.create(nodeFactory);
declSpec = generator.createDeclSpecFromType(type);
declarator = generator.createDeclaratorFromType(type,
paramName == null ? null : paramName.toCharArray());
} else {
declSpec = safeCopy(sourceDeclSpec);
declarator = createDeclarator(nodeFactory, sourceDeclarator, paramName);
}
Indirection indirection = getIndirection();
if (indirection == Indirection.POINTER) {
declarator.addPointerOperator(nodeFactory.newPointer());
} else if (indirection == Indirection.REFERENCE) {
declarator.addPointerOperator(((ICPPNodeFactory) nodeFactory).newReferenceOperator(false));
}
if (indirection != Indirection.NONE && !isWriteAccess && declSpec != null) {
declSpec.setConst(true);
}
declarator.setNestedDeclarator(sourceDeclarator.getNestedDeclarator());
return nodeFactory.newParameterDeclaration(declSpec, declarator);
}
public Indirection getIndirection() {
if (indirection == null) {
indirection = Indirection.NONE;
boolean isCpp = declarationName.getTranslationUnit() instanceof ICPPASTTranslationUnit;
if (isOutputParameter()) {
if (isCpp && !passOutputByPointer) {
indirection = Indirection.REFERENCE;
} else {
indirection = Indirection.POINTER;
}
} else {
IType type = TypeHelper.createType(getDeclarator());
if (TypeHelper.shouldBePassedByReference(type, declarationName.getTranslationUnit())) {
if (isCpp) {
if (!isWriteAccess) {
indirection = Indirection.REFERENCE;
}
// TODO(sprigogin): Verify availability of the copy ctor before passing by value
} else {
indirection = Indirection.POINTER;
}
}
}
}
return indirection;
}
private IASTDeclarator createDeclarator(INodeFactory nodeFactory, IASTDeclarator sourceDeclarator,
String name) {
IASTName astName = name != null ?
nodeFactory.newName(name.toCharArray()) : nodeFactory.newName();
IASTDeclarator declarator;
if (sourceDeclarator instanceof IASTArrayDeclarator) {
IASTArrayDeclarator arrDeclarator = (IASTArrayDeclarator) sourceDeclarator;
IASTArrayDeclarator arrayDeclarator = nodeFactory.newArrayDeclarator(astName);
IASTArrayModifier[] arrayModifiers = arrDeclarator.getArrayModifiers();
for (IASTArrayModifier arrayModifier : arrayModifiers) {
arrayDeclarator.addArrayModifier(arrayModifier.copy(CopyStyle.withLocations));
}
declarator= arrayDeclarator;
} else {
declarator = nodeFactory.newDeclarator(astName);
}
for (IASTPointerOperator pointerOp : sourceDeclarator.getPointerOperators()) {
declarator.addPointerOperator(pointerOp.copy(CopyStyle.withLocations));
}
return declarator;
}
@SuppressWarnings("unchecked")
private static <T extends IASTNode> T safeCopy(T node) {
return node == null ? null : (T) node.copy(CopyStyle.withLocations);
}
public ITranslationUnit getTranslationUnit() {
return name != null ? name.getTranslationUnit().getOriginatingTranslationUnit() : null;
}
public boolean isPassOutputByPointer() {
return passOutputByPointer;
}
public void setPassOutputByPointer(boolean passOutputByPointer) {
this.passOutputByPointer = passOutputByPointer;
indirection = null;
}
}