raimund@3295: package de.intevation.flys.exports;
raimund@3295: 
raimund@3295: import java.io.IOException;
raimund@3295: import java.util.ArrayList;
weinzierl@4255: import java.util.Collections;
raimund@3295: import java.util.HashMap;
raimund@3295: import java.util.List;
raimund@3295: import java.util.Map;
raimund@3295: 
raimund@3295: import javax.xml.xpath.XPathConstants;
raimund@3295: 
raimund@3295: import org.apache.log4j.Logger;
raimund@3295: import org.w3c.dom.Document;
raimund@3295: import org.w3c.dom.Element;
raimund@3295: import org.w3c.dom.Node;
raimund@3295: import org.w3c.dom.NodeList;
raimund@3295: 
raimund@3295: import de.intevation.artifactdatabase.Backend;
raimund@3295: import de.intevation.artifactdatabase.Backend.PersistentArtifact;
raimund@3295: import de.intevation.artifactdatabase.state.ArtifactAndFacet;
raimund@3295: import de.intevation.artifacts.Artifact;
raimund@3295: import de.intevation.artifacts.ArtifactDatabase;
raimund@3295: import de.intevation.artifacts.ArtifactDatabaseException;
raimund@3295: import de.intevation.artifacts.CallContext;
raimund@3295: import de.intevation.artifacts.CallMeta;
raimund@3295: import de.intevation.artifacts.common.ArtifactNamespaceContext;
raimund@3295: import de.intevation.artifacts.common.utils.ClientProtocolUtils;
raimund@3295: import de.intevation.artifacts.common.utils.XMLUtils;
raimund@3295: import de.intevation.flys.artifacts.FLYSArtifact;
raimund@3295: import de.intevation.flys.artifacts.context.FLYSContext;
raimund@3295: import de.intevation.flys.artifacts.model.ManagedDomFacet;
raimund@3295: import de.intevation.flys.artifacts.model.ManagedFacet;
raimund@3295: import de.intevation.flys.themes.Theme;
raimund@3295: import de.intevation.flys.themes.ThemeFactory;
raimund@3295: 
raimund@3295: public class OutputHelper {
raimund@3295:     /** The logger used in this class. */
raimund@3295:     private static Logger log = Logger.getLogger(OutputHelper.class);
raimund@3295: 
raimund@3295:     protected String identifier;
raimund@3295: 
raimund@3295:     public OutputHelper(String identifier) {
raimund@3295:         this.identifier = identifier;
raimund@3295:     }
raimund@3295:     /**
raimund@3295:      * Creates a concrete output.
raimund@3295:      *
raimund@3295:      * @param generator The OutGenerator that creates the output.
raimund@3295:      * @param outputName The name of the requested output.
raimund@3295:      * @param attributes The collection's attributes for this concrete output
raimund@3295:      * type.
raimund@3295:      * @param context The context object.
raimund@3295:      */
raimund@3295:     public void doOut(
raimund@3295:         OutGenerator generator,
raimund@3295:         String       outName,
raimund@3295:         String       facet,
raimund@3295:         Document     attributes,
raimund@3295:         CallContext  context)
raimund@3295:     throws IOException
raimund@3295:     {
raimund@3295:         boolean debug = log.isDebugEnabled();
raimund@3295: 
raimund@3295:         if (debug) {
raimund@3295:             log.debug("FLYSArtifactCollection.doOut: " + outName);
raimund@3295:         }
raimund@3295: 
raimund@3295:         ThemeList themeList = new ThemeList(attributes);
raimund@3295: 
raimund@3295:         int size = themeList.size();
raimund@3295:         if (debug) {
raimund@3295:             log.debug("Output will contain " + size + " elements.");
raimund@3295:         }
raimund@3295: 
raimund@3295:         List<ArtifactAndFacet> dataProviders =
raimund@3295:             doBlackboardPass(themeList, context);
raimund@3295: 
raimund@3295:         try {
raimund@3295:             for (int i = 0; i < size; i++) {
raimund@3295:                 ManagedFacet theme = themeList.get(i);
raimund@3295: 
raimund@3295:                 if (theme == null) {
raimund@3295:                     log.debug("Theme is empty - no output is generated.");
raimund@3295:                     continue;
raimund@3295:                 }
raimund@3295: 
raimund@3295:                 String art = theme.getArtifact();
raimund@3295:                 String facetName = theme.getName();
raimund@3295: 
raimund@3295:                 if (debug) {
raimund@3295:                     log.debug("Do output for...");
raimund@3295:                     log.debug("... artifact: " + art);
raimund@3295:                     log.debug("... facet: " + facetName);
raimund@3295:                 }
raimund@3295: 
raimund@3295:                 if (outName.equals("export") && !facetName.equals(facet)) {
raimund@3295:                     continue;
raimund@3295:                 }
raimund@3295: 
raimund@3295:                 // Skip invisible themes.
raimund@3295:                 if (theme.getVisible() == 0) {
raimund@3295:                     continue;
raimund@3295:                 }
raimund@3295: 
raimund@3295:                 if (outName.equals("sq_overview")) {
raimund@3295:                     generator.doOut(
raimund@3295:                         dataProviders.get(i),
raimund@3295:                         attributes,
raimund@3295:                         theme.getActive() == 1);
raimund@3295:                 }
raimund@3295:                 else {
raimund@3295:                     generator.doOut(
raimund@3295:                             dataProviders.get(i),
raimund@3295:                             getFacetThemeFromAttribute(
raimund@3295:                                 art,
raimund@3295:                                 outName,
raimund@3295:                                 facetName,
raimund@3295:                                 theme.getDescription(),
raimund@3295:                                 theme.getIndex(),
raimund@3295:                                 context),
raimund@3295:                             theme.getActive() == 1);
raimund@3295:                 }
raimund@3295:             }
raimund@3295:         }
raimund@3295:         catch (ArtifactDatabaseException ade) {
raimund@3295:             log.error(ade, ade);
raimund@3295:         }
raimund@3295:     }
raimund@3295:     /**
raimund@3295:      * Returns the attribute that belongs to an artifact and facet stored in
raimund@3295:      * this collection.
raimund@3295:      *
raimund@3295:      * @param uuid The Artifact's uuid.
raimund@3295:      * @param outname The name of the requested output.
raimund@3295:      * @param facet The name of the requested facet.
raimund@3295:      * @param context The CallContext.
raimund@3295:      *
raimund@3295:      * @return an attribute in form of a document.
raimund@3295:      */
raimund@3295:     protected Document getFacetThemeFromAttribute(
raimund@3295:         String      uuid,
raimund@3295:         String      outName,
raimund@3295:         String      facet,
raimund@3295:         String      pattern,
raimund@3295:         int         index,
raimund@3295:         CallContext context)
raimund@3295:     throws    ArtifactDatabaseException
raimund@3295:     {
raimund@3295:         boolean debug = log.isDebugEnabled();
raimund@3295: 
raimund@3295:         if (debug) {
raimund@3295:             log.debug(
raimund@3295:                 "FLYSArtifactCollection.getFacetThemeFromAttribute(facet="
raimund@3295:                 + facet + ", index=" + index + ")");
raimund@3295:         }
raimund@3295: 
raimund@3295:         ArtifactDatabase db = context.getDatabase();
raimund@3295:         CallMeta       meta = context.getMeta();
raimund@3295: 
raimund@3295:         Document attr = db.getCollectionItemAttribute(identifier, uuid, meta);
raimund@3295: 
raimund@3295:         if (attr == null) {
raimund@3295:             attr = initItemAttribute(uuid, facet, pattern, index, outName, context);
raimund@3295: 
raimund@3295:             if (attr == null) {
raimund@3295:                 return null;
raimund@3295:             }
raimund@3295:         }
raimund@3295: 
raimund@3295:         if (debug) {
raimund@3295:             log.debug("Search attribute of collection item: " + uuid);
raimund@3295:         }
raimund@3295: 
raimund@3295:         Node tmp = (Node) XMLUtils.xpath(
raimund@3295:             attr,
raimund@3295:             "/art:attribute",
raimund@3295:             XPathConstants.NODE,
raimund@3295:             ArtifactNamespaceContext.INSTANCE);
raimund@3295: 
raimund@3295:         if (tmp == null) {
raimund@3295:             log.warn("No attribute found. Operation failed.");
raimund@3295:             return null;
raimund@3295:         }
raimund@3295: 
raimund@3295:         if (debug) {
raimund@3295:             log.debug("Search theme for facet '" + facet + "' in attribute.");
raimund@3295:         }
raimund@3295: 
raimund@3295:         Map<String, String> vars = new HashMap<String, String>();
raimund@3295:         vars.put("facet", facet);
raimund@3295:         vars.put("index", String.valueOf(index));
raimund@3295: 
raimund@3295:         Node theme = (Node) XMLUtils.xpath(
raimund@3295:             tmp,
raimund@3295:             "art:themes/theme[@facet=$facet and @index=$index]",
raimund@3295:             XPathConstants.NODE,
raimund@3295:             ArtifactNamespaceContext.INSTANCE,
raimund@3295:             vars);
raimund@3295: 
raimund@3295:         if (theme == null) {
raimund@3295:             log.warn("Could not find the theme in attribute of: " + facet + " " + uuid);
raimund@3295: 
raimund@3295:             Theme t = getThemeForFacet(
raimund@3295:                 uuid, facet, pattern, index, outName, context);
raimund@3295: 
raimund@3295:             if (t == null) {
raimund@3295:                 log.warn("No theme found for facet: " + facet);
raimund@3295:                 return null;
raimund@3295:             }
raimund@3295: 
raimund@3295:             addThemeToAttribute(uuid, attr, t, context);
raimund@3295:             theme = t.toXML().getFirstChild();
raimund@3295:         }
raimund@3295: 
raimund@3295:         Document doc = XMLUtils.newDocument();
raimund@3295:         doc.appendChild(doc.importNode(theme, true));
raimund@3295: 
raimund@3295:         return doc;
raimund@3295:     }
raimund@3295:     /**
raimund@3295:      * Adds the theme of a facet to a CollectionItem's attribute.
raimund@3295:      *
raimund@3295:      * @param uuid The uuid of the artifact.
raimund@3295:      * @param attr The current attribute of an artifact.
raimund@3295:      * @param t The theme to add.
raimund@3295:      * @param context The CallContext.
raimund@3295:      */
raimund@3295:     protected void addThemeToAttribute(
raimund@3295:         String      uuid,
raimund@3295:         Document    attr,
raimund@3295:         Theme       t,
raimund@3295:         CallContext context)
raimund@3295:     {
raimund@3295:         log.debug("FLYSArtifactCollection.addThemeToAttribute: " + uuid);
raimund@3295: 
raimund@3295:         if (t == null) {
raimund@3295:             log.warn("Theme is empty - cancel adding it to attribute!");
raimund@3295:             return;
raimund@3295:         }
raimund@3295: 
raimund@3295:         XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
raimund@3295:             attr,
raimund@3295:             ArtifactNamespaceContext.NAMESPACE_URI,
raimund@3295:             ArtifactNamespaceContext.NAMESPACE_PREFIX);
raimund@3295: 
raimund@3295:         Node tmp = (Node) XMLUtils.xpath(
raimund@3295:             attr,
raimund@3295:             "/art:attribute",
raimund@3295:             XPathConstants.NODE,
raimund@3295:             ArtifactNamespaceContext.INSTANCE);
raimund@3295: 
raimund@3295:         if (tmp == null) {
raimund@3295:             tmp = ec.create("attribute");
raimund@3295:             attr.appendChild(tmp);
raimund@3295:         }
raimund@3295: 
raimund@3295:         Node themes = (Node) XMLUtils.xpath(
raimund@3295:             tmp,
raimund@3295:             "art:themes",
raimund@3295:             XPathConstants.NODE,
raimund@3295:             ArtifactNamespaceContext.INSTANCE);
raimund@3295: 
raimund@3295:         if (themes == null) {
raimund@3295:             themes = ec.create("themes");
raimund@3295:             tmp.appendChild(themes);
raimund@3295:         }
raimund@3295: 
raimund@3295:         themes.appendChild(attr.importNode(t.toXML().getFirstChild(), true));
raimund@3295: 
raimund@3295:         try {
raimund@3295:             setCollectionItemAttribute(uuid, attr, context);
raimund@3295:         }
raimund@3295:         catch (ArtifactDatabaseException e) {
raimund@3295:             // do nothing
raimund@3295:             log.warn("Cannot set attribute of item: " + uuid);
raimund@3295:         }
raimund@3295:     }
raimund@3295: 
raimund@3295:     /**
raimund@3295:      * Sets the attribute of a CollectionItem specified by <i>uuid</i> to a new
raimund@3295:      * value <i>attr</i>.
raimund@3295:      *
raimund@3295:      * @param uuid The uuid of the CollectionItem.
raimund@3295:      * @param attr The new attribute for the CollectionItem.
raimund@3295:      * @param context The CallContext.
raimund@3295:      */
raimund@3295:     public void setCollectionItemAttribute(
raimund@3295:         String      uuid,
raimund@3295:         Document    attr,
raimund@3295:         CallContext context)
raimund@3295:     throws ArtifactDatabaseException
raimund@3295:     {
raimund@3295:         Document doc = ClientProtocolUtils.newSetItemAttributeDocument(
raimund@3295:             uuid,
raimund@3295:             attr);
raimund@3295: 
raimund@3295:         if (doc == null) {
raimund@3295:             log.warn("Cannot set item attribute: No attribute found.");
raimund@3295:             return;
raimund@3295:         }
raimund@3295: 
raimund@3295:         ArtifactDatabase db = context.getDatabase();
raimund@3295:         CallMeta       meta = context.getMeta();
raimund@3295: 
raimund@3295:         db.setCollectionItemAttribute(identifier, uuid, doc, meta);
raimund@3295:     }
raimund@3295: 
raimund@3295: 
raimund@3295:     /**
raimund@3295:      * Show blackboard (context) to each facet and create a list of
raimund@3295:      * ArtifactAndFacets on the fly (with the same ordering as the passed
raimund@3295:      * ThemeList).
raimund@3295:      * @param themeList ThemeList to create a ArtifactAndFacetList along.
raimund@3295:      * @param context   The "Blackboard".
raimund@3295:      */
raimund@3295:     protected List<ArtifactAndFacet> doBlackboardPass(
raimund@3295:         ThemeList themeList, CallContext context
raimund@3295:     ) {
raimund@3295:         ArrayList<ArtifactAndFacet> dataProviders =
raimund@3295:             new ArrayList<ArtifactAndFacet>();
raimund@3295:         int size = themeList.size();
raimund@3295: 
raimund@3295:         try {
raimund@3295:             // Collect all ArtifactAndFacets for blackboard pass.
raimund@3295:             for (int i = 0; i < size; i++) {
raimund@3295:                 ManagedFacet theme = themeList.get(i);
raimund@3295:                 if (theme == null) {
raimund@3295:                     log.warn("A ManagedFacet in ThemeList is null.");
raimund@3295:                     continue;
raimund@3295:                 }
raimund@3295:                 String uuid        = theme.getArtifact();
raimund@3295:                 Artifact artifact  = getArtifact(uuid, context);
raimund@3295:                 FLYSArtifact flys  = (FLYSArtifact) artifact;
raimund@3295: 
raimund@3295:                 ArtifactAndFacet artifactAndFacet = new ArtifactAndFacet(
raimund@3295:                     artifact,
raimund@3295:                     flys.getNativeFacet(theme));
raimund@3295: 
raimund@3295:                 // XXX HELP ME PLEASE
raimund@3295:                 artifactAndFacet.setFacetDescription(theme.getDescription());
raimund@3295: 
raimund@3295:                 // Show blackboard to facet.
raimund@3295:                 artifactAndFacet.register(context);
raimund@3295: 
raimund@3295:                 // Add to themes.
raimund@3295:                 dataProviders.add(i, artifactAndFacet);
raimund@3295:             }
raimund@3295:         }
raimund@3295:         catch (ArtifactDatabaseException ade) {
raimund@3295:             log.error("ArtifactDatabaseException!", ade);
raimund@3295:         }
raimund@3295: 
raimund@3295:         return dataProviders;
raimund@3295:     }
raimund@3295:     /**
raimund@3295:      * Returns a concrete Artifact of this collection specified by its uuid.
raimund@3295:      *
raimund@3295:      * @param uuid The Artifact's uuid.
raimund@3295:      * @param context The CallContext.
raimund@3295:      *
raimund@3295:      * @return an Artifact.
raimund@3295:      */
raimund@3295:     protected Artifact getArtifact(String uuid, CallContext context)
raimund@3295:     throws    ArtifactDatabaseException
raimund@3295:     {
raimund@3295:         log.debug("FLYSArtifactCollection.getArtifact");
raimund@3295: 
raimund@3295:         Backend backend               = Backend.getInstance();
raimund@3295:         PersistentArtifact persistent = backend.getArtifact(uuid);
raimund@3295: 
raimund@3295:         return persistent != null ? persistent.getArtifact() : null;
raimund@3295:     }
raimund@3295: 
raimund@3295:     /**
raimund@3295:      * Initializes the attribute of an collection item with the theme of a
raimund@3295:      * specific facet.
raimund@3295:      *
raimund@3295:      * @param uuid The uuid of an artifact.
raimund@3295:      * @param facet The name of a facet.
raimund@3295:      * @param context The CallContext.
raimund@3295:      *
raimund@3295:      * @param the new attribute.
raimund@3295:      */
raimund@3295:     protected Document initItemAttribute(
raimund@3295:         String      uuid,
raimund@3295:         String      facet,
raimund@3295:         String      pattern,
raimund@3295:         int         index,
raimund@3295:         String      outName,
raimund@3295:         CallContext context)
raimund@3295:     {
raimund@3295:         boolean debug = log.isDebugEnabled();
raimund@3295: 
raimund@3295:         if (debug) {
raimund@3295:             log.debug("FLYSArtifactCollection.initItemAttribute");
raimund@3295:         }
raimund@3295: 
raimund@3295:         Theme t = getThemeForFacet(uuid, facet, pattern, index, outName, context);
raimund@3295: 
raimund@3295:         if (t == null) {
raimund@3295:             log.info("Could not find theme for facet. Cancel initialization.");
raimund@3295:             return null;
raimund@3295:         }
raimund@3295: 
raimund@3295:         Document attr = XMLUtils.newDocument();
raimund@3295:         addThemeToAttribute(uuid, attr, t, context);
raimund@3295: 
raimund@3295:         if (debug) {
raimund@3295:             log.debug("initItemAttribute for facet " + facet + ": "
raimund@3295:                 + XMLUtils.toString(attr));
raimund@3295:         }
raimund@3295: 
raimund@3295:         return attr;
raimund@3295:     }
sascha@3299: 
raimund@3295:         /**
raimund@3295:      * Returns the theme of a specific facet.
raimund@3295:      *
raimund@3295:      * @param uuid The uuid of an artifact.
raimund@3295:      * @param facet The name of the facet.
raimund@3295:      * @param context The CallContext object.
raimund@3295:      *
raimund@3295:      * @return the desired theme.
raimund@3295:      */
raimund@3295:     protected Theme getThemeForFacet(
raimund@3295:         String uuid,
raimund@3295:         String facet,
raimund@3295:         String pattern,
raimund@3295:         int    index,
raimund@3295:         String outName,
raimund@3295:         CallContext context)
raimund@3295:     {
raimund@3295:         log.info("FLYSArtifactCollection.getThemeForFacet: " + facet);
raimund@3295: 
raimund@3295:         FLYSContext flysContext = context instanceof FLYSContext
raimund@3295:             ? (FLYSContext) context
raimund@3295:             : (FLYSContext) context.globalContext();
raimund@3295: 
raimund@3295:         // Push artifact in flysContext.
raimund@3295:         ArtifactDatabase db = context.getDatabase();
raimund@3295:         try {
raimund@3295:             FLYSArtifact artifact = (FLYSArtifact) db.getRawArtifact(uuid);
raimund@3295:             log.debug("Got raw artifact");
raimund@3295:             flysContext.put(FLYSContext.ARTIFACT_KEY, artifact);
raimund@3295:         }
raimund@3295:         catch (ArtifactDatabaseException dbe) {
raimund@3295:             log.error("Exception caught when trying to get art.", dbe);
raimund@3295:         }
raimund@3295: 
raimund@3295:         Theme t = ThemeFactory.getTheme(
raimund@3295:                       flysContext,
raimund@3295:                       facet,
raimund@3295:                       pattern,
raimund@3295:                       outName,
raimund@3295:                       "default");
raimund@3295: 
raimund@3295:         if (t != null) {
ingo@3781:             log.debug("found theme for facet '" + facet + "'");
raimund@3295:             t.setFacet(facet);
raimund@3295:             t.setIndex(index);
raimund@3295:         }
ingo@3781:         else {
ingo@3781:             log.warn("unable to find theme for facet '" + facet + "'");
ingo@3781:         }
raimund@3295: 
raimund@3295:         return t;
raimund@3295:     }
raimund@3295: 
raimund@3295:     /**
raimund@3295:      * Inner class to structure/order the themes of a chart.
raimund@3295:      */
raimund@3295:     private static class ThemeList {
raimund@3295:         private Logger logger = Logger.getLogger(ThemeList.class);
weinzierl@4255:         protected List<ManagedFacet> themes;
raimund@3295: 
raimund@3295:         public ThemeList(Document output) {
weinzierl@4255:             themes = new ArrayList<ManagedFacet>();
raimund@3295:             parse(output);
raimund@3295:         }
raimund@3295: 
raimund@3295:         protected void parse(Document output) {
raimund@3295:             NodeList themeList = (NodeList) XMLUtils.xpath(
raimund@3295:                 output,
raimund@3295:                 "art:output/art:facet",
raimund@3295:                 XPathConstants.NODESET,
raimund@3295:                 ArtifactNamespaceContext.INSTANCE);
raimund@3295: 
raimund@3295:             int num = themeList != null ? themeList.getLength() : 0;
raimund@3295: 
raimund@3295:             logger.debug("Output has " +  num + " elements.");
raimund@3295: 
raimund@3295:             if (num == 0) {
raimund@3295:                 return;
raimund@3295:             }
raimund@3295: 
raimund@3295:             for (int i = 0; i < num; i++) {
raimund@3295:                 Element theme = (Element) themeList.item(i);
raimund@3295: 
raimund@3295:                 ManagedDomFacet facet = new ManagedDomFacet(theme);
weinzierl@4255:                 themes.add(facet);
raimund@3295:             }
weinzierl@4255: 
weinzierl@4255:             Collections.sort(themes);
raimund@3295:         }
raimund@3295: 
raimund@3295:         public ManagedFacet get(int idx) {
weinzierl@4255:             return themes.get(idx);
raimund@3295:         }
raimund@3295: 
raimund@3295:         public int size() {
raimund@3295:             return themes.size();
raimund@3295:         }
raimund@3295:     }
raimund@3295: }
sascha@3304: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :