ingo@147: package de.intevation.flys.collections;
ingo@147: 
ingo@300: import java.io.IOException;
ingo@300: import java.io.OutputStream;
ingo@300: import java.util.ArrayList;
ingo@147: import java.util.Date;
ingo@638: import java.util.HashMap;
ingo@300: import java.util.List;
ingo@300: import java.util.Map;
ingo@147: 
ingo@147: import javax.xml.xpath.XPathConstants;
ingo@147: 
ingo@147: import org.apache.log4j.Logger;
ingo@147: 
ingo@147: import org.w3c.dom.Document;
ingo@147: import org.w3c.dom.Element;
ingo@147: import org.w3c.dom.Node;
ingo@293: import org.w3c.dom.NodeList;
ingo@147: 
ingo@300: import de.intevation.artifacts.Artifact;
ingo@293: import de.intevation.artifacts.ArtifactDatabase;
ingo@293: import de.intevation.artifacts.ArtifactDatabaseException;
ingo@147: import de.intevation.artifacts.ArtifactNamespaceContext;
ingo@147: import de.intevation.artifacts.CallContext;
ingo@293: import de.intevation.artifacts.CallMeta;
ingo@147: 
ingo@350: import de.intevation.artifacts.common.utils.ClientProtocolUtils;
ingo@147: import de.intevation.artifacts.common.utils.XMLUtils;
ingo@147: 
ingo@300: import de.intevation.artifactdatabase.Backend;
ingo@300: import de.intevation.artifactdatabase.Backend.PersistentArtifact;
ingo@147: import de.intevation.artifactdatabase.DefaultArtifactCollection;
felix@1785: import de.intevation.artifactdatabase.state.StateEngine;
ingo@147: 
ingo@300: import de.intevation.flys.artifacts.context.FLYSContext;
felix@1784: import de.intevation.flys.artifacts.FLYSArtifact;
ingo@347: import de.intevation.flys.artifacts.model.ManagedFacet;
ingo@1717: import de.intevation.flys.artifacts.model.ManagedDomFacet;
ingo@300: import de.intevation.flys.exports.OutGenerator;
ingo@350: import de.intevation.flys.themes.Theme;
ingo@350: import de.intevation.flys.themes.ThemeFactory;
ingo@300: 
felix@1784: import de.intevation.flys.utils.FLYSUtils;
ingo@147: 
ingo@147: /**
ingo@147:  * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
ingo@147:  */
ingo@147: public class FLYSArtifactCollection extends DefaultArtifactCollection {
ingo@147: 
felix@1013:     /** The logger used in this class. */
ingo@147:     private static Logger log = Logger.getLogger(FLYSArtifactCollection.class);
ingo@147: 
felix@1013:     /** Constant XPath that points to the outputmodes of an artifact. */
ingo@147:     public static final String XPATH_ARTIFACT_OUTPUTMODES =
ingo@147:         "/art:result/art:outputmodes";
ingo@147: 
ingo@293:     public static final String XPATH_COLLECTION_ITEMS =
ingo@293:         "/art:result/art:artifact-collection/art:collection-item";
ingo@293: 
ingo@300:     public static final String XPATH_OUT_NAME = "/art:action/@art:name";
ingo@300: 
ingo@300:     public static final String XPATH_OUT_TYPE = "/art:action/@art:type";
ingo@300: 
felix@1780:     /** Xpath to master artifacts uuid. */
felix@1780:     public static final String XPATH_MASTER_UUID =
felix@1780:         "/art:artifact-collection/art:artifact/@art:uuid";
felix@1744: 
ingo@971:     public static final String XPATH_LOADED_RECOMMENDATIONS =
ingo@971:         "/art:attribute/art:loaded-recommendations";
ingo@971: 
ingo@300: 
felix@1628:     /**
felix@1628:      * Return description Document for this collection.
felix@1628:      */
ingo@147:     @Override
ingo@147:     public Document describe(CallContext context) {
ingo@147:         log.debug("FLYSArtifactCollection.describe: " + identifier);
ingo@147: 
ingo@147:         Document doc = XMLUtils.newDocument();
ingo@147: 
ingo@147:         XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
ingo@147:             doc,
ingo@147:             ArtifactNamespaceContext.NAMESPACE_URI,
ingo@147:             ArtifactNamespaceContext.NAMESPACE_PREFIX);
ingo@147: 
ingo@147:         Date creationTime = getCreationTime();
ingo@147:         String creation   = creationTime != null
ingo@147:             ? Long.toString(creationTime.getTime())
ingo@147:             : "";
ingo@147: 
ingo@147:         Element collection = ec.create("artifact-collection");
ingo@147:         Element artifacts  = ec.create("artifacts");
ingo@147: 
ingo@147:         ec.addAttr(collection, "name", getName(), true);
ingo@147:         ec.addAttr(collection, "uuid", identifier(), true);
ingo@147:         ec.addAttr(collection, "creation", creation,  true);
ingo@713:         ec.addAttr(collection, "ttl", String.valueOf(getTTL()), true);
ingo@147: 
ingo@147:         collection.appendChild(artifacts);
ingo@147:         doc.appendChild(collection);
ingo@147: 
ingo@346:         ArtifactDatabase db = context.getDatabase();
ingo@971:         Document oldAttrs   = getAttribute();
ingo@293: 
ingo@300:         try {
ingo@971:             String[] aUUIDs = getArtifactUUIDs(context);
ingo@971:             Node newAttr    = mergeAttributes(db, context, oldAttrs, aUUIDs);
ingo@638: 
ingo@971:             collection.appendChild(doc.importNode(newAttr, true));
ingo@346: 
felix@1784:             // Make it an empty array if null.
felix@1784:             if (aUUIDs == null) {
felix@1784:                 aUUIDs = new String[] {};
felix@1784:             }
felix@1784: 
felix@1784:             for (String uuid: aUUIDs) {
felix@1784:                 try {
felix@1784:                     artifacts.appendChild(
felix@1784:                         buildArtifactNode(db, uuid, context, ec));
felix@1784:                 }
felix@1784:                 catch (ArtifactDatabaseException dbe) {
felix@1784:                     log.warn(dbe, dbe);
ingo@293:                 }
ingo@293:             }
ingo@293:         }
ingo@293:         catch (ArtifactDatabaseException ade) {
ingo@293:             log.error(ade, ade);
ingo@971: 
ingo@971:             collection.appendChild(
ingo@971:                 doc.importNode(oldAttrs.getFirstChild(), true));
ingo@147:         }
ingo@147: 
ingo@147:         return doc;
ingo@147:     }
ingo@147: 
ingo@147: 
ingo@971:     /**
ingo@971:      * Merge the current art:outputs nodes with the the outputs provided by the
ingo@971:      * artifacts in the Collection.
felix@1784:      *
felix@1709:      * @param uuids Artifact uuids.
ingo@971:      */
ingo@971:     protected Node mergeAttributes(
ingo@971:         ArtifactDatabase db,
ingo@971:         CallContext      context,
ingo@971:         Document         oldAttrs,
ingo@971:         String[]         uuids)
ingo@971:     {
ingo@971:         Document doc = buildOutAttributes(db, context, oldAttrs, uuids);
ingo@971:         Node newAttr = doc.getFirstChild();
ingo@971: 
ingo@971:         newAttr = mergeLoadedRecommendations(oldAttrs, newAttr);
ingo@971: 
ingo@971:         try {
felix@1013:             // Save the merged document into database.
ingo@971:             db.setCollectionAttribute(identifier(), context.getMeta(), doc);
ingo@971:         }
ingo@971:         catch (ArtifactDatabaseException adb) {
ingo@971:             log.error(adb, adb);
ingo@971:         }
ingo@971: 
ingo@971:         return newAttr;
ingo@971:     }
ingo@971: 
ingo@971: 
ingo@971:     /**
ingo@971:      * Merge the recommendations which have already been loaded from the old
ingo@971:      * attribute document into the new attribute document. This is necessary,
ingo@971:      * because mergeAttributes() only merges the art:outputs nodes - all
ingo@971:      * other nodes are skiped.
ingo@971:      */
ingo@971:     protected Node mergeLoadedRecommendations(Document oldAttrs, Node newAttrs){
ingo@971:         Element loadedRecoms = (Element) XMLUtils.xpath(
ingo@971:             oldAttrs,
ingo@971:             XPATH_LOADED_RECOMMENDATIONS,
ingo@971:             XPathConstants.NODE,
ingo@971:             ArtifactNamespaceContext.INSTANCE);
ingo@971: 
ingo@971:         if (loadedRecoms != null) {
ingo@971:             Document doc = newAttrs.getOwnerDocument();
ingo@971:             newAttrs.appendChild(doc.importNode(loadedRecoms, true));
ingo@971:         }
ingo@971: 
ingo@971:         return newAttrs;
ingo@971:     }
ingo@971: 
ingo@971: 
ingo@300:     @Override
ingo@646:     public void out(
ingo@646:         String       type,
ingo@646:         Document     format,
ingo@646:         OutputStream out,
ingo@646:         CallContext  context)
ingo@300:     throws IOException
ingo@300:     {
ingo@300:         log.info("FLYSArtifactCollection.out");
ingo@300: 
ingo@300:         String name = XMLUtils.xpathString(
ingo@300:             format, XPATH_OUT_NAME, ArtifactNamespaceContext.INSTANCE);
ingo@300: 
ingo@646:         String subtype = XMLUtils.xpathString(
ingo@300:             format, XPATH_OUT_TYPE, ArtifactNamespaceContext.INSTANCE);
ingo@300: 
ingo@646:         log.info("-> Output name = " + name);
ingo@646:         log.info("-> Output type = " + type);
ingo@646:         log.info("-> Output subtype = " + subtype);
ingo@388: 
ingo@646:         OutGenerator generator = null;
felix@1780:         if (type != null
felix@1780:              && type.length() > 0
felix@1780:              && type.indexOf("chartinfo") > 0)
felix@1780:         {
felix@1780:             generator = getOutGenerator(context, type, subtype);
ingo@646:         }
ingo@646:         else {
ingo@646:             generator = getOutGenerator(context, name, subtype);
ingo@646:         }
ingo@646: 
ingo@300:         if (generator == null) {
ingo@390:             log.error("There is no generator specified for output: " + name);
felix@1013:             // TODO Throw an exception.
ingo@300: 
ingo@300:             return;
ingo@300:         }
ingo@300: 
ingo@300:         generator.init(format, out, context);
ingo@300: 
felix@1744:         // Get master artifact.
felix@1784:         FLYSArtifact master = getMasterArtifact(context);
felix@1784:         if (master != null) {
felix@1784:             log.debug("Will set master Artifact to uuid: " + master.identifier());
felix@1784:             generator.setMasterArtifact(master);
felix@1780:         }
felix@1784:         else {
felix@1784:             log.warn("Could not set master artifact.");
felix@1744:         }
ingo@412: 
ingo@300:         try {
ingo@646:             Document attr = getAttribute(context, name);
ingo@646:             doOut(generator, name, subtype, attr, context);
ingo@347:         }
ingo@347:         catch (ArtifactDatabaseException adbe) {
ingo@347:             log.error(adbe, adbe);
ingo@347:         }
ingo@347:     }
ingo@300: 
ingo@347: 
ingo@347:     /**
felix@1029:      * Creates the concrete output.
ingo@347:      *
ingo@347:      * @param generator The OutGenerator that creates the output.
ingo@350:      * @param outputName The name of the requested output.
ingo@347:      * @param attributes The collection's attributes for this concrete output
ingo@347:      * type.
ingo@347:      * @param context The context object.
ingo@347:      */
ingo@347:     protected void doOut(
ingo@347:         OutGenerator generator,
ingo@350:         String       outName,
ingo@388:         String       facet,
ingo@347:         Document     attributes,
ingo@347:         CallContext  context)
ingo@347:     throws IOException
ingo@347:     {
ingo@646:         log.info("FLYSArtifactCollection.doOut: " + outName);
ingo@347: 
ingo@347:         ThemeList themeList = new ThemeList(attributes);
ingo@347: 
ingo@347:         int size = themeList.size();
ingo@347:         log.debug("Output will contain " + size + " elements.");
ingo@347: 
ingo@347:         try {
ingo@347:             for (int i = 0; i < size; i++) {
ingo@347:                 ManagedFacet theme = themeList.get(i);
ingo@347: 
ingo@347:                 if (theme == null) {
ingo@370:                     log.debug("Theme is empty - no output is generated.");
ingo@347:                     continue;
ingo@347:                 }
ingo@347: 
ingo@347:                 String art = theme.getArtifact();
ingo@347: 
ingo@347:                 if (log.isDebugEnabled()) {
ingo@347:                     log.debug("Do output for...");
ingo@347:                     log.debug("... artifact: " + art);
ingo@347:                     log.debug("... facet: " + theme.getName());
ingo@347:                 }
ingo@347: 
ingo@364:                 String facetName = theme.getName();
ingo@364: 
ingo@388:                 if (outName.equals("export") && !facetName.equals(facet)) {
ingo@388:                     continue;
ingo@388:                 }
ingo@388: 
ingo@412:                 Artifact artifact = getArtifact(art, context);
ingo@412: 
ingo@347:                 generator.doOut(
ingo@412:                     artifact,
ingo@695:                     theme,
ingo@350:                     getFacetThemeFromAttribute(
ingo@350:                         art,
ingo@350:                         outName,
ingo@364:                         facetName,
ingo@1747:                         theme.getDescription(),
ingo@1668:                         theme.getIndex(),
ingo@1684:                         context),
ingo@1684:                     theme.getActive() == 1);
ingo@300:             }
ingo@300:         }
ingo@300:         catch (ArtifactDatabaseException ade) {
ingo@300:             log.error(ade, ade);
ingo@300:         }
ingo@300: 
ingo@300:         generator.generate();
ingo@300:     }
ingo@300: 
ingo@300: 
felix@1628:     /**
felix@1784:      * @return masterartifact or null if exception/not found.
felix@1784:      */
felix@1784:     protected FLYSArtifact getMasterArtifact(CallContext context)
felix@1784:     {
felix@1784:         try {
felix@1784:             ArtifactDatabase db = context.getDatabase();
felix@1784:             CallMeta callMeta   = context.getMeta();
felix@1784:             Document document   = db.getCollectionsMasterArtifact(
felix@1784:                 identifier(), callMeta);
felix@1784:     
felix@1784:             String masterUUID   = XMLUtils.xpathString(
felix@1784:                 document, XPATH_MASTER_UUID, ArtifactNamespaceContext.INSTANCE);
felix@1784:             FLYSArtifact masterArtifact =
felix@1784:                 (FLYSArtifact) getArtifact(masterUUID, context);
felix@1784:             return masterArtifact;
felix@1784:         }
felix@1784:         catch (ArtifactDatabaseException ade) {
felix@1784:             log.error(ade, ade);
felix@1784:         }
felix@1784:         return null;
felix@1784:     }
felix@1784: 
felix@1784: 
felix@1784:     /**
felix@1628:      * Return merged output document.
felix@1709:      * @param uuids List of artifact uuids.
felix@1628:      */
ingo@971:     protected Document buildOutAttributes(
ingo@346:         ArtifactDatabase db,
ingo@346:         CallContext      context,
ingo@346:         Document         oldAttr,
felix@1709:         String[]         uuids)
ingo@346:     {
ingo@346:         Document doc = XMLUtils.newDocument();
ingo@346: 
felix@1785:         FLYSContext flysContext = FLYSUtils.getFlysContext(context);
felix@1785:         StateEngine engine = (StateEngine) flysContext.get(
felix@1785:         FLYSContext.STATE_ENGINE_KEY);
felix@1785: 
felix@1785:         FLYSArtifact masterArtifact = getMasterArtifact(context);
felix@1785:     
ingo@346:         XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
ingo@346:             doc,
ingo@346:             ArtifactNamespaceContext.NAMESPACE_URI,
ingo@346:             ArtifactNamespaceContext.NAMESPACE_PREFIX);
felix@1785:     
ingo@346:         AttributeParser aParser = new AttributeParser();
ingo@944:         OutputParser    oParser = new OutputParser(db, context);
felix@1785:     
felix@1709:         if (uuids != null) {
felix@1709:             for (String uuid: uuids) {
ingo@1175:                 try {
ingo@1175:                     oParser.parse(uuid);
ingo@1175:                 }
ingo@1175:                 catch (ArtifactDatabaseException ade) {
ingo@1175:                     log.warn(ade, ade);
ingo@1175:                 }
ingo@346:             }
ingo@346:         }
felix@1785:     
ingo@346:         aParser.parse(oldAttr);
felix@1785:     
felix@1709:         return new AttributeWriter(
felix@1709:             db,
felix@1709:             aParser.getOuts(),
felix@1785:             aParser.getFacets(),
felix@1785:             oParser.getOuts(),
felix@1785:             oParser.getFacets(),
felix@1785:             engine.getCompatibleFacets(masterArtifact.getStateHistoryIds())
felix@1785:             ).write();
ingo@346:     }
ingo@346: 
ingo@346: 
ingo@300:     /**
felix@1780:      * Returns the "attribute" (part of description document) for a specific
felix@1780:      * output type.
ingo@347:      *
ingo@347:      * @param output The name of the desired output type.
ingo@347:      *
ingo@347:      * @return the attribute for the desired output type.
ingo@347:      */
ingo@347:     protected Document getAttribute(CallContext context, String output)
ingo@347:     throws    ArtifactDatabaseException
ingo@347:     {
ingo@971:         Document attr = buildOutAttributes(
ingo@347:             context.getDatabase(),
ingo@347:             context,
ingo@347:             getAttribute(),
ingo@347:             getArtifactUUIDs(context));
ingo@347: 
ingo@347:         Node out = (Node) XMLUtils.xpath(
ingo@347:             attr,
ingo@638:             "art:attribute/art:outputs/art:output[@name='" + output + "']",
ingo@347:             XPathConstants.NODE,
ingo@347:             ArtifactNamespaceContext.INSTANCE);
ingo@347: 
ingo@347: 
ingo@347:         if (out != null) {
ingo@347:             Document o = XMLUtils.newDocument();
ingo@347: 
ingo@347:             o.appendChild(o.importNode(out, true));
ingo@347: 
ingo@347:             return o;
ingo@347:         }
ingo@347: 
ingo@347:         return null;
ingo@347:     }
ingo@347: 
ingo@347: 
ingo@347:     /**
ingo@300:      * This method returns the list of artifact UUIDs that this collections
ingo@300:      * contains.
ingo@300:      *
ingo@300:      * @param context The CallContext that is necessary to get information about
ingo@300:      * the ArtifactDatabase.
ingo@300:      *
ingo@300:      * @return a list of uuids.
ingo@300:      */
ingo@300:     protected String[] getArtifactUUIDs(CallContext context)
ingo@300:     throws    ArtifactDatabaseException
ingo@300:     {
ingo@300:         log.debug("FLYSArtifactCollection.getArtifactUUIDs");
ingo@300: 
ingo@300:         ArtifactDatabase db = context.getDatabase();
ingo@300:         CallMeta meta       = context.getMeta();
ingo@300: 
ingo@300:         Document itemList = db.listCollectionArtifacts(identifier(), meta);
ingo@300:         NodeList items    = (NodeList) XMLUtils.xpath(
ingo@300:             itemList,
ingo@300:             XPATH_COLLECTION_ITEMS,
ingo@300:             XPathConstants.NODESET,
ingo@300:             ArtifactNamespaceContext.INSTANCE);
ingo@300: 
ingo@300:         if (items == null || items.getLength() == 0) {
ingo@300:             log.debug("No artifacts found in this collection.");
ingo@300:             return null;
ingo@300:         }
ingo@300: 
ingo@300:         int num = items.getLength();
ingo@300: 
ingo@300:         List<String> uuids = new ArrayList<String>(num);
ingo@300: 
ingo@300:         for (int i = 0; i < num; i++) {
ingo@300:             String uuid = XMLUtils.xpathString(
ingo@300:                 items.item(i),
ingo@300:                 "@art:uuid",
ingo@300:                 ArtifactNamespaceContext.INSTANCE);
ingo@300: 
ingo@300:             if (uuid != null && uuid.trim().length() != 0) {
ingo@300:                 uuids.add(uuid);
ingo@300:             }
ingo@300:         }
ingo@300: 
ingo@300:         return (String[]) uuids.toArray(new String[uuids.size()]);
ingo@300:     }
ingo@300: 
ingo@300: 
ingo@300:     /**
ingo@300:      * Returns a concrete Artifact of this collection specified by its uuid.
ingo@300:      *
ingo@300:      * @param uuid The Artifact's uuid.
ingo@300:      * @param context The CallContext.
ingo@300:      *
ingo@300:      * @return an Artifact.
ingo@300:      */
ingo@300:     protected Artifact getArtifact(String uuid, CallContext context)
ingo@300:     throws    ArtifactDatabaseException
ingo@300:     {
ingo@300:         log.debug("FLYSArtifactCollection.getArtifact");
ingo@300: 
ingo@300:         Backend backend               = Backend.getInstance();
ingo@300:         PersistentArtifact persistent = backend.getArtifact(uuid);
ingo@300: 
ingo@300:         return persistent != null ? persistent.getArtifact() : null;
ingo@300:     }
ingo@300: 
ingo@300: 
ingo@300:     /**
felix@1822:      * Returns the attribute that belongs to an artifact and facet stored in
felix@1822:      * this collection.
ingo@300:      *
ingo@300:      * @param uuid The Artifact's uuid.
ingo@350:      * @param outname The name of the requested output.
ingo@350:      * @param facet The name of the requested facet.
ingo@300:      * @param context The CallContext.
ingo@300:      *
ingo@300:      * @return an attribute in form of a document.
ingo@300:      */
ingo@350:     protected Document getFacetThemeFromAttribute(
ingo@350:         String      uuid,
ingo@350:         String      outName,
ingo@350:         String      facet,
ingo@1747:         String      pattern,
ingo@1668:         int         index,
ingo@350:         CallContext context)
ingo@300:     throws    ArtifactDatabaseException
ingo@300:     {
ingo@350:         log.debug("FLYSArtifactCollection.getFacetThemeFromAttribute");
ingo@300: 
ingo@350:         ArtifactDatabase db = context.getDatabase();
ingo@350:         CallMeta       meta = context.getMeta();
ingo@350: 
ingo@350:         FLYSContext flysContext = context instanceof FLYSContext
ingo@350:             ? (FLYSContext) context
ingo@350:             : (FLYSContext) context.globalContext();
ingo@350: 
ingo@350:         Document attr = db.getCollectionItemAttribute(identifier(), uuid, meta);
ingo@350: 
ingo@350:         if (attr == null) {
felix@1828:             attr = initItemAttribute(uuid, facet, pattern, index, outName, context);
ingo@350: 
ingo@350:             if (attr == null) {
ingo@350:                 return null;
ingo@350:             }
ingo@350:         }
ingo@350: 
ingo@350:         log.debug("Search attribute of collection item: " + uuid);
ingo@350: 
ingo@350:         Node tmp = (Node) XMLUtils.xpath(
ingo@350:             attr,
ingo@350:             "/art:attribute",
ingo@350:             XPathConstants.NODE,
ingo@350:             ArtifactNamespaceContext.INSTANCE);
ingo@350: 
ingo@350:         if (tmp == null) {
ingo@350:             log.warn("No attribute found. Operation failed.");
ingo@350:             return null;
ingo@350:         }
ingo@350: 
ingo@1747:         log.debug("Search theme for facet '" + facet + "' in attribute.");
ingo@350: 
ingo@350:         Node theme = (Node) XMLUtils.xpath(
ingo@350:             tmp,
ingo@1668:             "art:themes/theme[@facet='" + facet +
ingo@1668:             "' and @index='" + String.valueOf(index) + "']",
ingo@350:             XPathConstants.NODE,
ingo@350:             ArtifactNamespaceContext.INSTANCE);
ingo@350: 
ingo@350:         if (theme == null) {
ingo@350:             log.warn("Could not find the theme in attribute of: " + uuid);
ingo@350: 
felix@1828:             Theme t = getThemeForFacet(
felix@1828:                 uuid, facet, pattern, index, outName, context);
ingo@350: 
ingo@350:             if (t == null) {
ingo@350:                 log.warn("No theme found for facet: " + facet);
ingo@350:                 return null;
ingo@350:             }
ingo@350: 
ingo@350:             addThemeToAttribute(uuid, attr, t, context);
felix@1828:             theme = t.toXML().getFirstChild();
ingo@350:         }
ingo@350: 
ingo@350:         Document doc = XMLUtils.newDocument();
ingo@350:         doc.appendChild(doc.importNode(theme, true));
ingo@350: 
ingo@350:         return doc;
ingo@350:     }
ingo@350: 
ingo@350: 
ingo@350:     /**
ingo@350:      * Adds the theme of a facet to a CollectionItem's attribute.
ingo@350:      *
ingo@350:      * @param uuid The uuid of the artifact.
ingo@350:      * @param attr The current attribute of an artifact.
ingo@350:      * @param t The theme to add.
ingo@350:      * @param context The CallContext.
ingo@350:      */
ingo@350:     protected void addThemeToAttribute(
ingo@350:         String      uuid,
ingo@350:         Document    attr,
ingo@350:         Theme       t,
ingo@350:         CallContext context)
ingo@350:     {
ingo@350:         log.debug("FLYSArtifactCollection.addThemeToAttribute: " + uuid);
ingo@350: 
ingo@350:         if (t == null) {
ingo@350:             log.warn("Theme is empty - cancel adding it to attribute!");
ingo@350:             return;
ingo@350:         }
ingo@350: 
ingo@350:         XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
ingo@350:             attr,
ingo@350:             ArtifactNamespaceContext.NAMESPACE_URI,
ingo@350:             ArtifactNamespaceContext.NAMESPACE_PREFIX);
ingo@350: 
ingo@350:         Node tmp = (Node) XMLUtils.xpath(
ingo@350:             attr,
ingo@350:             "/art:attribute",
ingo@350:             XPathConstants.NODE,
ingo@350:             ArtifactNamespaceContext.INSTANCE);
ingo@350: 
ingo@350:         if (tmp == null) {
ingo@350:             tmp = ec.create("attribute");
ingo@350:             attr.appendChild(tmp);
ingo@350:         }
ingo@350: 
ingo@350:         Node themes = (Node) XMLUtils.xpath(
ingo@350:             tmp,
ingo@350:             "art:themes",
ingo@350:             XPathConstants.NODE,
ingo@350:             ArtifactNamespaceContext.INSTANCE);
ingo@350: 
ingo@350:         if (themes == null) {
ingo@350:             themes = ec.create("themes");
ingo@350:             tmp.appendChild(themes);
ingo@350:         }
ingo@350: 
ingo@350:         themes.appendChild(attr.importNode(t.toXML().getFirstChild(), true));
ingo@350: 
ingo@350:         try {
ingo@350:             setCollectionItemAttribute(uuid, attr, context);
ingo@350: 
ingo@350:             log.debug("Successfully added theme to item attribute.");
ingo@350:         }
ingo@350:         catch (ArtifactDatabaseException e) {
ingo@350:             // do nothing
ingo@350:             log.warn("Cannot set attribute of item: " + uuid);
ingo@350:         }
ingo@350:     }
ingo@350: 
ingo@350: 
ingo@350:     /**
ingo@350:      * Initializes the attribute of an collection item with the theme of a
ingo@350:      * specific facet.
ingo@350:      *
ingo@350:      * @param uuid The uuid of an artifact.
ingo@350:      * @param facet The name of a facet.
ingo@350:      * @param context The CallContext.
ingo@350:      *
ingo@350:      * @param the new attribute.
ingo@350:      */
ingo@350:     protected Document initItemAttribute(
ingo@350:         String      uuid,
ingo@350:         String      facet,
ingo@1747:         String      pattern,
ingo@1668:         int         index,
felix@1828:         String      outName,
ingo@350:         CallContext context)
ingo@350:     {
ingo@350:         log.info("FLYSArtifactCollection.initItemAttribute");
ingo@350: 
felix@1828:         Theme t = getThemeForFacet(uuid, facet, pattern, index, outName, context);
ingo@350: 
ingo@350:         if (t == null) {
ingo@350:             log.info("Could not find theme for facet. Cancel initialization.");
ingo@350:             return null;
ingo@350:         }
ingo@350: 
ingo@350:         Document attr = XMLUtils.newDocument();
ingo@350: 
ingo@350:         addThemeToAttribute(uuid, attr, t, context);
ingo@350: 
ingo@350:         return attr;
ingo@350:     }
ingo@350: 
ingo@350: 
ingo@350:     /**
ingo@350:      * Sets the attribute of a CollectionItem specified by <i>uuid</i> to a new
ingo@350:      * value <i>attr</i>.
ingo@350:      *
ingo@350:      * @param uuid The uuid of the CollectionItem.
ingo@350:      * @param attr The new attribute for the CollectionItem.
ingo@350:      * @param context The CallContext.
ingo@350:      */
ingo@350:     public void setCollectionItemAttribute(
ingo@350:         String      uuid,
ingo@350:         Document    attr,
ingo@350:         CallContext context)
ingo@350:     throws ArtifactDatabaseException
ingo@350:     {
ingo@350:         Document doc = ClientProtocolUtils.newSetItemAttributeDocument(
ingo@350:             uuid,
ingo@350:             attr);
ingo@350: 
ingo@350:         if (doc == null) {
ingo@350:             log.warn("Cannot set item attribute: No attribute found.");
ingo@350:             return;
ingo@350:         }
ingo@350: 
ingo@350:         ArtifactDatabase db = context.getDatabase();
ingo@350:         CallMeta       meta = context.getMeta();
ingo@350: 
ingo@350:         db.setCollectionItemAttribute(identifier(), uuid, doc, meta);
ingo@350:     }
ingo@350: 
ingo@350: 
ingo@350:     /**
ingo@350:      * Returns the theme of a specific facet.
ingo@350:      *
ingo@350:      * @param uuid The uuid of an artifact.
ingo@350:      * @param facet The name of the facet.
ingo@350:      * @param context The CallContext object.
ingo@350:      *
ingo@350:      * @return the desired theme.
ingo@350:      */
ingo@350:     protected Theme getThemeForFacet(
ingo@350:         String uuid,
ingo@350:         String facet,
ingo@1747:         String pattern,
ingo@1668:         int    index,
felix@1828:         String outName,
ingo@350:         CallContext context)
ingo@350:     {
ingo@350:         log.info("FLYSArtifactCollection.getThemeForFacet: " + facet);
ingo@350: 
ingo@350:         FLYSContext flysContext = context instanceof FLYSContext
ingo@350:             ? (FLYSContext) context
ingo@350:             : (FLYSContext) context.globalContext();
ingo@350: 
felix@1822:         // Push artifact in flysContext.
felix@1822:         ArtifactDatabase db = context.getDatabase();
felix@1822:         try {
felix@1822:             FLYSArtifact artifact = (FLYSArtifact) db.getRawArtifact(uuid);
felix@1822:             log.debug("Got raw artifact");
felix@1822:             flysContext.put(flysContext.ARTIFACT_KEY, artifact);
felix@1822:         }
felix@1837:         catch (ArtifactDatabaseException dbe) {
felix@1837:             log.error("Exception caught when trying to get art.", dbe);
felix@1822:         }
felix@1822: 
felix@1828:         Theme t = ThemeFactory.getTheme(flysContext, facet, pattern, outName);
ingo@1668: 
ingo@1668:         if (t != null) {
ingo@1668:             t.setFacet(facet);
ingo@1668:             t.setIndex(index);
ingo@1668:         }
ingo@1668: 
ingo@1668:         return t;
ingo@300:     }
ingo@300: 
ingo@300: 
ingo@300:     /**
ingo@300:      * Returns the OutGenerator for a specified <i>type</i>.
ingo@300:      *
ingo@300:      * @param name The name of the output type.
ingo@300:      * @param type Defines the type of the desired OutGenerator.
ingo@300:      *
felix@1029:      * @return Instance of an OutGenerator for specified <i>type</i>.
ingo@300:      */
ingo@300:     protected OutGenerator getOutGenerator(
ingo@300:         CallContext context,
ingo@300:         String      name,
ingo@300:         String      type)
ingo@300:     {
ingo@300:         FLYSContext flysContext = context instanceof FLYSContext
ingo@300:             ? (FLYSContext) context
ingo@300:             : (FLYSContext) context.globalContext();
ingo@300: 
ingo@300:         Map<String, Class> generators = (Map<String, Class>)
ingo@300:             flysContext.get(FLYSContext.OUTGENERATORS_KEY);
ingo@300: 
ingo@300:         if (generators == null) {
ingo@300:             log.error("No output generators found in the running application!");
ingo@300:             return null;
ingo@300:         }
ingo@300: 
ingo@388:         Class clazz = generators.get(name);
ingo@300: 
ingo@300:         try {
ingo@300:             return clazz != null ? (OutGenerator) clazz.newInstance() : null;
ingo@300:         }
ingo@300:         catch (InstantiationException ie) {
ingo@300:             log.error(ie, ie);
ingo@300:         }
ingo@300:         catch (IllegalAccessException iae) {
ingo@300:             log.error(iae, iae);
ingo@300:         }
ingo@300: 
ingo@300:         return null;
ingo@300:     }
ingo@300: 
ingo@300: 
ingo@147:     protected Element buildArtifactNode(
ingo@293:         ArtifactDatabase        database,
ingo@293:         String                  uuid,
ingo@147:         CallContext             context,
ingo@147:         XMLUtils.ElementCreator ec)
ingo@293:     throws ArtifactDatabaseException
ingo@147:     {
ingo@293:         log.debug("Append artifact '" + uuid + "' to collection description");
ingo@147: 
ingo@346:         // TODO
ingo@293:         String hash = "MYHASH";
ingo@156: 
ingo@147:         Element ci   = ec.create("artifact");
ingo@148:         ec.addAttr(ci, "uuid", uuid, true);
ingo@293:         ec.addAttr(ci, "hash", hash, true);
ingo@147: 
ingo@346:         // XXX I am not sure if it works well every time with an empty document
ingo@346:         // in the describe operation of an artifact.
ingo@346:         Document description = database.describe(uuid, null, context.getMeta());
ingo@346: 
ingo@293:         Node outputModes = (Node) XMLUtils.xpath(
ingo@147:             description,
ingo@147:             XPATH_ARTIFACT_OUTPUTMODES,
ingo@147:             XPathConstants.NODE,
ingo@147:             ArtifactNamespaceContext.INSTANCE);
ingo@147: 
ingo@147:         if (outputModes != null) {
ingo@147:             Document doc = ci.getOwnerDocument();
ingo@147:             ci.appendChild(doc.importNode(outputModes, true));
ingo@147:         }
ingo@147: 
ingo@147:         return ci;
ingo@147:     }
ingo@347: 
ingo@347: 
ingo@347:     /**
ingo@347:      * Inner class to structure/order the themes of a chart.
ingo@347:      */
sascha@1718:     private static class ThemeList {
ingo@347:         private Logger logger = Logger.getLogger(ThemeList.class);
ingo@638:         protected Map<Integer, ManagedFacet> themes;
ingo@347: 
ingo@347:         public ThemeList(Document output) {
ingo@638:             themes = new HashMap<Integer, ManagedFacet>();
ingo@347:             parse(output);
ingo@347:         }
ingo@347: 
ingo@347:         protected void parse(Document output) {
ingo@1717:             NodeList themeList = (NodeList) XMLUtils.xpath(
ingo@347:                 output,
ingo@945:                 "art:output/art:facet",
ingo@347:                 XPathConstants.NODESET,
ingo@347:                 ArtifactNamespaceContext.INSTANCE);
ingo@347: 
ingo@1717:             int num = themeList != null ? themeList.getLength() : 0;
ingo@347: 
ingo@347:             logger.debug("Output has " +  num + " elements.");
ingo@347: 
ingo@347:             if (num == 0) {
ingo@347:                 return;
ingo@347:             }
ingo@347: 
ingo@694:             String uri = ArtifactNamespaceContext.NAMESPACE_URI;
ingo@347: 
ingo@694:             for (int i = 0; i < num; i++) {
ingo@1717:                 Element theme = (Element) themeList.item(i);
ingo@347: 
ingo@1717:                 ManagedDomFacet facet = new ManagedDomFacet(theme);
sascha@1718:                 themes.put(Integer.valueOf(facet.getPosition()-1), facet);
ingo@347:             }
ingo@347:         }
ingo@347: 
ingo@347:         public ManagedFacet get(int idx) {
sascha@1718:             return themes.get(Integer.valueOf(idx));
ingo@347:         }
ingo@347: 
ingo@347:         public int size() {
ingo@347:             return themes.size();
ingo@347:         }
ingo@347:     }
ingo@147: }
ingo@147: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :