package com.baselet.generator; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import com.baselet.control.basics.geom.Rectangle; import com.baselet.control.config.ConfigClassGen; import com.baselet.control.enums.ElementId; import com.baselet.control.enums.generator.FieldOptions; import com.baselet.control.enums.generator.MethodOptions; import com.baselet.control.enums.generator.SignatureOptions; import com.baselet.diagram.CurrentDiagram; import com.baselet.diagram.DiagramHandler; import com.baselet.diagram.FontHandler; import com.baselet.diagram.Notifier; import com.baselet.element.ElementFactorySwing; import com.baselet.element.interfaces.GridElement; import com.baselet.generator.java.Accessible.AccessFlag; import com.baselet.generator.java.Field; import com.baselet.generator.java.JavaClass; import com.baselet.generator.java.JavaClass.ClassRole; import com.baselet.generator.java.Method; import com.baselet.generator.java.bcel.BcelJavaClass; import com.baselet.generator.java.jp.ClassParserException; import com.baselet.generator.java.jp.JpJavaClass; import com.baselet.generator.sorting.AlphabetLayout; import com.baselet.generator.sorting.HeightLayout; import com.baselet.generator.sorting.PackageLayout; import com.baselet.generator.sorting.RelationLayout; import com.baselet.generator.sorting.SortableElement; import com.baselet.gui.command.AddElement; /** * Creates a class element from a filename pointing to a .class or .java file according to UML standards, * adds the class to the current diagram and resizes this class element to minimum size where all text is visible. * * @author Lisi Bluemelhuber * */ public class ClassDiagramConverter { private final int GRIDSIZE; public ClassDiagramConverter() { GRIDSIZE = CurrentDiagram.getInstance().getDiagramHandler().getGridSize(); } public static String convertFailuresToString(List<Exception> failures) { StringBuilder sb = new StringBuilder(); for (Exception failure : failures) { if (sb.length() > 0) { sb.append('\n'); } sb.append(failure.getMessage()); } return sb.toString(); } public void createClassDiagrams(List<String> filesToOpen) { List<Exception> failures = new ArrayList<Exception>(); List<SortableElement> elements = new ArrayList<SortableElement>(); for (String filename : filesToOpen) { try { SortableElement element = createElement(filename); if (element != null) { elements.add(element); } } catch (Exception e) { failures.add(e); } } if (!failures.isEmpty()) { Notifier.getInstance().showError(ClassDiagramConverter.convertFailuresToString(failures)); return; // if errors are in any of the files don't add any of them } switch (ConfigClassGen.getInstance().getGenerateClassSortings()) { case PACKAGE: new PackageLayout().layout(elements); break; case ALPHABET: new AlphabetLayout().layout(elements); break; case RELATIONS: new RelationLayout().layout(elements); break; default: new HeightLayout().layout(elements); // by height } addElementsToDiagram(elements); } private SortableElement createElement(String filename) throws Exception { JavaClass parsedClass = parseFile(filename); if (parsedClass == null) { return null; } String propertiesText = getElementProperties(parsedClass); List<String> propList = Arrays.asList(propertiesText.split("\n")); Rectangle initialSize = adjustSize(propList); GridElement clazz = ElementFactorySwing.create(ElementId.UMLClass, initialSize, propertiesText, null, CurrentDiagram.getInstance().getDiagramHandler()); return new SortableElement(clazz, parsedClass); } private void addElementsToDiagram(List<SortableElement> elements) { DiagramHandler handler = CurrentDiagram.getInstance().getDiagramHandler(); for (SortableElement e : elements) { new AddElement(e.getElement(), handler.realignToGrid(e.getElement().getRectangle().x), handler.realignToGrid(e.getElement().getRectangle().y), false).execute(handler); } handler.setChanged(true); } /** * Adjusts a Class GridElement to the minimum size where all text is visible. * * @param clazz * @return */ private Rectangle adjustSize(List<String> strings) { // GridElement clazz not yet fully initialized, cannot call clazz.getHandler(); FontHandler fontHandler = CurrentDiagram.getInstance().getDiagramHandler().getFontHandler(); int width = 0; int height = strings.size(); double heightTweaker = 0.1; for (String string : strings) { if (string.isEmpty()) { heightTweaker += 1; } else if (string.equals("--")) { heightTweaker += 0.5; } if (fontHandler.getTextWidth(string) > width) { width = (int) (fontHandler.getTextWidth(string) + fontHandler.getDistanceBetweenTexts()) + 10; } } height = (int) (fontHandler.getFontSize() + fontHandler.getDistanceBetweenTexts()) * (height - (int) heightTweaker); return new Rectangle(0, 0, align(width), align(height)); // width&height must be multiples of grid size } private int align(int n) { return n - n % GRIDSIZE + GRIDSIZE; } private String getElementProperties(JavaClass parsedClass) { StringBuilder sb = new StringBuilder(""); createTopSection(parsedClass, sb); sb.append("--\n"); createFieldSection(parsedClass, sb); sb.append("--\n"); createMethodSection(parsedClass, sb); sb.append("--\n"); return sb.toString(); } private void createMethodSection(JavaClass parsedClass, StringBuilder sb) { for (Method method : parsedClass.getMethods()) { if (ConfigClassGen.getInstance().getGenerateClassMethods() == MethodOptions.PUBLIC && method.getAccess() == AccessFlag.PUBLIC) { sb.append(getMethodString(method)); } else if (ConfigClassGen.getInstance().getGenerateClassMethods() == MethodOptions.ALL) { sb.append(getMethodString(method)); } } } private String getMethodString(Method method) { if (ConfigClassGen.getInstance().getGenerateClassSignatures() == SignatureOptions.PARAMS_ONLY) { return method.getAccess() + method.getName() + "(" + method.getSignature() + ")\n"; } else if (ConfigClassGen.getInstance().getGenerateClassSignatures() == SignatureOptions.RETURN_ONLY) { return method.getAccess() + method.getName() + ": " + method.getReturnType() + "\n"; } else { return method.getAccess() + method.getName() + "(" + method.getSignature() + "): " + method.getReturnType() + "\n"; } } private void createFieldSection(JavaClass parsedClass, StringBuilder sb) { for (Field field : parsedClass.getFields()) { if (ConfigClassGen.getInstance().getGenerateClassFields() == FieldOptions.PUBLIC && field.getAccess() == AccessFlag.PUBLIC) { sb.append(field.getAccess()).append(field.getName()).append(": ").append(field.getType()).append("\n"); } else if (ConfigClassGen.getInstance().getGenerateClassFields() == FieldOptions.ALL) { sb.append(field.getAccess()).append(field.getName()).append(": ").append(field.getType()).append("\n"); } } } private void createTopSection(JavaClass parsedClass, StringBuilder sb) { ClassRole role = parsedClass.getRole(); if (role == ClassRole.INTERFACE) { sb.append("<<").append(role).append(">>\n").append(AlphabetLayout.getClassName(parsedClass)); } else if (role == ClassRole.ABSTRACT) { sb.append("/").append(AlphabetLayout.getClassName(parsedClass)).append("/"); } else { sb.append(AlphabetLayout.getClassName(parsedClass)); } sb.append("\n"); } private JavaClass parseFile(String filename) throws Exception { String extension = getExtension(filename); if (extension.equals("java")) { return new JpJavaClass(filename); } else if (extension.equals("class")) { return new BcelJavaClass(filename); } else { throw new ClassParserException("Unknown extension " + extension + " of file " + filename); } } private String getExtension(String filename) { int dotPosition = filename.lastIndexOf("."); return filename.substring(dotPosition + 1, filename.length()); } }