001package org.nasdanika.html.model.app.gen.maven;
002
003import java.io.File;
004import java.io.IOException;
005import java.net.URL;
006import java.util.ArrayList;
007import java.util.Collection;
008import java.util.Collections;
009import java.util.List;
010import java.util.Objects;
011import java.util.function.Function;
012import java.util.stream.Collectors;
013
014import org.apache.maven.plugins.annotations.Parameter;
015import org.eclipse.emf.common.util.URI;
016import org.eclipse.emf.ecore.EObject;
017import org.nasdanika.common.Context;
018import org.nasdanika.common.DiagramGenerator;
019import org.nasdanika.common.DiagramGeneratorImpl;
020import org.nasdanika.common.NasdanikaException;
021import org.nasdanika.common.ProgressMonitor;
022import org.nasdanika.common.Util;
023import org.nasdanika.drawio.Document;
024import org.nasdanika.html.emf.RepresentationProcessor;
025import org.nasdanika.html.model.app.Action;
026import org.nasdanika.html.model.app.Link;
027import org.nasdanika.html.model.app.gen.SemanticSiteGenerator;
028import org.nasdanika.html.model.app.gen.SiteGeneratorContributor;
029import org.nasdanika.maven.AbstractCommandMojo;
030import org.nasdanika.ncore.util.SemanticInfo;
031import org.nasdanika.ncore.util.SemanticRegistry;
032
033/**
034 * Base class for Mojos performing different semantic generations - XMI, action, site.
035 */
036public abstract class AbstractSemanticGeneratorMojo extends AbstractCommandMojo {
037                
038        /**
039         * Pluggable diagram generators. 
040         */
041    @Parameter
042    private List<DiagramGenerator> diagramGenerators;
043                
044        /**
045         * URL of the Drawio viewer script for rendering Drawio diagrams.
046         * Set if you are hosting your own Drawio site.
047         */
048        @Parameter      
049        private String drawioViewer;        
050        
051        /**
052         * URI of the semantic model to load. Resolved relative to the project base directory
053         */
054        @Parameter(required = true)     
055        protected String model;
056
057        /**
058         * If true, generation is performed in parallel threads.
059         */
060        @Parameter(required = false)    
061        protected boolean parallel;
062        
063        /**
064         * Contributors to site generation.
065         */
066    @Parameter
067    private List<SiteGeneratorContributor> contributors;
068        
069        /**
070         * URL's of JSON resources with information about external semantic elements. Such JSON resources are created as part of site generation. 
071         * They are named semantic-info.json
072         * Semantic infos are used to link model elements to externally defined element by URI. It is similar to how Java modules require other modules and then 
073         * classes in that module may reference elements from the required modules by their fully qualified names.
074         * 
075         * Semantic info may be created programmatically using {@link SemanticInfo} and {@link SemanticRegistry} classes.
076         */
077        @Parameter
078        private List<String> semanticInfos;
079        
080        protected List<SiteGeneratorContributor> getContributors() {
081                return contributors == null ? Collections.emptyList() : contributors;
082        }
083    
084        protected SemanticSiteGenerator createSemanticSiteGenerator(Context context, ProgressMonitor progressMonitor) {         
085                File baseDir = project.getBasedir();
086                URI baseDirURI = URI.createFileURI(baseDir.getAbsolutePath()).appendSegment("");                
087                SemanticRegistry semanticRegistry = new SemanticRegistry();             
088                if (semanticInfos != null) {
089                        for (String smLocation: semanticInfos) {                        
090                                URI semanticInfoURI = URI.createURI(smLocation).resolve(baseDirURI);
091                                try {
092                                        semanticRegistry.load(new URL(semanticInfoURI.toString()));
093                                } catch (IOException e) {
094                                        String message = "Could not load semantic info from " + semanticInfoURI;
095                                        getLog().error(message, e);
096                                        throw new NasdanikaException(message, e);
097                                }
098                        }
099                }
100                
101                return new SemanticSiteGenerator() {
102                        
103                        {
104                                this.parallel = AbstractSemanticGeneratorMojo.this.parallel;
105                        }
106                        
107                        @Override
108                        protected Iterable<SemanticInfo> getSemanticInfos() {
109                                return semanticRegistry
110                                        .stream()
111                                        .filter(SemanticInfo.class::isInstance)
112                                        .map(SemanticInfo.class::cast)
113                                        .toList();
114                        }
115                        
116                        @Override
117                        protected boolean isSemanticInfoLink(Link link) {
118                                if (link == null || Util.isBlank(link.getLocation())) {
119                                        return false;
120                                }
121                                String linkLocation = link.getLocation();
122                                return semanticRegistry
123                                        .stream()
124                                        .filter(SemanticInfo.class::isInstance)
125                                        .map(SemanticInfo.class::cast)
126                                        .map(SemanticInfo::getLocation)
127                                        .filter(Objects::nonNull)
128                                        .map(Object::toString)
129                                        .filter(linkLocation::equals)
130                                        .findFirst()
131                                        .isPresent();
132                        }                       
133                        
134                        @Override
135                        protected Context createContext(ProgressMonitor progressMonitor) {
136                                DiagramGenerator diagramGenerator;
137                                if (Util.isBlank(drawioViewer)) {
138                                        diagramGenerator = DiagramGenerator.INSTANCE;
139                                } else {
140                                        diagramGenerator = new DiagramGeneratorImpl() {
141                                                
142                                                protected String getDrawioViewer() {
143                                                        return drawioViewer;
144                                                };
145                                                
146                                        };
147                                }
148                                if (diagramGenerators != null) {
149                                        for (DiagramGenerator dg: diagramGenerators) {
150                                                diagramGenerator = dg.compose(diagramGenerator);
151                                        }
152                                }
153                                
154                                RepresentationProcessor representationProcessor = new RepresentationProcessor() {
155                                        
156                                        @Override
157                                        public Document processDrawioRepresentation(
158                                                        Document document, 
159                                                        Action action,
160                                                        Function<URI, EObject> semanticLinkResolver,
161                                                        org.nasdanika.html.emf.EObjectActionResolver.Context context,
162                                                        ProgressMonitor progressMonitor) {
163                                                
164                                                for (SiteGeneratorContributor contributor: getContributors()) {
165                                                        document = contributor.processDrawioRepresentation(document, action, semanticLinkResolver, context, progressMonitor);
166                                                }
167                                                
168                                                return document;
169                                        }
170                                        
171                                };
172                                
173                                return context
174                                                .compose(Context.singleton(DiagramGenerator.class, diagramGenerator))
175                                                .compose(Context.singleton(RepresentationProcessor.class, representationProcessor))
176                                                .compose(super.createContext(progressMonitor));
177                        }
178                        
179                        @Override
180                        protected ProgressMonitor createProgressMonitor() {
181                                return progressMonitor;
182                        }
183                        
184                        @Override
185                        protected Collection<SiteGeneratorContributor> getContributors() {
186                                Collection<SiteGeneratorContributor> allContributors = new ArrayList<>(super.getContributors());
187                                allContributors.addAll(AbstractSemanticGeneratorMojo.this.getContributors());
188                                return allContributors;
189                        }
190                                                
191                };
192        }
193
194}