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}