// TODO Have an input for number of tabs? @Override public String toGraphVizString() { StringBuilder builder = new StringBuilder(); int i = 0; builder.append("\""); builder.append(this.fullName); builder.append("\" [\n\tshape = "); builder.append(this.shape); builder.append(",\n\tlabel = \"{"); // TODO Change for other types of classes. if ((this.accessType & Opcodes.ACC_INTERFACE) == Opcodes.ACC_INTERFACE) { builder.append("\\<\\<"); builder.append(this.fullName); builder.append("\\>\\>"); } else { builder.append(this.fullName); } for (String s : this.patternNames) { builder.append("\\n\\<\\<"); builder.append(s); builder.append("\\>\\>"); } if (this.fields.size() != 0 || this.methods.size() != 0) { // TODO Shouldn't this always do this? builder.append("|"); } i = 0; for (UMLField f : this.fields) { if (i > MAX_ROWS) { builder.append("..."); break; } builder.append(f.toGraphVizString()); i++; } builder.append("|"); i = 0; for (UMLMethod m : this.methods) { if (i > MAX_ROWS) { builder.append("..."); break; } builder.append(m.toGraphVizString()); i++; } builder.append("}\"\nstyle=filled\nfillcolor=\""); builder.append(this.fillColor); builder.append("\""); builder.append("\ncolor=\"").append(this.color).append("\"\n];\n"); for (UMLArrow arrow : this.arrows) { builder.append(arrow.toGraphVizString()); } return builder.toString(); }
public ArrayList<UMLClass> getAllUsedClasses() { ArrayList<UMLClass> usedClasses = new ArrayList<UMLClass>(); for (UMLArrow arrow : this.arrows) { if (arrow.isUsesArrow()) { usedClasses.add(arrow.getEndClass()); } } return usedClasses; }
/** * This method is only useful if the graph has been built, and {@link #generateArrows(ArrayList)} * has been called on all classes. This method removes any arrows that point to classes that are * super classes of classes that this class has arrows to. */ public void removeExtraArrows() { ArrayList<UMLClass> pointedTo = new ArrayList<UMLClass>(); for (UMLArrow arrow : this.arrows) { pointedTo.add(arrow.getEndClass()); } // If the used class is in a field, ignore it. for (UMLClass firstClass : new ArrayList<UMLClass>(pointedTo)) { for (UMLClass secondClass : new ArrayList<UMLClass>(pointedTo)) { if (firstClass.checkExtendsOrImplements(secondClass)) { pointedTo.remove(secondClass); } else if (secondClass.checkExtendsOrImplements(firstClass)) { pointedTo.remove(firstClass); } } } // Add back in extends/implements arrows for (UMLArrow arrow : this.arrows) { if (arrow.extendsOrImplements()) { pointedTo.add(arrow.getEndClass()); } } for (UMLArrow arrow : new ArrayList<UMLArrow>(this.arrows)) { if ((!pointedTo.contains(arrow.getEndClass())) && arrow.isUsesArrow()) { this.arrows.remove(arrow); } } }
public ArrayList<UMLClass> getAllExtendsOrImplements() { ArrayList<UMLClass> finalList = new ArrayList<UMLClass>(); ArrayList<UMLClass> extendsOrImplements = new ArrayList<UMLClass>(); for (UMLArrow arrow : this.arrows) { if (arrow.extendsOrImplements()) { extendsOrImplements.add(arrow.getEndClass()); } } for (UMLClass uClass : extendsOrImplements) { finalList.addAll(uClass.getAllExtendsOrImplementsHelper()); } return finalList; }
/** * Helper method for {@link #toGraphVizString()}. Adds an arrow to an ArrayList if there isn't an * arrow that already connects the two. * * @param arrows ArrayList of UMLArrows that the arrow (may) be added to. * @param firstName Full name of the class the arrow will start from. * @param secondName Full name of the class the arrow will end at. * @param arrowType The type of arrowhead. See GraphViz documentation. * @param lineType The type of line. See GraphViz documentation. */ private void addArrow(UMLClass end, String arrowType, String lineType) { // TODO Instead of just checking to see if the arrow exists, check the priority of the arrow. // eg association overwrites uses so it's no longer dependant on what arrows we make first. boolean isConnected = false; for (UMLArrow arrow : arrows) { if (arrow.connects(this, end)) { // if( (arrowType.equals("onormal") && arrow.extendsOrImplements()) { // || // (arrowType.equals("vee") && lineType.equals("dashed")) || // (arrow.isAssociationArrow() && arrowType.equals("vee") && // lineType.equals("dashed"))) { isConnected = true; break; // } } } if (!isConnected) { arrows.add(new UMLArrow(this, end, arrowType, lineType)); } }
/** * This method is only useful if the graph has been built, and {@link #generateArrows(ArrayList)} * has been called on all classes. * * @param c The class to check if this class (directly or indirectly) extends or implements. * @return Whether or not this class extends or implements c. */ public boolean checkExtendsOrImplements(UMLClass c) { if ((this.extension == null || this.extension.isEmpty()) && this.implementations.isEmpty()) { return false; } ArrayList<UMLClass> extImpl = new ArrayList<UMLClass>(); for (UMLArrow arrow : this.arrows) { if (arrow.extendsOrImplements()) { extImpl.add(arrow.getEndClass()); } } for (UMLClass cls : extImpl) { if (cls.getName().equals(c.getName())) { return true; } else if (cls.checkExtendsOrImplements(c)) { return true; } } return false; }
private ArrayList<UMLClass> getAllExtendsOrImplementsHelper() { ArrayList<UMLClass> finalList = new ArrayList<UMLClass>(); ArrayList<UMLClass> extendsOrImplements = new ArrayList<UMLClass>(); for (UMLArrow arrow : this.arrows) { if (arrow.extendsOrImplements()) { extendsOrImplements.add(arrow.getEndClass()); } } for (UMLClass uClass : extendsOrImplements) { ArrayList<UMLClass> temp = uClass.getAllExtendsOrImplementsHelper(); for (UMLClass uClass2 : temp) { if (!finalList.contains(uClass2)) { finalList.add(uClass2); } } } finalList.add(this); return finalList; }
// TODO Move these methods into UMLGraph where it makes more sense? // TODO Have a seperate array stored for all of the arrows? public void removeRedundantUsesArrows() { ArrayList<UMLClass> extendsOrImplements = this.getAllExtendsOrImplements(); ArrayList<UMLClass> thisUsed = this.getAllUsedClasses(); // Go through everything this class extends or implements (directly or // indirectly) for (UMLClass extendedClass : extendsOrImplements) { ArrayList<UMLClass> otherUsed = extendedClass.getAllUsedClasses(); // Go through all of the classes that this class uses for (UMLClass usedClass : thisUsed) { boolean removeArrow = true; // If the other class uses the same thing as this class, check // to see // if all instances where that is used is the same in both // classes // or not used in this class. If so, remove the arrow from this // class. if (otherUsed.contains(usedClass)) { // Check if the used class is within a field boolean foundField = false; for (UMLField field : this.getFields()) { if (field.getType().getFullBaseDataType().equals(usedClass.getName())) { foundField = true; removeArrow = false; } } // If the used class is in a field, don't remove the arrow, // so skip the arrow checks. if (!foundField) { // Check everything for type, and if they are the same // in the two // If everything is the same, remove this class' used // arrow for that thing for (UMLMethod thisMethod : this.methods) { boolean wasUsed = false; // Check to see if this method uses the used class // at all for (TypeData d : thisMethod.getClassesUsed()) { if (d.getFullBaseDataType().equals(usedClass.getName())) { wasUsed = true; break; } } // The class was used in the method. Check to see if // there is an identical method in the other. if (wasUsed) { boolean isSame = false; for (UMLMethod extendedMethod : extendedClass.getMethods()) { // If the methods have the same signature if (thisMethod.sameSignature(extendedMethod)) { isSame = true; break; } } if (!isSame) { removeArrow = false; break; } } } } if (removeArrow) { for (UMLArrow arrow : new ArrayList<UMLArrow>(this.arrows)) { if (arrow.isUsesArrow() && arrow.getEndClass().getName().equals(usedClass.getName())) { this.arrows.remove(arrow); break; } } } } } } }