package pyUML.pythonTree; import java.util.List; import java.util.Vector; import org.eclipse.uml2.uml.Comment; import org.eclipse.uml2.uml.NamedElement; import org.eclipse.uml2.uml.Property; import org.eclipse.uml2.uml.VisibilityKind; import org.python.pydev.parser.jython.ast.Assign; import org.python.pydev.parser.jython.ast.Attribute; import org.python.pydev.parser.jython.ast.Call; import org.python.pydev.parser.jython.ast.Name; import org.python.pydev.parser.jython.ast.NameTok; import org.python.pydev.parser.jython.ast.exprType; import pyUML.backend.GlobalConstants; import pyUML.backend.ParseHelpers; /** * This PythonTree member represents a python attribute. * It can represent static attributes (defined outside any method) * or object attributes (defined with "self." inside __init__() */ public class PythonTreeAttribute extends PythonTreeNode { private Assign astNode; private boolean valid; private boolean staticAtt; private String value; private String fullValue; private int line; private int col; private int nextLine; /** * @param parent the parent in the PythonTree * @param astNode the corresponding Python AST node * @param staticAtt true, if this is an attribute outside a * function definition * @param nextLine the line of the next Python statement. This * is needed to extract multi-line-attributes */ public PythonTreeAttribute(PythonTreeNode parent, Assign astNode, boolean staticAtt, int nextLine) { super(parent); this.astNode = astNode; this.staticAtt = staticAtt; this.nextLine = nextLine; this.valid = this.initAttribute(); } /** * @return false, if this is no valid attribute */ private boolean initAttribute(){ exprType left = this.astNode.targets[0]; exprType right = this.astNode.value; // analyze the left side of the assign and see // if this might be a valid attribute try { if (staticAtt) { //static: attr without "self" this.name = ((Name)left).id; this.line = ((Name)left).beginLine; this.col = ((Name)left).beginColumn; } else{ // object attr: begins with "self." Attribute leftAtt = (Attribute)left; if (! ((Name)leftAtt.value).id.equals("self")) return false; this.name =((NameTok)(leftAtt).attr).id; this.line = ((NameTok)(leftAtt).attr).beginLine; this.col = ((NameTok)(leftAtt).attr).beginColumn; } } catch (ClassCastException e) { // if anything is not like expected, this is not a // att=value // or self.att=value // attribute -> this is not considered a valid attribute! return false; } // now, look at the right side if (right instanceof Call) return false; this.value = ParseHelpers.getStringOfExpr(right, this.getParent().getInFile(), true, true, this.nextLine); // if the value was abbreviated, save full value, also. if (this.value.endsWith("(...)")) { this.fullValue = ParseHelpers.getStringOfExpr(right, this.getParent().getInFile(), true, false, this.nextLine); } // if everything went right, this is considered a valid attribute! return true; } /** * A PythonTreeAttribute is created always when an Assign * is found in the class body or the constructor. * Sometimes an assign does not declare an attribute; then, * this object should not be added to the PythonTreeClass * attributes and be left for the garbage collector. * * @return if this astNode really represents a python * static/object attribute and shall be saved */ public boolean isValid() { return this.valid; } public boolean isStatic() { return staticAtt; } /** * @return this attribute's (default) value */ public String getValue() { return value; } public boolean synchronizeModel(NamedElement modelElement) { Property modelProperty = (Property) modelElement; // set this attribute private in model, if its name starts with "__" // use protected, if class starts with "_" (this is only python convention) if (this.getName().startsWith("__")) { modelProperty.setVisibility(VisibilityKind.PRIVATE_LITERAL); } else if (this.getName().startsWith("_")) { modelProperty.setVisibility(VisibilityKind.PROTECTED_LITERAL); } String defVal = this.getValue(); if (! "None".equals(defVal)) modelProperty.setDefault(this.getValue()); // if default value was abbreviated -> // save full default value as comment, // beginning with a PyUML Marker if (this.fullValue != null) { Comment fullValueComment = null; for (Comment comment : modelProperty.getOwnedComments()) { if (comment.getBody().startsWith(GlobalConstants.getFullDefault())) { fullValueComment=comment; break; } } if (fullValueComment == null) fullValueComment = modelProperty.createOwnedComment(); fullValueComment.setBody(GlobalConstants.getFullDefault() + this.fullValue); } else { // there is no "full" value -> delete all comments // that save a full value List commentList = new Vector(); commentList.addAll(modelProperty.getOwnedComments()); for (Comment comment : commentList) { if (comment.getBody().startsWith(GlobalConstants.getFullDefault())) { comment.destroy(); } } } modelProperty.setIsStatic(this.isStatic()); return true; } public int getLine() { return line; } public int getCol() { return col; } public PythonTreeClass getParent() { return (PythonTreeClass) super.getParent(); } /** * @return the un-abbreviated attribute (default) value * for this attribute */ public String getFullValue() { return fullValue; } public int getNextLine() { return nextLine; } }