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