001package org.nasdanika.html.ecore; 002 003import java.io.IOException; 004import java.nio.charset.StandardCharsets; 005import java.util.ArrayList; 006import java.util.Collection; 007import java.util.Collections; 008import java.util.function.Function; 009import java.util.stream.Collectors; 010 011import org.apache.commons.codec.binary.Hex; 012import org.eclipse.emf.common.util.EList; 013import org.eclipse.emf.ecore.EClass; 014import org.eclipse.emf.ecore.EClassifier; 015import org.eclipse.emf.ecore.EObject; 016import org.eclipse.emf.ecore.EPackage; 017import org.nasdanika.common.Context; 018import org.nasdanika.common.DiagramGenerator; 019import org.nasdanika.common.ProgressMonitor; 020import org.nasdanika.emf.PlantUmlTextGenerator; 021import org.nasdanika.emf.PlantUmlTextGenerator.RelationshipDirection; 022import org.nasdanika.exec.content.ContentFactory; 023import org.nasdanika.exec.content.Text; 024import org.nasdanika.html.model.app.Action; 025import org.nasdanika.html.model.app.AppFactory; 026import org.nasdanika.ncore.util.NcoreUtil; 027 028public class EPackageActionSupplier extends ENamedElementActionSupplier<EPackage> { 029 030 public EPackageActionSupplier(EPackage value, Context context, java.util.function.Function<EPackage,String> ePackagePathComputer) { 031 super(value, context, ePackagePathComputer); 032// dump(value, 0); 033 } 034 035 @Override 036 protected void header(Action action, ProgressMonitor progressMonitor) throws Exception { 037 Text text = ContentFactory.eINSTANCE.createText(); 038 text.setContent("<div class='text-monospace'>" + eObject.getNsURI() + "</div>"); 039 action.getContent().add(text); 040 } 041 042 043// private static void dump(EPackage ePackage, int offset) { 044// String prefix = ""; 045// for (int i = 0; i < offset; ++i) { 046// prefix += "\t"; 047// } 048// System.out.println(prefix + ePackage.getName()); 049// for (EPackage sp: ePackage.getESubpackages()) { 050// dump(sp, offset + 1); 051// } 052// for (EClassifier ec: ePackage.getEClassifiers()) { 053// System.out.println(prefix + "\t" + ec.getName()); 054// if (ec instanceof EClass) { 055// for (EStructuralFeature sf: ((EClass) ec).getEStructuralFeatures()) { 056// System.out.println(prefix + "\t\t" + sf.getName()); 057// } 058// for (EOperation op: ((EClass) ec).getEOperations()) { 059// System.out.println(prefix + "\t\t" + op.getName() + "()"); 060// } 061// } 062// } 063// } 064 065 @Override 066 public Action execute(EClass contextEClass, ProgressMonitor progressMonitor) throws Exception { 067 Action action = super.execute(contextEClass, progressMonitor); 068 String ePackageFolder = ePackagePathComputer == null ? Hex.encodeHexString(eObject.getNsURI().getBytes(StandardCharsets.UTF_8)) : ePackagePathComputer.apply(eObject); 069 action.setLocation(ePackageFolder + "/package-summary.html"); 070 action.setId(eObject.eClass().getName() + "-" + encodeEPackage(eObject)); 071 072 String diagramMode = NcoreUtil.getNasdanikaAnnotationDetail(eObject, "diagram", "navigation"); 073 switch (diagramMode) { 074 case "content": 075 addContent(action, generateDiagram(false, null, 0, RelationshipDirection.both, true, true)); 076 break; 077 case "none": 078 break; 079 case "navigation": { 080 Action diagramAction = AppFactory.eINSTANCE.createAction(); 081 action.getNavigation().add(diagramAction); 082 diagramAction.setText("Diagram"); 083 diagramAction.setIcon("fas fa-project-diagram"); 084 diagramAction.setLocation("package-summary-diagram.html"); 085 addContent(diagramAction, generateDiagram(false, null, 0, RelationshipDirection.both, true, true)); 086 break; 087 } 088 case "anonymous": { 089 Action diagramAction = AppFactory.eINSTANCE.createAction(); 090 action.getAnonymous().add(diagramAction); 091 diagramAction.setText("Diagram"); 092 diagramAction.setIcon("fas fa-project-diagram"); 093 diagramAction.setLocation(ePackageFolder + "/package-summary-diagram.html"); 094 addContent(diagramAction, generateDiagram(false, null, 0, RelationshipDirection.both, true, true)); 095 break; 096 } 097 default: 098 throw new IllegalArgumentException("Unsupported diagram annotation value '" + diagramMode +"' on EPackage " + eObject); 099 } 100 101 // TODO - Table (list) of contents 102// addContent(data, Collections.singletonMap("component-list-of-contents", Collections.singletonMap("tooltip", true))); 103 104 EList<EObject> children = action.getChildren(); 105 for (EPackage subPackage: eObject.getESubpackages().stream().sorted((a,b) -> a.getName().compareTo(b.getName())).collect(Collectors.toList())) { 106 children.add(adaptChild(subPackage).execute(contextEClass, progressMonitor)); 107 } 108 109 for (EClassifier eClassifier: eObject.getEClassifiers().stream().sorted((a,b) -> a.getName().compareTo(b.getName())).collect(Collectors.toList())) { 110 children.add(adaptChild(eClassifier).execute(contextEClass, progressMonitor)); 111 } 112 113 return action; 114 } 115 116 /** 117 * Generates PNG diagram. 118 * @return Inline PNG and the image map. 119 * @throws IOException 120 */ 121 protected String generateDiagram( 122 boolean leftToRightDirection, 123 String width, 124 int depth, 125 PlantUmlTextGenerator.RelationshipDirection relationshipDirection, 126 boolean appendAttributes, 127 boolean appendOperations) throws Exception { 128 129 StringBuilder sb = new StringBuilder(); 130 131 PlantUmlTextGenerator gen = new PlantUmlTextGenerator(sb, eClassifierLinkResolver, this::getEModelElementFirstDocSentence) { 132 133 @Override 134 protected Collection<EClass> getSubTypes(EClass eClass) { 135 return EPackageActionSupplier.this.getSubTypes(eClass); 136 } 137 138 @Override 139 protected Collection<EClass> getReferrers(EClass eClass) { 140 return EPackageActionSupplier.this.getReferrers(eClass); 141 } 142 143 @Override 144 protected boolean isAppendAttributes(EClass eClass) { 145 return appendAttributes; 146 } 147 148 @Override 149 protected boolean isAppendOperations(EClass eClass) { 150 return appendOperations; 151 } 152 153 @Override 154 protected Collection<EClass> getUses(EClassifier eClassifier) { 155 return Collections.emptySet(); // No usage information on package diagrams - too much. 156 } 157 158 }; 159 160 if (leftToRightDirection) { 161 sb.append("left to right direction").append(System.lineSeparator()); 162 } 163 164 if (width != null) { 165 sb.append("scale ").append(width).append(" width").append(System.lineSeparator()); 166 } 167 168 gen.appendWithRelationships(eObject.getEClassifiers(), relationshipDirection, depth); 169 return context.get(DiagramGenerator.class).generateUmlDiagram(sb.toString()); 170 } 171 172 /** 173 * Override to return a list of sub-types of given EClass. 174 * This implementation returns all sub-types found in the current package. 175 * @param eClass 176 * @return 177 */ 178 protected Collection<EClass> getSubTypes(EClass eClass) { 179 Collection<EClass> ret = new ArrayList<>(); 180 for (EClassifier ec: eObject.getEClassifiers()) { 181 if (eClass != ec && ec instanceof EClass && eClass.isSuperTypeOf((EClass) ec)) { 182 ret.add((EClass) ec); 183 } 184 } 185 return ret; 186 } 187 188 protected Function<EClassifier, String> eClassifierLinkResolver = target -> { 189 String localName = target.getName() + ".html"; 190 if (target.getEPackage().getNsURI().equals(eObject.getNsURI())) { 191 return localName; 192 } 193 194 StringBuilder pathUp = new StringBuilder(); 195 for (EPackage p = eObject; p != null; p = p.getESuperPackage()) { 196 pathUp.append("../"); 197 } 198 199 return pathUp + encodeEPackage(target.getEPackage()) + "/" + localName; 200 }; 201 202}