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}