package pyUML.backend; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.uml2.uml.Classifier; import org.eclipse.uml2.uml.Model; import org.python.pydev.parser.jython.ast.Name; import org.python.pydev.parser.jython.ast.Num; import org.python.pydev.parser.jython.ast.Str; import org.python.pydev.parser.jython.ast.exprType; import pyUML.pythonTree.PythonTreeFile; import pyUML.pythonTree.PythonTreePackage; import pyUML.pythonTree.PythonTreeRoot; /** * Some Helper methods needed for parsing Pything Code * and handling the Python Abstract Syntax Tree */ public class ParseHelpers { /** * Reads out a file and returns the file content as a string * * @param file the file to read * @return a String representing the file's content * null on Error */ public static String fileToString(File file, IProject project) { IFile iFile = EclipseHelperMethods.createFile(file.getAbsolutePath(), project); return EclipseHelperMethods.iFileToString(iFile); } /** * (Re-)Writes a string to a file and updates the file in the project, * if the project is != null * @param file the file to write to * @param str the String to write to the File * @param project (optional) the project where the file is located in */ public static void stringToFile(File file, String str, IProject project) { try { file.createNewFile(); BufferedWriter writer = new BufferedWriter(new FileWriter(file)); writer.write(str); writer.close(); EclipseHelperMethods.updateFile(file.getAbsolutePath(), project); }catch (IOException e) { MessageDialog.openError(null, "IOException", "Error writing to file "+ file.getPath()); e.printStackTrace(); } } /** * Extracts a String representation of a python expression, * if the expression is of type String or Num. * The quotes of Strings are preserved * * @param expr The Python AST expression * @param fileContent the content of the file where the expression * is to be read * @param attribute if true, expressions other than Strings or numbers are * read by just reading the pure file content to the end of the line * (or to a # sign). This works ONLY for attributes, not for expressions in * function definitions. * @param abbreviate if true, long values are abbreviated; at the end "(...)" * is appended * @param ONLY if this reads an attribute value: the value of the next line * (needed to read multi-line attributes) * if this is no attribute, this value is ignored. * * @return The String representation of this expression, null if expr is null */ public static String getStringOfExpr(exprType expr, PythonTreeFile pyFile, boolean attribute, boolean abbreviate, int nextLine){ if (expr == null) return null; String exprString; if (expr instanceof Str) { Str defaultValStr = (Str) expr; exprString = defaultValStr.s; if (defaultValStr.type == Str.SingleDouble) { exprString = "\"" + exprString + "\""; } else if (defaultValStr.type == Str.SingleSingle) { exprString = "'" + exprString + "'"; } } else if (expr instanceof Name) { Name defaultValNum = (Name) expr; exprString = defaultValNum.id; } else if (expr instanceof Num) { Num defaultValNum = (Num) expr; exprString = defaultValNum.num; } else if (attribute){ exprString = readExprAsText(expr, pyFile, nextLine); } else { return GlobalConstants.getUnknownExpression(); } // cut default value, if it is too long if (abbreviate && exprString.length() > 19) { exprString = exprString.substring(0,14) + "(...)"; } return exprString; } /** * Helper method for getStringOfExpr() * Can read a multi-line-expression of an attribute * * @param expr * @param pyFile * @param nextLine * @return */ public static String readExprAsText(exprType expr, PythonTreeFile pyFile, int nextLine) { String[] lines = pyFile.getSplittedFileContent(); String exprString = lines[expr.beginLine-1]; exprString = exprString.substring(exprString.indexOf("=")+1); exprString = exprString.replaceAll("^[\\s]*",""); exprString = exprString.replaceAll(" # created by PyUML.*$", ""); exprString = exprString.replaceAll("[\\s]*$", ""); for (int i=expr.beginLine; i < nextLine - 1; i++) { String line = lines[i]; // docString comments of static attributes have a wrong "beginLine" value // -> manually define here to ignore docStrings! if (line.matches("^[\\s]*\"\"\".*")) break; line = line.replaceAll("# created by PyUML.*$", ""); line = " " +line.replaceAll("^[\\s]*", ""); exprString += line; exprString = exprString.replaceAll("[\\s]*$", ""); } // if there are called other classes/packages, the index // is on the value, not on the "a.b.c"-prefix. // if that's the case, add the prefix afterwards: return exprString; } /** * Simple method to extract the XMI-ID out of a line * containing the PyUML string "# PyUML: XMI_ID: ..." * @param content * @return */ public static String extractXmiFromString(String content) { String xmi_id = null; String[] lines = content.split("\n"); for (String line : lines) { if (line.matches("\\s*# PyUML:.*")) { String[] splittedLine = line.split("XMI_ID:"); if (splittedLine.length == 2) { xmi_id = splittedLine[1]; } } } return xmi_id; } /** * This creates a string containing the model package structure as * "/packSup/packSub/" , where the last package contains the * current class. * @param cl The class in the model to find the package structure for * @return a string containing the parent package structure * of this class * */ public static String getModelPackageStructure(org.eclipse.uml2.uml.Type cl) { org.eclipse.uml2.uml.Package pack = cl.getPackage(); String structure = ""; while (! (pack instanceof Model)) { structure = "/"+pack.getName() + structure ; pack = pack.getNestingPackage(); } return structure + "/"; } }