ingo@346: package de.intevation.flys.collections;
ingo@346: 
felix@1639: import java.util.ArrayList;
ingo@346: import java.util.List;
felix@1810: import java.util.HashMap;
ingo@346: import java.util.Map;
ingo@346: 
ingo@346: import org.apache.log4j.Logger;
ingo@346: 
felix@1709: import de.intevation.artifacts.ArtifactDatabase;
felix@1709: import de.intevation.artifacts.ArtifactDatabaseException;
ingo@346: 
ingo@346: import de.intevation.artifactdatabase.state.Facet;
ingo@346: import de.intevation.artifactdatabase.state.Output;
ingo@346: 
felix@1709: import de.intevation.flys.artifacts.FLYSArtifact;
ingo@346: import de.intevation.flys.artifacts.model.ManagedFacet;
ingo@346: 
felix@1634: /**
felix@1634:  * Create attribute- element of describe document of an ArtifactCollection.
felix@1634:  * The attribute-element contains the merged output of all outputmodes and
felix@1780:  * facets that are part of the collection.
felix@1634:  */
ingo@346: public class AttributeWriter {
ingo@346: 
felix@1709:     /** ArtifactDatabase used to fetch Artifacts. */
felix@1709:     protected ArtifactDatabase db = null;
felix@1709: 
ingo@346:     protected Map<String, Output> oldAttr;
felix@1709: 
ingo@346:     protected Map<String, Output> newAttr;
ingo@346: 
felix@1785:     /** List of already seen facets. */
felix@1785:     protected List<Facet>         oldFacets;
felix@1785: 
felix@1785:     /** List of "new" facets. */
felix@1785:     protected List<Facet>         newFacets;
felix@1785: 
felix@1785:     /**
felix@1785:      * "Compatibility matrix", mapws list of facet names to output names.
felix@1785:      * Any facet that is not found in the list for a specific output will
felix@1785:      * not be added to the resulting document.
felix@1785:      */
felix@1785:     protected Map<String, List<String>> compatibilities;
felix@1785: 
ingo@1976: 
ingo@1976:     /** The result of the <i>write()</i> operation.*/
ingo@1976:     protected CollectionAttribute attribute;
ingo@1976: 
ingo@1976: 
ingo@346:     private static Logger logger = Logger.getLogger(AttributeWriter.class);
ingo@346: 
ingo@346: 
felix@1634:     /**
felix@1634:      * Create a AttributeWriter.
felix@1634:      * Attributes not present in newAttr will not be included in the document.
felix@1709:      * @param db      Database to fetch artifacts.
felix@1634:      * @param oldAttr "Old" (possibly user-changed) outputs.
felix@1780:      * @param newAttr "New" (eventually re-read in its original, unchanged
felix@1634:      *                form) outputs.
felix@1634:      */
ingo@346:     public AttributeWriter(
felix@1709:         ArtifactDatabase    db,
ingo@1993:         CollectionAttribute attribute,
ingo@346:         Map<String, Output> oldAttr,
felix@1785:         List<Facet>         oldFacets,
felix@1785:         Map<String, Output> newAttr,
felix@1785:         List<Facet>         newFacets,
felix@1785:         Map<String, List<String>> matrix)
ingo@346:     {
felix@1785:         this.db        = db;
ingo@1993:         this.attribute = attribute;
felix@1785:         this.oldAttr   = oldAttr;
felix@1785:         this.newAttr   = newAttr;
felix@1785:         this.oldFacets = oldFacets;
felix@1785:         this.newFacets = newFacets;
felix@1785:         this.compatibilities = matrix;
ingo@346:     }
ingo@346: 
ingo@346: 
felix@1628:     /**
felix@1634:      * Create document by merging outputs given in
felix@1634:      * constructor.
felix@1780:      *
felix@1634:      * The "new" set rules about existance of attributes, so anything not
felix@1634:      * present in it will not be included in the resulting document.
felix@1634:      * The "old" set rules about the content of attributes (as user changes
felix@1634:      * are recorded here and not in the new set).
felix@1780:      *
felix@1634:      * @return document with merged outputs as described.
felix@1628:      */
ingo@1976:     protected CollectionAttribute write() {
sascha@1995:         for (Map.Entry<String, Output> entry: newAttr.entrySet()) {
sascha@1995:             String outName = entry.getKey();
sascha@1995:             Output a       = entry.getValue();
ingo@1996: 
ingo@2094: 
ingo@1996:             Output exists = attribute.getOutput(outName);
ingo@1996:             if (exists == null) {
ingo@1996:                 attribute.addOutput(outName, a);
ingo@1996:             }
ingo@1996: 
ingo@1993:             attribute.clearFacets(outName);
ingo@2094: 
ingo@2094:             if (logger.isDebugEnabled()) {
ingo@2094:                 logger.debug("Merge Output: " + outName);
ingo@2094:                 logger.debug("   old Facets: " + oldFacets.size());
ingo@2094:                 logger.debug("   new Facets: " + newFacets.size());
ingo@2094:             }
ingo@2094: 
ingo@1976:             writeOutput(a.getName(), newFacets, oldFacets);
ingo@346:         }
ingo@346: 
ingo@2079:         // THIS CALL IS ABSOLUTELY NECESSARY!
ingo@2079:         attribute.cleanEmptyOutputs();
ingo@2079: 
ingo@1976:         return attribute;
ingo@346:     }
ingo@346: 
felix@1634: 
felix@1628:     /**
felix@1785:      * @param outputName the "new" outputs name
felix@1785:      * @param newOutFacets Facets of the new outputs
felix@1785:      * @param oldOutFacets Facets of the old outputs (can be null)
felix@1628:      */
ingo@346:     protected void writeOutput(
ingo@1976:         String      outputName,
ingo@1976:         List<Facet> newOutFacets,
ingo@1976:         List<Facet> oldOutFacets
ingo@1976:     ) {
ingo@1976:         List<String> compatFacets = this.compatibilities.get(outputName);
ingo@2094: 
ingo@2094:         if (logger.isDebugEnabled() && compatFacets != null) {
ingo@2094:             logger.debug("Compabitle Facets:");
ingo@2094:             for (String compatible: compatFacets) {
ingo@2094:                 logger.debug(   "- " + compatible);
ingo@2094:             }
ingo@2094:         }
ingo@2094: 
felix@1709:         try {
ingo@1976:             writeFacets(outputName, newOutFacets, oldOutFacets, compatFacets);
felix@1709:         }
felix@1709:         catch (ArtifactDatabaseException ade) {
felix@1709:             logger.error(ade, ade);
felix@1709:         }
ingo@346:     }
ingo@346: 
ingo@346: 
felix@1628:     /**
felix@1785:      * @param newFacets the new facets
felix@1785:      * @param oldFacets the old facets
felix@1785:      * @param compatibleFacets List of facets to accept
felix@1831:      * @return true if any facets are written to the out.
felix@1628:      */
felix@1831:     protected boolean writeFacets(
ingo@1976:         String         outputName,
felix@1635:         List<Facet>    newFacets,
felix@1785:         List<Facet>    oldFacets,
felix@1785:         List<String>   compatibleFacets)
felix@1709:     throws ArtifactDatabaseException
ingo@346:     {
felix@1788:         if (compatibleFacets == null) {
felix@1788:             logger.warn("No compatible facets, not generating out.");
felix@1831:             return false;
felix@1788:         }
felix@1788: 
felix@1635:         int num = newFacets.size();
ingo@346: 
felix@1635:         // Add all new Facets either in their old state or (if really
felix@1635:         // new) as they are.
felix@1639:         List<ManagedFacet> currentFacets      = new ArrayList<ManagedFacet>();
felix@1639:         List<ManagedFacet> genuinelyNewFacets = new ArrayList<ManagedFacet>();
felix@1785: 
ingo@346:         for (int i = 0; i < num; i++) {
felix@1635:             ManagedFacet facet = (ManagedFacet) newFacets.get(i);
ingo@2094: 
ingo@2094:             logger.debug("Try to add Facet: " + facet.getName());
ingo@2094: 
felix@1785:             if (!compatibleFacets.contains(facet.getName())) {
felix@1785:                 //logger.debug("Have incompatible facet, skip: " + facet.getName());
felix@1785:                 continue;
felix@1785:             }
felix@1785:             //else logger.debug("Have compatible facet: " + facet.getName());
ingo@346: 
felix@1635:             ManagedFacet picked = pickFacet(facet, oldFacets);
felix@1785: 
felix@1639:             if (facet.equals(picked)) {
felix@1639:                 genuinelyNewFacets.add(picked);
felix@1639:             }
felix@1639:             else {
felix@1639:                 currentFacets.add(picked);
felix@1639:             }
felix@1639:         }
felix@1639: 
felix@1709:         // With each genuinely new Facet, ask Artifact whether it comes to live
felix@1709:         // in/activate.
felix@1709:         for (ManagedFacet newMF: genuinelyNewFacets) {
felix@1709:             FLYSArtifact flys = (FLYSArtifact) db.getRawArtifact(newMF.getArtifact());
felix@1709:             newMF.setActive(flys.getInitialFacetActivity(
ingo@1976:                 outputName,
felix@1709:                 newMF.getName(),
felix@1709:                 newMF.getIndex()));
felix@1709:         }
ingo@1976: 
felix@1639:         // For each genuinely new Facet check positional conflicts.
felix@1639:         for (ManagedFacet newMF: genuinelyNewFacets) {
felix@1639:             boolean conflicts = true;
felix@1639:             // Loop until all conflicts resolved.
felix@1639:             while (conflicts) {
felix@1639:                 conflicts = false;
felix@1639:                 for (ManagedFacet oldMF: currentFacets) {
felix@1639:                     if (newMF.getPosition() == oldMF.getPosition()) {
felix@1639:                         conflicts = true;
felix@1639:                         logger.debug("Positional conflict while merging " +
felix@1780:                             "facets, pushing newest facet 1 up (" + newMF.getPosition() + ")");
felix@1639:                         newMF.setPosition(newMF.getPosition() + 1);
felix@1639:                         break;
felix@1639:                     }
felix@1639:                 }
felix@1639:             }
felix@1639:             currentFacets.add(newMF);
felix@1639:         }
felix@1639: 
felix@1810:         // Fill/correct "gaps" (e.g. position 1,2,5 are taken, after gap filling
felix@1810:         // expect positions 1,2,3 [5->3])
felix@1810:         // Preparations to be able to detect gaps.
felix@1810:         Map<Integer, ManagedFacet> mfmap = new HashMap<Integer, ManagedFacet>();
felix@1810:         int max = 0;
felix@1810:         for (ManagedFacet mf: currentFacets) {
felix@1810:             int pos = mf.getPosition();
felix@1810:             mfmap.put(Integer.valueOf(pos), mf);
felix@1810:             if (pos > max) max = pos;
felix@1810:         }
felix@1810: 
felix@1810:         // Finally do gap correction.
felix@1810:         if (max != currentFacets.size()) {
felix@1810:             int gap = 0;
felix@1810:             for (int i = 1; i <= max; i++) {
felix@1810:                 ManagedFacet mf = mfmap.get(Integer.valueOf(i));
felix@1810:                 if (mf == null) {
felix@1810:                     gap++;
felix@1810:                     continue;
felix@1810:                 }
felix@1810:                 mf.setPosition(mf.getPosition() - gap);
felix@1810:             }
felix@1810:         }
felix@1810: 
felix@1639:         // Now add all facets.
felix@1639:         for (ManagedFacet oldMF: currentFacets) {
ingo@1976:             attribute.addFacet(outputName, oldMF);
ingo@346:         }
felix@1831: 
felix@1831:         return currentFacets.size() > 0;
ingo@346:     }
ingo@346: 
ingo@346: 
felix@1628:     /**
felix@1635:      * Returns the facet to be added to Document.
felix@1635:      * Return the new facet only if the "same" facet was not present before.
felix@1635:      * Return the "old" facet otherwise (user-defined information sticks
felix@1635:      * to it).
felix@1635:      * @param facet     the new facet.
felix@1635:      * @param oldFacets the old facets, new facet is compared against each of
felix@1635:      *                  these.
felix@1635:      * @return facet if genuinely new, matching old facet otherwise.
felix@1628:      */
ingo@1976:     protected ManagedFacet pickFacet(ManagedFacet facet, List<Facet> oldFacets)
felix@1709:     {
felix@1635:         if (oldFacets == null) {
felix@1635:             logger.debug("No old facets to compare a new to found.");
felix@1635:             return facet;
ingo@346:         }
ingo@1976: 
felix@1635:         String hash = facet.getName() + facet.getIndex() + facet.getArtifact();
ingo@346: 
felix@1635:         // Compare "new" facet with all old facets.
felix@1635:         // Take oldFacet if that facet was already present (otherwise
felix@1635:         // information is lost, the new one otherwise.
felix@1635:         for (Facet oFacet: oldFacets) {
felix@1635:             ManagedFacet oldFacet = (ManagedFacet) oFacet;
felix@1635:             String oldHash = oldFacet.getName()
felix@1635:                            + oldFacet.getIndex()
felix@1635:                            + oldFacet.getArtifact();
felix@1635:             if (hash.equals(oldHash)) {
felix@1635:                 return oldFacet;
ingo@346:             }
ingo@346:         }
felix@1635:         return facet;
ingo@346:     }
ingo@346: }
sascha@705: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :