001: /*
002: * This file is part of PFIXCORE.
003: *
004: * PFIXCORE is free software; you can redistribute it and/or modify
005: * it under the terms of the GNU Lesser General Public License as published by
006: * the Free Software Foundation; either version 2 of the License, or
007: * (at your option) any later version.
008: *
009: * PFIXCORE is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public License
015: * along with PFIXCORE; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018:
019: package de.schlund.pfixcore.editor2.core.spring.internal;
020:
021: import java.util.Collection;
022: import java.util.HashMap;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.LinkedHashSet;
026: import java.util.Map;
027: import java.util.TreeSet;
028:
029: import org.apache.log4j.Logger;
030:
031: import de.schlund.pfixcore.editor2.core.dom.AbstractProject;
032: import de.schlund.pfixcore.editor2.core.dom.Image;
033: import de.schlund.pfixcore.editor2.core.dom.IncludePartThemeVariant;
034: import de.schlund.pfixcore.editor2.core.dom.Page;
035: import de.schlund.pfixcore.editor2.core.dom.Target;
036: import de.schlund.pfixcore.editor2.core.dom.ThemeList;
037: import de.schlund.pfixcore.editor2.core.dom.Variant;
038: import de.schlund.pfixcore.editor2.core.exception.EditorInitializationException;
039: import de.schlund.pfixcore.editor2.core.exception.EditorParsingException;
040: import de.schlund.pfixcore.editor2.core.spring.ImageFactoryService;
041: import de.schlund.pfixcore.editor2.core.spring.IncludeFactoryService;
042: import de.schlund.pfixcore.editor2.core.spring.PageFactoryService;
043: import de.schlund.pfixcore.editor2.core.spring.PustefixTargetUpdateService;
044: import de.schlund.pfixcore.editor2.core.spring.TargetFactoryService;
045: import de.schlund.pfixcore.editor2.core.spring.ThemeFactoryService;
046: import de.schlund.pfixcore.editor2.core.spring.VariantFactoryService;
047: import de.schlund.pfixcore.workflow.Navigation;
048: import de.schlund.pfixcore.workflow.NavigationFactory;
049: import de.schlund.pfixcore.workflow.Navigation.NavigationElement;
050: import de.schlund.pfixxml.event.ConfigurationChangeEvent;
051: import de.schlund.pfixxml.event.ConfigurationChangeListener;
052: import de.schlund.pfixxml.resources.FileResource;
053: import de.schlund.pfixxml.resources.ResourceUtil;
054: import de.schlund.pfixxml.targets.AuxDependency;
055: import de.schlund.pfixxml.targets.AuxDependencyFactory;
056: import de.schlund.pfixxml.targets.AuxDependencyImage;
057: import de.schlund.pfixxml.targets.DependencyType;
058: import de.schlund.pfixxml.targets.PageInfo;
059: import de.schlund.pfixxml.targets.PageTargetTree;
060: import de.schlund.pfixxml.targets.TargetDependencyRelation;
061: import de.schlund.pfixxml.targets.TargetGenerator;
062: import de.schlund.pfixxml.targets.TargetGeneratorFactory;
063: import de.schlund.pfixxml.targets.Themes;
064:
065: /**
066: * Implementation of Project using a XML file to read project information during
067: * construction.
068: *
069: * @author Sebastian Marsching <sebastian.marsching@1und1.de>
070: */
071: public class ProjectImpl extends AbstractProject {
072: private String projectName;
073:
074: private String projectComment;
075:
076: private VariantFactoryService variantfactory;
077:
078: private ThemeFactoryService themefactory;
079:
080: private PageFactoryService pagefactory;
081:
082: private Collection<Page> toppages;
083:
084: private Collection<Page> allpages;
085:
086: private Map<String, Map<Variant, Page>> pagemap;
087:
088: private TargetGenerator tgen;
089:
090: private IncludeFactoryService includefactory;
091:
092: private ImageFactoryService imagefactory;
093:
094: private TargetFactoryService targetfactory;
095:
096: private FileResource dependPath;
097:
098: /**
099: * Creates a new Project object
100: *
101: * @param pathresolver
102: * Reference to PathResolverService
103: * @param name
104: * Name of the project to create
105: * @param dependFile
106: * "depend.xml" file to read project configuration from
107: * @throws EditorInitializationException
108: * if navigation information cannot be loaded
109: */
110: public ProjectImpl(VariantFactoryService variantfactory,
111: ThemeFactoryService themefactory,
112: PageFactoryService pagefactory,
113: IncludeFactoryService includefactory,
114: ImageFactoryService imagefactory,
115: TargetFactoryService targetfactory,
116: PustefixTargetUpdateService updater, String name,
117: String comment, String dependFile)
118: throws EditorInitializationException {
119: this .projectName = name;
120: this .projectComment = comment;
121: this .variantfactory = variantfactory;
122: this .themefactory = themefactory;
123: this .pagefactory = pagefactory;
124: this .includefactory = includefactory;
125: this .imagefactory = imagefactory;
126: this .targetfactory = targetfactory;
127:
128: this .dependPath = ResourceUtil
129: .getFileResourceFromDocroot(dependFile);
130:
131: TargetGenerator gen;
132: try {
133: gen = TargetGeneratorFactory.getInstance().createGenerator(
134: this .dependPath);
135: } catch (Exception e) {
136: String err = "Cannot create TargetGenerator for project "
137: + name + "!";
138: Logger.getLogger(this .getClass()).error(err, e);
139: throw new EditorInitializationException(err, e);
140: }
141: this .tgen = gen;
142:
143: // Register target generator for updates
144: updater.registerTargetGeneratorForUpdateLoop(tgen);
145:
146: // Register event listener to be informed about changes
147: // in TargetGenerator
148:
149: this .tgen.addListener(new ConfigurationChangeListener() {
150:
151: public void configurationChanged(
152: ConfigurationChangeEvent event) {
153: reloadConfig();
154:
155: }
156:
157: });
158:
159: this .pagemap = new HashMap<String, Map<Variant, Page>>();
160:
161: // Load configuration
162: this .reloadConfig();
163: }
164:
165: private synchronized void reloadConfig() {
166: Navigation navi = this .getNavigation();
167: TargetGenerator gen = this .getTargetGenerator();
168:
169: // Create hierarchical tree of pages
170: PageTargetTree ptree = gen.getPageTargetTree();
171: HashSet<Page> pages = new HashSet<Page>();
172: NavigationElement[] navElements = navi.getNavigationElements();
173: for (int i = 0; i < navElements.length; i++) {
174: pages.addAll(this .recurseNavigationElement(navElements[i],
175: null, ptree));
176: }
177:
178: // Create pagename => page map
179: HashMap<String, Map<Variant, Page>> pagemap = new HashMap<String, Map<Variant, Page>>();
180: for (Iterator<Page> i = pages.iterator(); i.hasNext();) {
181: Page page = i.next();
182: this .recursePage(page, pagemap);
183: }
184:
185: // Create collection containing all page objects
186: HashSet<Page> allpages = new HashSet<Page>();
187: for (Iterator<Map<Variant, Page>> i = pagemap.values()
188: .iterator(); i.hasNext();) {
189: Map<Variant, Page> map = i.next();
190: allpages.addAll(map.values());
191: }
192:
193: // Add pages from target definitions which are not present in navigation tree
194: for (PageInfo pinfo : gen.getPageTargetTree().getPageInfos()) {
195: String pageName = pinfo.getName();
196: String pageHandler = "none";
197: String variantName = pinfo.getVariant();
198: Variant pageVariant;
199: if (variantName != null) {
200: pageVariant = this .variantfactory
201: .getVariant(variantName);
202: } else {
203: pageVariant = null;
204: }
205: ThemeList pageThemes = new ThemeListImpl(this .themefactory,
206: ptree.getTargetForPageInfo(pinfo).getThemes());
207: if (!pagemap.containsKey(pageName)
208: || !pagemap.get(pageName).containsKey(pageVariant)) {
209: // Create new page only if there has not been a page
210: // with the same name and same variant before
211: MutablePage page;
212: page = this .pagefactory.getMutablePage(pageName,
213: pageVariant, pageHandler, pageThemes, this );
214: page.setHandlerPath(pageHandler);
215: pages.add(page);
216: this .recursePage(page, pagemap);
217: allpages.add(page);
218: }
219: }
220:
221: this .toppages = pages;
222: this .pagemap = pagemap;
223: this .allpages = allpages;
224:
225: }
226:
227: /**
228: * Recurses over a page element adding the page and its subpages to the map
229: *
230: * @param page
231: * Page object to recurse over
232: * @param allpages
233: * Map to add page objects to
234: */
235: private void recursePage(Page page,
236: HashMap<String, Map<Variant, Page>> allpages) {
237: Map<Variant, Page> pagetable;
238: if (allpages.containsKey(page.getName())) {
239: pagetable = allpages.get(page.getName());
240: } else {
241: pagetable = new HashMap<Variant, Page>();
242: allpages.put(page.getName(), pagetable);
243: }
244: pagetable.put(page.getVariant(), page);
245: for (Iterator<Page> i = page.getSubPages().iterator(); i
246: .hasNext();) {
247: Page page2 = i.next();
248: this .recursePage(page2, allpages);
249: }
250: }
251:
252: /**
253: * Recurses over NavigationElement object to retrieve the navigation
254: * structure of the page tree
255: *
256: * @param nav
257: * Element to recurse over
258: * @param parent
259: * Parent page object - needed to create new page objects
260: * @param ptree
261: * Tree of navigation objects used by the Pustefix generator
262: * @return Collection containing page objects for all variants of the page
263: * specified by the NavigationElement
264: */
265: private Collection<Page> recurseNavigationElement(
266: NavigationElement nav, Page parent, PageTargetTree ptree) {
267: HashSet<MutablePage> pages = new HashSet<MutablePage>();
268: Page defaultPage = null;
269:
270: String pageName = nav.getName();
271: String pageHandler = nav.getHandler();
272: Collection<PageInfo> pinfos = ptree
273: .getPageInfoForPageName(pageName);
274: if (pinfos == null) {
275: String msg = "Could not load PageInfo from PageTree for page "
276: + pageName + "! No target for page defined?";
277: Logger.getLogger(this .getClass()).warn(msg);
278: } else {
279: for (Iterator<PageInfo> iter = pinfos.iterator(); iter
280: .hasNext();) {
281: PageInfo pinfo = iter.next();
282: String variantName = pinfo.getVariant();
283: Variant pageVariant;
284: if (variantName != null) {
285: pageVariant = this .variantfactory
286: .getVariant(variantName);
287: } else {
288: pageVariant = null;
289: }
290: ThemeList pageThemes = new ThemeListImpl(
291: this .themefactory, ptree.getTargetForPageInfo(
292: pinfo).getThemes());
293: MutablePage page = (MutablePage) this .getPage(pageName,
294: pageVariant);
295: if (page == null) {
296: // Create new page only if there has not been a page
297: // with the same name and same variant before
298: page = this .pagefactory.getMutablePage(pageName,
299: pageVariant, pageHandler, pageThemes, this );
300: }
301: page.setHandlerPath(pageHandler);
302: pages.add(page);
303: if (pageVariant == null) {
304: defaultPage = page;
305: }
306: }
307: }
308:
309: // make sure a default page exists, even if there is no target
310: // otherwise, subpages cannot not be handled correctly
311: if (defaultPage == null) {
312: MutablePage page;
313: page = this .pagefactory.getMutablePage(pageName, null,
314: pageHandler, new ThemeListImpl(this .themefactory,
315: new Themes("default")), this );
316: page.setHandlerPath(pageHandler);
317: pages.add(page);
318: defaultPage = page;
319: }
320:
321: HashSet<Page> subpages = new HashSet<Page>();
322:
323: if (nav.hasChildren()) {
324: NavigationElement[] elements = nav.getChildren();
325: for (int i = 0; i < elements.length; i++) {
326: Collection<Page> subpageElements = this
327: .recurseNavigationElement(elements[i],
328: defaultPage, ptree);
329: subpages.addAll(subpageElements);
330: }
331: }
332:
333: for (Iterator<MutablePage> iter = pages.iterator(); iter
334: .hasNext();) {
335: MutablePage page = iter.next();
336: if (page == null) {
337: String err = "Page returned by iteration is null!";
338: Logger.getLogger(this .getClass()).error(err);
339: }
340: page.setSubPages(subpages);
341: }
342:
343: return new LinkedHashSet<Page>(pages);
344: }
345:
346: /*
347: * (non-Javadoc)
348: *
349: * @see de.schlund.pfixcore.editor2.core.dom.Project#getName()
350: */
351: public String getName() {
352: return this .projectName;
353: }
354:
355: public String getComment() {
356: return this .projectComment;
357: }
358:
359: /*
360: * (non-Javadoc)
361: *
362: * @see de.schlund.pfixcore.editor2.core.dom.Project#getAllPages()
363: */
364: public Collection<Page> getAllPages() {
365: return new HashSet<Page>(this .allpages);
366: }
367:
368: /*
369: * (non-Javadoc)
370: *
371: * @see de.schlund.pfixcore.editor2.core.dom.Project#getTopPages()
372: */
373: public Collection<Page> getTopPages() {
374: return new HashSet<Page>(this .toppages);
375: }
376:
377: /*
378: * (non-Javadoc)
379: *
380: * @see de.schlund.pfixcore.editor2.core.dom.Project#getPage(java.lang.String,
381: * de.schlund.pfixcore.editor2.core.dom.Variant)
382: */
383: public Page getPage(String pagename, Variant variant) {
384: if (!this .pagemap.containsKey(pagename)) {
385: return null;
386: } else {
387: return this .pagemap.get(pagename).get(variant);
388: }
389: }
390:
391: /*
392: * (non-Javadoc)
393: *
394: * @see de.schlund.pfixcore.editor2.core.dom.Project#getPageByName(java.lang.String)
395: */
396: public Collection<Page> getPageByName(String name) {
397: if (!this .pagemap.containsKey(name)) {
398: return new HashSet<Page>();
399: } else {
400: return new HashSet<Page>(this .pagemap.get(name).values());
401: }
402: }
403:
404: public Target getTarget(String name) {
405: de.schlund.pfixxml.targets.Target pfixTarget = this .tgen
406: .getTarget(name);
407: if (pfixTarget != null) {
408: return this .targetfactory.getTargetFromPustefixTarget(
409: pfixTarget, this );
410: }
411: return null;
412: }
413:
414: public TargetGenerator getTargetGenerator() {
415: return this .tgen;
416: }
417:
418: private Navigation getNavigation() {
419: try {
420: return NavigationFactory.getInstance().getNavigation(
421: this .dependPath, tgen.getXsltVersion());
422: } catch (Exception e) {
423: throw new RuntimeException(
424: "Could not get navigation object for prokec \""
425: + this .getName() + "\"!");
426: }
427: }
428:
429: public Collection<IncludePartThemeVariant> getAllIncludeParts() {
430: HashSet<IncludePartThemeVariant> includes = new HashSet<IncludePartThemeVariant>();
431: TreeSet<AuxDependency> deps = TargetDependencyRelation
432: .getInstance().getProjectDependenciesForType(this .tgen,
433: DependencyType.TEXT);
434: if (deps == null) {
435: return includes;
436: }
437:
438: for (Iterator<AuxDependency> i = deps.iterator(); i.hasNext();) {
439: AuxDependency auxdep = i.next();
440: try {
441: includes.add(this .includefactory
442: .getIncludePartThemeVariant(auxdep));
443: } catch (EditorParsingException e) {
444: // Ignore exception and go on
445: }
446: }
447: return includes;
448: }
449:
450: public Collection<Image> getAllImages() {
451: HashSet<Image> images = new HashSet<Image>();
452: TreeSet<AuxDependency> deps = TargetDependencyRelation
453: .getInstance().getProjectDependenciesForType(this .tgen,
454: DependencyType.IMAGE);
455: if (deps == null) {
456: return images;
457: }
458: for (Iterator<AuxDependency> i = deps.iterator(); i.hasNext();) {
459: AuxDependencyImage auxdep = (AuxDependencyImage) i.next();
460: images.add(this .imagefactory.getImage(auxdep.getPath()
461: .getRelativePath()));
462: }
463: return images;
464: }
465:
466: public IncludePartThemeVariant findIncludePartThemeVariant(
467: String file, String part, String theme) {
468: AuxDependency auxdep = AuxDependencyFactory.getInstance()
469: .getAuxDependencyInclude(
470: ResourceUtil.getFileResourceFromDocroot(file),
471: part, theme);
472:
473: TreeSet<AuxDependency> deps = TargetDependencyRelation
474: .getInstance().getProjectDependencies(tgen);
475: if (deps == null) {
476: return null;
477: }
478:
479: if (deps.contains(auxdep)) {
480: try {
481: return this .includefactory
482: .getIncludePartThemeVariant(auxdep);
483: } catch (EditorParsingException e) {
484: String msg = "Failed to get include part " + part + ":"
485: + theme + "@" + file + "!";
486: Logger.getLogger(this .getClass()).warn(msg, e);
487: return null;
488: }
489: } else {
490: return null;
491: }
492: }
493:
494: public boolean hasIncludePart(String file, String part, String theme) {
495: AuxDependency aux = AuxDependencyFactory.getInstance()
496: .getAuxDependencyInclude(
497: ResourceUtil.getFileResourceFromDocroot(file),
498: part, theme);
499: TreeSet<TargetGenerator> generators = TargetDependencyRelation
500: .getInstance().getAffectedTargetGenerators(aux);
501: if (generators == null) {
502: return false;
503: }
504:
505: return generators.contains(this.tgen);
506: }
507:
508: }
|