View Javadoc

1   /*
2    * $Id: SnippetToPageWorker.java 2993 2011-11-24 19:51:48Z andrewinkler $
3    * ============================================================================
4    * Project gluehloch-homepage-core
5    * Copyright (c) 2004-2010 by Andre Winkler. All rights reserved.
6    * ============================================================================
7    *          GNU LESSER GENERAL PUBLIC LICENSE
8    *  TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
9    *
10   *  This library is free software; you can redistribute it and/or
11   *  modify it under the terms of the GNU Lesser General Public
12   *  License as published by the Free Software Foundation; either
13   *  version 2.1 of the License, or (at your option) any later version.
14   *
15   *  This library is distributed in the hope that it will be useful,
16   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18   *  Lesser General Public License for more details.
19   *
20   *  You should have received a copy of the GNU Lesser General Public
21   *  License along with this library; if not, write to the Free Software
22   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23   *
24   */
25  
26  package de.awtools.homegen.worker;
27  
28  import java.io.File;
29  import java.io.FileOutputStream;
30  import java.io.OutputStreamWriter;
31  import java.io.StringWriter;
32  import java.io.Writer;
33  import java.util.ArrayList;
34  import java.util.LinkedList;
35  import java.util.List;
36  
37  import org.apache.commons.io.IOUtils;
38  import org.apache.commons.lang.StringUtils;
39  import org.apache.commons.lang.Validate;
40  import org.apache.commons.logging.Log;
41  import org.apache.commons.logging.LogFactory;
42  import org.apache.velocity.Template;
43  import org.apache.velocity.VelocityContext;
44  import org.apache.velocity.app.Velocity;
45  
46  import de.awtools.basic.file.AWToolsFileUtils;
47  import de.awtools.homegen.DirectoryHolder;
48  import de.awtools.homegen.TransformerException;
49  import de.awtools.homegen.config.GHConfiguration;
50  import de.awtools.homegen.directory.FileType;
51  import de.awtools.homegen.directory.FileWorker;
52  import de.awtools.homegen.directory.GHDirectory;
53  import de.awtools.homegen.directory.GHFile;
54  import de.awtools.homegen.directory.file.FileBuilder;
55  import de.awtools.homegen.directory.snippet.SnippetHtmlFile;
56  import de.awtools.homegen.directory.utils.GHFileUtils;
57  import de.awtools.homegen.navigation.Navigation;
58  import de.awtools.homegen.velocity.ContextFactory;
59  import de.awtools.homegen.velocity.VelocityUtils;
60  
61  /**
62   * Erstellt aus einem Snippet eine HTML Datei.
63   *
64   * @version $LastChangedRevision: 2993 $ $LastChangedDate: 2011-11-24 20:51:48 +0100 (Thu, 24 Nov 2011) $
65   * @author by Andre Winkler, $LastChangedBy: andrewinkler $
66   */
67  public final class SnippetToPageWorker implements FileWorker {
68  
69      /** Der private Logger der Klasse. */
70      private static Log log = LogFactory.getLog(SnippetToPageWorker.class);
71  
72      /** Die zu verwendende Konfiguration. */
73      private final GHConfiguration configuration;
74  
75      /** Verwaltet die eingescannten Verzeichnisse. */
76      private final DirectoryHolder directoryHolder;
77  
78      /** Der Velocity Kontext. */
79      private final VelocityContext velocityContext;
80  
81      /** Eine Liste der modifizierten Dateien. */
82      private final List<GHFile> modifiedFiles;
83  
84      /** Der Worker soll seine Arbeit unbedingt durchführen. */
85      private final boolean force;
86  
87      private GHFile targetFile;
88  
89      private final FileBuilder fileBuilder = new FileBuilder();
90  
91      /**
92       * Konstruktor.
93       * 
94       * @param _config Die Konfiguration.
95       * @param _directoryHolder Die eingescannten Verzeichnisse.
96       * @param _velocityContext Der Velocity Kontext.
97       * @param _modifiedFiles Die Liste der modifizierten Dateien.
98       * @param _force Die Arbeit der Workers unbedingt durchführen.
99       */
100     public SnippetToPageWorker(final GHConfiguration _config,
101             final DirectoryHolder _directoryHolder,
102             final VelocityContext _velocityContext,
103             final List<GHFile> _modifiedFiles, final boolean _force) {
104 
105         configuration = _config;
106         directoryHolder = _directoryHolder;
107         velocityContext = _velocityContext;
108         modifiedFiles = _modifiedFiles;
109         force = _force;
110     }
111 
112     @Override
113     public boolean accept(final GHFile file) {
114         targetFile = directoryHolder.getTargetWwwDirectory().findFile(file,
115                 "html");
116 
117         boolean fileIsNewer = true;
118         if (!force) {
119             fileIsNewer = file.isNewer(targetFile);
120         }
121 
122         boolean accept = (fileIsNewer
123                 && file.getFileType().equals(FileType.XML)
124                 && !file.getName().equals("leftcontent.xml") && !file.getName()
125                 .equals("rightcontent.xml"));
126 
127         if (accept) {
128             if (log.isInfoEnabled()) {
129                 log.info("Second transformation of file '"
130                         + file.getNameAndPath() + "' at level '"
131                         + file.getLevelFromRoot() + "'.");
132             }
133 
134             configuration.getStatusChannel().sendStatus(
135                     "Second transformation of file '" + file.getNameAndPath()
136                             + "'.");
137         } else {
138             configuration.getStatusChannel().sendStatus(
139                     "Second transformation of file '" + file.getNameAndPath()
140                             + "' canceled.");
141         }
142 
143         return accept;
144     }
145 
146     @Override
147     public void apply(final GHFile file) {
148         generateHtmlFile((SnippetHtmlFile) file);
149     }
150 
151     /**
152      * Generiert aus den XML/HTML Schnipseln komplette HTML Seiten. Hier wird
153      * der relative Pfad zum Projektwurzelverzeichnis ermittelt. Dieser ist
154      * wichtig, um die korrekten Pfade z.B. für das Menü zu ermitteln.
155      * 
156      * @param snippetHtmlFile Der XML/HTML Schnipsel.
157      */
158     private void generateHtmlFile(final SnippetHtmlFile snippetHtmlFile) {
159         // Individuelle Seitentitel.
160         StringBuilder title = new StringBuilder();
161         Navigation navigation = (Navigation) velocityContext.get("navigation");
162         title.append(navigation.getHeader().getTitle());
163         if (!StringUtils.isBlank(snippetHtmlFile.getTitle())) {
164             title.append(" - ");
165             title.append(snippetHtmlFile.getTitle());
166         }
167 
168         ArrayList<String> cssFiles = new ArrayList<String>();
169         for (String file : snippetHtmlFile.getCSSFiles()) {
170             cssFiles.add(file);
171         }
172 
173         // Setzt den relativen Pfad zum Root-Verzeichnis.
174         velocityContext.put(ContextFactory.ROOT_DIR,
175                 AWToolsFileUtils.pathToRoot(snippetHtmlFile.getLevelFromRoot()));
176         velocityContext.put("centerContent",
177                 snippetHtmlFile.getBody(configuration.getEncoding()));
178         velocityContext.put("headline", navigation.getHeader().getHeadline());
179         velocityContext.put("cssNavigations", navigation.getHeader()
180                 .getCssFiles());
181         velocityContext.put("cssImports", cssFiles);
182         velocityContext.put("title", title.toString());
183 
184         if (log.isDebugEnabled()) {
185             log.debug(">>> BODY >>> "
186                     + snippetHtmlFile.getBody(configuration.getEncoding()));
187             log.debug(snippetHtmlFile.getBody(configuration.getEncoding()));
188         }
189 
190         //
191         // Erstellt das Menü (Die Menüpfade werden relativ zu dem aktuellen
192         // Arbeitsverzeichnis angepasst.
193         //
194         String leftMenu = VelocityUtils.toString(velocityContext,
195                 configuration.getVelocityTemplatePath() + "/menuLinks.vm",
196                 configuration.getEncoding());
197         velocityContext.put("leftmenu", leftMenu);
198 
199         //
200         // Erstellt den Header. Die Pfade auf die Bilder des Headers sind
201         // seitenspezifisch.
202         //
203         String header = VelocityUtils.toString(velocityContext,
204                 configuration.getVelocityTemplatePath() + "/header.vm",
205                 configuration.getEncoding());
206         velocityContext.put("header", header);
207 
208         String headerNavigation = VelocityUtils.toString(velocityContext,
209                 configuration.getVelocityTemplatePath() + "/headernavigation.vm",
210                 configuration.getEncoding());
211         velocityContext.put("headernavigation", headerNavigation);
212 
213         setContent(velocityContext, "leftContent",
214                 snippetHtmlFile.getDirectory(), "leftcontent.xml");
215         setContent(velocityContext, "rightContent",
216                 snippetHtmlFile.getDirectory(), "rightcontent.xml");
217 
218         addFrameToContent(snippetHtmlFile);
219     }
220 
221     /**
222      * Der HTML Schnipsel wird um einen Rahmen ergänzt.
223      * 
224      * @param snippetHtmlFile Der HTML Schnipsel.
225      */
226     private void addFrameToContent(final SnippetHtmlFile snippetHtmlFile) {
227         Writer writer = null;
228         try {
229             String htmlFileName = StringUtils.replace(
230                     snippetHtmlFile.getNameAndPath(), ".xml", ".html");
231 
232             GHDirectory htmlDirectory = GHFileUtils.createPath(
233                     snippetHtmlFile.getDirectoryPath(),
234                     directoryHolder.getTargetWwwDirectory(), fileBuilder);
235 
236             File htmlFile = new File(directoryHolder.getTargetWwwDirectory()
237                     .getFile().getAbsoluteFile()
238                     + "/" + htmlFileName);
239 
240             FileOutputStream fos = new FileOutputStream(htmlFile);
241             writer = new OutputStreamWriter(fos, configuration.getEncoding());
242 
243             GHFile htmlGHFile = fileBuilder.buildFile(htmlFile, htmlDirectory,
244                     "/" + htmlFile.getName());
245 
246             if (htmlDirectory.findFile(htmlGHFile) == null) {
247                 htmlDirectory.addFile(htmlGHFile);
248             }
249             modifiedFiles.add(htmlGHFile);
250 
251             Template template = Velocity.getTemplate(
252                     configuration.getVelocityTemplatePath() + "/frame.vm",
253                     configuration.getEncoding());
254             template.merge(velocityContext, writer);
255 
256             if (log.isDebugEnabled()) {
257                 StringWriter sw = new StringWriter();
258                 template.merge(velocityContext, sw);
259                 log.debug(">>> KONTEXT >>> " + htmlFileName);
260                 log.debug(velocityContext.get("centerContent"));
261                 log.debug(sw.getBuffer().toString());
262             }
263         } catch (Exception ex) {
264             throw new TransformerException(ex);
265         } finally {
266             IOUtils.closeQuietly(writer);
267         }
268     }
269 
270     /**
271      * Sammelt verzeichnisweise Informationen zusammen, der anschliessend im
272      * Velocity Kontext abgelegt wird. I.A. wird dieser Mechanismus für die
273      * linke, sowie für die rechte Spalte der generierten Homepage verwendet.
274      * 
275      * @param context Ein Velocity Kontext.
276      * @param contextKey Der Schlüssel unter dem der Inhalt angelegt werden soll.
277      * @param path Das zu untersuchende Verzeichnis.
278      * @param fileName Der in Frage kommende Dateiname.
279      */
280     private void setContent(final VelocityContext context,
281             final String contextKey, final GHDirectory path,
282             final String fileName) {
283 
284         Validate.isTrue(!(StringUtils.isBlank(fileName)));
285         List<SnippetHtmlFile> files = findSameFileNames(path, fileName);
286 
287         if (log.isDebugEnabled()) {
288             log.debug("Base......:"
289                     + directoryHolder.getTargetWwwPrepDirectory().getFile()
290                             .getAbsolutePath());
291             log.debug("Pfad......:" + path);
292             log.debug("FileName..:" + fileName);
293         }
294 
295         StringBuffer buf = new StringBuffer();
296         for (SnippetHtmlFile snippetHtmlFile : files) {
297             buf.append(snippetHtmlFile.getBody(configuration.getEncoding()));
298         }
299 
300         context.put(contextKey, buf);
301     }
302 
303     /**
304      * Startet die Suche nach {@link SnippetHtmlFile}s, die zwischen dem
305      * aktuellen <code>path</code> und dem Wurzelverzeichnis liegen.
306      * 
307      * @param path Das zu untersuchende Verzeichnis.
308      * @param fileName Der gesuchte Dateiname.
309      * @return Eine Liste der gefundenen Dateien.
310      */
311     private List<SnippetHtmlFile> findSameFileNames(final GHDirectory path,
312             final String fileName) {
313 
314         List<SnippetHtmlFile> results = new LinkedList<SnippetHtmlFile>();
315 
316         for (GHFile ghFile : path.getFiles()) {
317             SnippetHtmlFile snippetHtmlFile = (SnippetHtmlFile) ghFile;
318 
319             // TODO Bug '/' - Dateinamen beginnen alle mit '/'.
320             String findMeFileName = snippetHtmlFile.getName();
321             if (snippetHtmlFile.getName().startsWith("/")) {
322                 findMeFileName = StringUtils.removeStart(
323                         snippetHtmlFile.getName(), "/");
324             }
325 
326             if (findMeFileName.equals(fileName)) {
327                 results.add(snippetHtmlFile);
328                 break;
329             }
330         }
331 
332         if (path.getParent() != null) {
333             results.addAll(findSameFileNames(path.getParent(), fileName));
334         }
335 
336         return results;
337     }
338 
339 }