001package org.nasdanika.html.model.app.gen.maven;
002
003import java.io.File;
004import java.io.FileNotFoundException;
005import java.io.IOException;
006import java.io.PrintStream;
007import java.util.Arrays;
008import java.util.Collection;
009import java.util.List;
010import java.util.Map;
011import java.util.Map.Entry;
012import java.util.concurrent.CancellationException;
013
014import org.apache.maven.plugin.AbstractMojo;
015import org.apache.maven.plugin.MojoExecutionException;
016import org.apache.maven.plugins.annotations.LifecyclePhase;
017import org.apache.maven.plugins.annotations.Mojo;
018import org.apache.maven.plugins.annotations.Parameter;
019import org.apache.maven.project.MavenProject;
020import org.eclipse.emf.common.util.DiagnosticException;
021import org.eclipse.emf.common.util.URI;
022import org.nasdanika.common.Context;
023import org.nasdanika.common.DiagramGenerator;
024import org.nasdanika.common.DiagramGeneratorImpl;
025import org.nasdanika.common.NasdanikaException;
026import org.nasdanika.common.ProgressMonitor;
027import org.nasdanika.common.Status;
028import org.nasdanika.common.Util;
029import org.nasdanika.html.model.app.gen.ActionSiteGenerator;
030
031/**
032 * Generates action site.
033 */
034@Mojo(name = "generate-action-site", defaultPhase = LifecyclePhase.SITE)
035public class ActionSiteGeneratorMojo extends AbstractMojo {
036
037        @Parameter(defaultValue = "target/action-site") 
038        private File outputDirectory;
039
040        @Parameter(defaultValue = "target/action-site-work-dir")        
041        private File workDirectory;
042        
043        @Parameter()    
044        private boolean cleanWorkDir;   
045        
046        @Parameter(required = true)     
047        private String action;
048        
049        @Parameter(required = true)     
050        private String pageTemplate;
051        
052        @Parameter()    
053        private String siteMapDomain;
054        
055        @Parameter()    
056        private String drawioViewer;    
057        
058        @Parameter(required = false)    
059        private int errors;
060        
061        @Parameter()    
062        private File progressOutput;    
063        
064    @Parameter()
065    private List<DiagramGenerator> diagramGenerators;
066        
067        
068//      @Parameter(name = "json-progress")      
069//      private boolean jsonProgress;   
070        
071        @Parameter(defaultValue = "${project}", required = true, readonly = true)
072        MavenProject project;   
073        
074        /**
075         * Progress monitor reporting to a {@link PrintStream}, e.g. ``System.out``.
076         * This monitor indents sub-tasks and worked messages. It can be thought of as a hierarchical logger.
077         * @author Pavel
078         *
079         */
080        public class PrintStreamProgressMonitor implements ProgressMonitor {
081
082                private PrintStream out;
083                private boolean closeStream;
084                private String indent;
085                protected int indentIncrement = 2;
086                private boolean cancelled;
087                
088                /**
089                 * Constructs a progress monitor for a given print stream.
090                 * @param out
091                 * @param indent Indent in spaces for this monitor.
092                 * @param indentIncrement Increment to add to the indent of this monitor when constructing a sub-monitor.
093                 * @param closeStream if true the monitor closes the stream in its close() method.
094                 */
095                public PrintStreamProgressMonitor(PrintStream out, int indent, int indentIncrement, boolean closeStream) {
096                        this.out = out;
097                        StringBuilder indentBuilder = new StringBuilder();
098                        for (int i = 0; i < indent; ++i) {
099                                indentBuilder.append(' ');
100                        }
101                        this.indent = indentBuilder.toString();
102                        this.closeStream = closeStream;
103                }
104                
105                /**
106                 * Constructs a progres monitor outputting to System.out with indent 0, indentIncrement 2 and not closing the stream.
107                 */
108                public PrintStreamProgressMonitor() {
109                        this(System.out, 0, 2, false);
110                }
111                
112                @Override
113                public void close() {
114                        if (closeStream) {
115                                out.close();
116                        }
117                }
118
119                @Override
120                public boolean isCancelled() {
121                        return cancelled;
122                }
123
124                @Override
125                public ProgressMonitor split(String taskName, double size, Object... data) {
126                        if (isCancelled()) {
127                                throw new CancellationException();
128                        }
129                        out.println(indent+"  "+taskName+" ("+size+")");
130                        if (data != null) {
131                                for (Object d: data) {
132                                        out.println(formatDetail(d, indent + "    "));                  
133                                }
134                        }
135                        return new PrintStreamProgressMonitor(out, indent.length()+indentIncrement, indentIncrement, false) {
136                                
137                                @Override
138                                public boolean isCancelled() {
139                                        return PrintStreamProgressMonitor.this.isCancelled();
140                                }
141                                
142                        };
143                }
144                
145                /**
146                 * Formats detail element for output. This implementation concatenates the indent with the detail.
147                 * @param detail
148                 * @param indent
149                 * @return
150                 */
151                protected String formatDetail(Object detail, String indent) {
152                        return indent + detail;
153                }
154
155                @Override
156                public void worked(Status status, double work, String progressMessage, Object... data) {
157                        out.print(indent+"  ["+status+" "+work+"] "+progressMessage);
158                        if (data.length > 0) { 
159                                out.print(": "+Arrays.deepToString(data));
160                        }
161                        out.println();
162                        if (status == Status.CANCEL) {
163                                cancelled = true;
164                        }
165                }
166
167                @Override
168                public ProgressMonitor setWorkRemaining(double size) {
169                        // NOP
170                        return this;
171                }
172
173        }       
174
175        public void execute() throws MojoExecutionException {
176                
177                ActionSiteGenerator actionSiteGenerator = new ActionSiteGenerator() {
178                        
179                        @Override
180                        protected Context createContext(ProgressMonitor progressMonitor) {
181                                DiagramGenerator diagramGenerator;
182                                if (Util.isBlank(drawioViewer)) {
183                                        diagramGenerator = DiagramGenerator.INSTANCE;
184                                } else {
185                                        diagramGenerator = new DiagramGeneratorImpl() {
186                                                
187                                                protected String getDrawioViewer() {
188                                                        return drawioViewer;
189                                                };
190                                                
191                                        };
192                                }
193                                if (diagramGenerators != null) {
194                                        for (DiagramGenerator dg: diagramGenerators) {
195                                                diagramGenerator = dg.compose(diagramGenerator);
196                                        }
197                                }
198                                return Context.singleton(DiagramGenerator.class, diagramGenerator).compose(super.createContext(progressMonitor));
199                        }
200                        
201                        @Override
202                        protected ProgressMonitor createProgressMonitor() {
203                                if (progressOutput == null) {
204                                        return new LogProgressMonitor(getLog(), 0, 2);
205                                }
206                                
207//                              if (jsonProgress) {
208//                                      // JSON output progress monitor
209//                                      
210//                              }
211                                
212                                try {
213                                        return new PrintStreamProgressMonitor(new PrintStream(progressOutput), 0, 2, true);
214                                } catch (FileNotFoundException e) {
215                                        throw new NasdanikaException(e);
216                                }
217                        }
218                        
219                };
220                
221                File baseDir = project.getBasedir();
222                URI baseDirURI = URI.createFileURI(baseDir.getAbsolutePath()).appendSegment("");
223                
224                URI actionURI = URI.createURI(action).resolve(baseDirURI);
225                URI pageTemplateURI = URI.createURI(pageTemplate).resolve(baseDirURI);
226
227                try {
228                        Map<String, Collection<String>> errors = actionSiteGenerator.generate(
229                                        actionURI, 
230                                        pageTemplateURI, 
231                                        siteMapDomain, 
232                                        outputDirectory, 
233                                        workDirectory, 
234                                        cleanWorkDir);
235                        
236                        int errorCount = 0;                     
237                        for (Entry<String, Collection<String>> ee: errors.entrySet()) {
238                                getLog().error(ee.getKey());
239                                for (String error: ee.getValue()) {
240                                        ++errorCount;
241                                        getLog().error("\t" + error);
242                                }
243                        }
244                        if (errorCount != this.errors) {
245                                throw new MojoExecutionException("There are " + errorCount + " site errors");
246                        }
247                } catch (IOException | DiagnosticException ex) {
248                        throw new MojoExecutionException(ex);
249                }
250        }
251}