Subversion Repositories SmartDukaan

Rev

Rev 1050 | Blame | Last modification | View Log | RSS feed

/**
 * 
 */
package in.shop2020.util;

import in.shop2020.metamodel.core.Bullet;
import in.shop2020.metamodel.core.Entity;
import in.shop2020.metamodel.core.Feature;
import in.shop2020.metamodel.core.PrimitiveDataObject;
import in.shop2020.metamodel.core.Slide;
import in.shop2020.metamodel.definitions.BulletDefinition;
import in.shop2020.metamodel.definitions.Catalog;
import in.shop2020.metamodel.definitions.Category;
import in.shop2020.metamodel.definitions.DatatypeDefinition;
import in.shop2020.metamodel.definitions.DefinitionsContainer;
import in.shop2020.metamodel.definitions.EntityContainer;
import in.shop2020.metamodel.definitions.FacetDefinition;
import in.shop2020.metamodel.definitions.FacetRuleDefinition;
import in.shop2020.metamodel.definitions.FeatureDefinition;
import in.shop2020.metamodel.definitions.SlideDefinition;
import in.shop2020.metamodel.util.CreationUtils;
import in.shop2020.metamodel.util.ExpandedBullet;
import in.shop2020.metamodel.util.ExpandedBulletDefinition;
import in.shop2020.metamodel.util.ExpandedCategoryFacetDefinition;
import in.shop2020.metamodel.util.ExpandedEntity;
import in.shop2020.metamodel.util.ExpandedFacetDefinition;
import in.shop2020.metamodel.util.ExpandedFacetRuleDefinition;
import in.shop2020.metamodel.util.ExpandedFeature;
import in.shop2020.metamodel.util.ExpandedFeatureDefinition;
import in.shop2020.metamodel.util.ExpandedSlide;
import in.shop2020.model.v1.catalog.CatalogService.Client;
import in.shop2020.model.v1.catalog.Item;
import in.shop2020.model.v1.catalog.status;
import in.shop2020.thrift.clients.CatalogServiceClient;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;


/**
 * Command line utility to convert IR Definitions into IR Data and IR Meta-data
 * 
 * Usage: IR [irmetadata|irdata] {Category ID}
 * 
 * @author naveen
 *
 */
public class IR {
        
        /**
         * 
         */
        private long categoryID;
        
        /**
         * Level - 4, 8, 12, 16
         */
        private String[] xmlIndentation = {"", "    ", "        ", "            ", 
                        "                "};

        private String[] xmlTabIndentation = {"", "\t", "\t\t", "\t\t\t", 
                        "\t\t\t\t", "\t\t\t\t\t", "\t\t\t\t\t\t"};

        private Map<Long, List<String>> facetIDFacetValues = 
                new HashMap<Long, List<String>>();

        CatalogServiceClient catalogServiceClient = null;
        in.shop2020.model.v1.catalog.InventoryService.Client client = null;
        
        /**
         * @param args
         * @throws Exception 
         */
        public static void main(String[] args) throws Exception {
                String[] commands = new String[] {"irmetadata", "irdata"};
                
                String usage = "Usage: IR ["+ StringUtils.join(commands, "|") +
                        "] {Category ID}\n";
                
                if(args.length < 1) {
                        System.out.println(usage);
                        System.exit(-1);
                }
                
                String inputCommand = args[0];
                
                if(!ArrayUtils.contains(commands, inputCommand)) {
                        System.out.println(usage);
                        System.exit(-1);
                }

                long categoryID = 0L;
                if(args.length > 1) {
                        try {
                                categoryID = Long.parseLong(args[1]);
                        }
                        catch (NumberFormatException nfe) {
                                System.out.println(usage);
                                System.exit(-1);
                        }
                }
                
                IR ir = new IR(categoryID);
                
                if (inputCommand.equals("irdata")) {
                        ir.exportIRData();
                        ir.transformIrDataXMLtoSolrXML();
                        return;
                }
                
                if (inputCommand.equals("irmetadata")) {
                        ir.exportIRMetaData();
                        ir.transformIrMetaDataXMLSolrSchemaXML();
                        return;
                }
                
        }
        
        /**
         * 
         * @param categoryID
         * @throws Exception 
         */
        public IR(long categoryID) throws Exception {
                this.categoryID = categoryID;
                catalogServiceClient = new CatalogServiceClient();
                client = catalogServiceClient.getClient();
        }
        
        
        /**
         * 
         * @param inXMLFilename
         * @param xslFilename
         * @param outXMLFilename
         * @throws Exception 
         */
        
        public void xsltTransformation(String inXMLFilename, String xslFilename, String outXMLFilename) throws Exception{
                // Use the static TransformerFactory.newInstance() method to instantiate 
                  // a TransformerFactory. The javax.xml.transform.TransformerFactory 
                  // system property setting determines the actual class to instantiate --
                  // org.apache.xalan.transformer.TransformerImpl.
                TransformerFactory tFactory = TransformerFactory.newInstance();
                        
                        // Use the TransformerFactory to instantiate a Transformer that will work with  
                        // the stylesheet you specify. This method call also processes the stylesheet
                  // into a compiled Templates object.
                Transformer transformer = tFactory.newTransformer(new StreamSource(xslFilename));

                        // Use the Transformer to apply the associated Templates object to an XML document
                        // (foo.xml) and write the output to a file (foo.out).
                transformer.transform(new StreamSource(inXMLFilename), new StreamResult(new FileOutputStream(outXMLFilename)));
                
        }
        
        /**
         * 
         * @throws Exception
         */
        public void transformIrDataXMLtoSolrXML() throws Exception {
                String irDataFilename = Utils.EXPORT_IR_PATH + "irdata.xml";
                String irSolrDataFilename = Utils.EXPORT_SOLR_PATH + "irdata_solr.xml";
                String irXslFilename = "src/xsl/irdata_solrdata.xsl";
                System.out.println(irSolrDataFilename);
                File solrFile = new File(irSolrDataFilename);
                if(!solrFile.exists()){
                        solrFile.createNewFile();
                }
                xsltTransformation(irDataFilename, irXslFilename, irSolrDataFilename);
        }
        

        /**
         * 
         * @throws Exception
         */
        public void transformIrMetaDataXMLSolrSchemaXML() throws Exception {
                String irDataFilename = Utils.EXPORT_IR_PATH + "irmetadata.xml";
                String irSolrDataFilename = Utils.EXPORT_SOLR_PATH + "irmetadata_solrschema.xml";
                String irXslFilename = "src/xsl/irmetadata_solrschema.xsl";
                System.out.println(irSolrDataFilename);
                File solrFile = new File(irSolrDataFilename);
                if(!solrFile.exists()){
                        solrFile.createNewFile();
                }
                xsltTransformation(irDataFilename, irXslFilename, irSolrDataFilename);
        }
        
        

        /**
         * 
         * @throws Exception
         */
        public void exportIRMetaData() throws Exception {
                DefinitionsContainer defs = 
                        Catalog.getInstance().getDefinitionsContainer();
                                
                // <IRMetaData>
                List<String> xmlSnippets = new ArrayList<String>();
                xmlSnippets.add("<IRMetaData>");
                xmlSnippets.add("\t<Facets>");
                
                IRMetaDataJythonWrapper jy = new IRMetaDataJythonWrapper();
                
                List<Long> facetFeatures = new ArrayList<Long>();
                
                // Iterate over all facet definitions
                Map<Long, FacetDefinition> facetDefs = defs.getFacetDefinitions();
                for(FacetDefinition facetDef : facetDefs.values()) {
                        
                        jy.reset();
                        jy.initialize();

                        ExpandedFacetDefinition expFacetDef = 
                                new ExpandedFacetDefinition(facetDef);
                        
                        jy.setExpandedFacetDefinition(expFacetDef);
                        
                        jy.executeRule();
                        
                        String facetXMLSnip = jy.getXMLSnippet();
                        Utils.info("facetXMLSnip=" + facetXMLSnip);
                        
                        xmlSnippets.add(facetXMLSnip);
                        
                        facetFeatures.add(new Long(expFacetDef.getFeatureDefinitionID()));
                }
                xmlSnippets.add("\t</Facets>");
                
                Utils.info("facetFeatures=" + facetFeatures);
                
                xmlSnippets.add("\n\t<Properties>");
                
                // Iterate over all feature definitions
                Map<Long, FeatureDefinition> featureDefs = defs.getFeatureDefinitions();
                for(FeatureDefinition featureDef : featureDefs.values()) {
                        if(facetFeatures.contains(new Long(featureDef.getID()))) {
                                // Ignore, is already covered as facet
                                continue;
                        }
                        String propertyXMLSnip = this.getPropertyXMLSnippet(featureDef);
                        Utils.info("propertyXMLSnip=" + propertyXMLSnip);
                        
                        xmlSnippets.add(propertyXMLSnip);
                }
                
                xmlSnippets.add("\t</Properties>");
                
                xmlSnippets.add("\n\t<Categories>");
                
                // Iterate over all categories
                /*
                Category rootCategory = defs.getCategory(Catalog.getInstance().getRootCategory().getID());
                List<Category> children = rootCategory.getChildrenCategory();
                for (Category child : children) {

                        String categoryXMLSnip = this.getCategoryXMLSnippet(child, 2);
                        Utils.info("categoryXMLSnip=" + categoryXMLSnip);
                        
                        xmlSnippets.add(categoryXMLSnip);
                }
                */
                
                Category rootCategory = defs.getCategory(Catalog.getInstance().getRootCategory().getID());
                String categoryXMLSnip = this.getCategoryXMLSnippet(rootCategory, 2);
                Utils.info("categoryXMLSnip=" + categoryXMLSnip);
                
                xmlSnippets.add(categoryXMLSnip);
        

                xmlSnippets.add("\t</Categories>");
                
                // </IRMetaData>
                xmlSnippets.add("</IRMetaData>");
                
                String irMetaDataXML = StringUtils.join(xmlSnippets, "\n");
                Utils.info(irMetaDataXML);
                
                // Write it to file
                String irMetaDataFilename = Utils.EXPORT_IR_PATH + "irmetadata.xml";
                DBUtils.store(irMetaDataXML, irMetaDataFilename);
        }
        
        /**
         * 
         * @param category
         * @return
         * @throws Exception 
         */
        private String getCategoryXMLSnippet(Category category, int indent) 
                throws Exception {
                
                DefinitionsContainer defs = 
                        Catalog.getInstance().getDefinitionsContainer();
                
                String xmlSnip = this.xmlTabIndentation[indent] + "<Category";
                
                xmlSnip += " ID=\"" + category.getID() + "\"";
                xmlSnip += " label=\"" + category.getLabel() + "\"";
                xmlSnip += ">\n";
                
                List<Category> children = category.getChildrenCategory();
                
                if(children != null) {
                        for(Category child : children) {
                                xmlSnip += this.getCategoryXMLSnippet(child, indent+1);
                        }
                }
                else {
                        // Only leaf category will have entities
                        // Facet IDs
                        xmlSnip += this.xmlTabIndentation[indent+1] + "<Facets>\n";
                        
                        List<Long> facetDefIDs = 
                                defs.getFacetDefinitionIDs(category.getID());
                        Utils.info("facetDefIDs=" + facetDefIDs);
                        
                        
                        
                        if( facetDefIDs != null && !facetDefIDs.isEmpty() ){
                                        for(Long facetDefID : facetDefIDs) {
                                        xmlSnip += this.xmlTabIndentation[indent+2] + 
                                                "<FacetID>" + facetDefID + "</FacetID>\n";
                                }
                        }
                        
                        
                        xmlSnip += this.xmlTabIndentation[indent+1] + "</Facets>\n\n";
                        
                        // Feature IDs
                        xmlSnip += this.xmlTabIndentation[indent+1] + "<Properties>\n";

                        List<Long> featureDefIDs = 
                                defs.getFeatureDefinitionIDs(category.getID());
                        Utils.info("featureDefIDs=" + featureDefIDs);
                        
                        for(Long featureDefID : featureDefIDs) {
                                xmlSnip += this.xmlTabIndentation[indent+2] + 
                                        "<FeatureID>" + featureDefID + "</FeatureID>\n";
                        }
                        
                        xmlSnip += this.xmlTabIndentation[indent+1] + "</Properties>\n";
                }
                
                xmlSnip += this.xmlTabIndentation[indent] + "</Category>\n";
                
                return xmlSnip;
        }
        
        /**
         * 
         * @param featureDef
         * @return
         * @throws Exception 
         */
        private String getPropertyXMLSnippet(FeatureDefinition featureDef) 
                throws Exception {
                String xmlSnip = "\t\t<Property";
                
                xmlSnip += " ID=\"" + featureDef.getID() + "\"";
                xmlSnip += " label=\"" + featureDef.getLabel() + "\"";
                        
                ExpandedFeatureDefinition expFeatureDef = 
                        new ExpandedFeatureDefinition(featureDef);
                
                ExpandedBulletDefinition expBulletDef = 
                        expFeatureDef.getExpandedBulletDefinition();
                
                String datatype = "string";
                if(expBulletDef.isComposite() || expBulletDef.isEnumerated()) {
                        datatype = "string";
                }
                else {
                        DatatypeDefinition datatypeDef = 
                                expBulletDef.getDatatypeDefinition();
                        
                        datatype = datatypeDef.getName();
                        
                        // REVISIT
                        if(datatype.equals("hours_mins") || datatype.equals("days_hours") ||
                                        datatype.equals("hours_mins") || 
                                        datatype.equals("days_hours")) {
                                datatype = "string";
                        }
                }
                xmlSnip += " datatype=\"" + datatype + "\"";
                
                String multivalue = "false";
                if (expBulletDef.isMultivalue()) {
                        multivalue = "true";
                }
                xmlSnip += " isMultivalue=\"" + multivalue + "\"";
                
                xmlSnip += "/>";
                return xmlSnip;
        }
        
        /**
         * @throws Exception 
         * 
         */
        public void exportIRData() throws Exception {
                DefinitionsContainer defs = 
                        Catalog.getInstance().getDefinitionsContainer();
                
                EntityContainer ents = 
                        Catalog.getInstance().getEntityContainer();
                

                
                // <IRData>
                List<String> entityXMLSnippets = new ArrayList<String>();
                entityXMLSnippets.add("<IRData>");
                
                List<Category> categories = null;
                if(this.categoryID != 0L) {
                        categories = new ArrayList<Category>();
                        categories.add(defs.getCategory(this.categoryID));
                }
                else {
                        // Get all categories
                        categories = defs.getChildrenCategories(10001);
                }
                
                int i=0;
                for(Category cat : categories) {
                        long catID = cat.getID();
                        if(i>0){
                                continue;
                        }
                        i++;
                        // Get all facets for the category
                        ExpandedCategoryFacetDefinition expCategoryFacetDef = 
                                defs.getExpandedCategoryFacetDefinition(catID);
                        System.out.println(expCategoryFacetDef);
                        List<ExpandedFacetRuleDefinition> expFacetRuleDefs = 
                                expCategoryFacetDef.getExpandedFacetRuleDefinitions();
                        System.out.println(expFacetRuleDefs);
                        // Get all entities for the category
                        //FIXME
                        Collection<Entity> entities = CreationUtils.getEntities(catID);
                        
                        if(entities == null) {
                                continue;
                        }

                        
                        //Modified by Rajveer, Now it will search according to each catalog item.
                        // For each entity in catalog
                        
                        
                        List<Item> items = client.getAllItemsByStatus(status.ACTIVE);
                        Set<Long> processedCatalogItems = new HashSet<Long>();
                        Date todate = new Date();
                        for(Item item : items){
                                long entityID = item.getCatalogItemId();
                                if(todate.getTime() < item.getStartDate() ){
                                        continue;
                                }
                                if(processedCatalogItems.contains(entityID)){
                                        continue;
                                }
                                processedCatalogItems.add(entityID);
                                
                                ExpandedEntity expEntity = 
                                        ents.getExpandedEntity(entityID);
                                //Skip if entity doesnt exist
                                if(expEntity == null){
                                        continue;
                                }
                                List<String> facetXMLSnippets = new ArrayList<String>();
                                
                                // Collect features which have become FACETs
                                List<Long> facetFeatureIDs = new ArrayList<Long>();
                                
                                Set<Long> processedFacets = new HashSet<Long>();
                                // For each facet execute Rule
                                for(ExpandedFacetRuleDefinition expFacetRuleDef : 
                                                expFacetRuleDefs) {
                                        System.out.println(expFacetRuleDef.getFacetDefinitionID());
                                        String facetXMLSnip = 
                                                this.processFacet(expEntity, expFacetRuleDef);
                                        
                                        if(facetXMLSnip != null && 
                                                        !StringUtils.trim(facetXMLSnip).isEmpty()) {
                                                facetXMLSnippets.add(facetXMLSnip);
                                                Utils.info("1 facetXMLSnip=" + facetXMLSnip);
                                                processedFacets.add(expFacetRuleDef.getFacetDefinitionID());
                                        }
                                        
                                        // Collect features already covered as Facet
                                        if(expFacetRuleDef.getFeatureDefinition() != null) {
                                                facetFeatureIDs.add(new Long(
                                                                expFacetRuleDef.getFeatureDefinitionID()));
                                        }
                                }
                                Entity entity = CreationUtils.getEntity(entityID);
                                // Handle borrowed slides
                                List<Slide> borrowedSlides = ents.getBorrowedSlides(entity);
                                
                                for (Slide borrowedSlide : borrowedSlides) {
                                        if(borrowedSlide.getBorrowedCategoryID() == -1){
                                                continue;
                                        }
                                        String facetXMLSnip = this.processBorrowedSlide(expEntity,
                                                        borrowedSlide, processedFacets);
                                        
                                        if(facetXMLSnip != null && 
                                                        !StringUtils.trim(facetXMLSnip).isEmpty()) {
                                                facetXMLSnippets.add(facetXMLSnip);
                                        }
                                        
                                        Utils.info("2 facetXMLSnip=" + facetXMLSnip);
                                }
                                
                                String facetXMLSnippetsStr = 
                                        StringUtils.join(facetXMLSnippets, "\n");
                                        
                                Utils.info("facetXMLSnippetsStr=" + facetXMLSnippetsStr);
                                
                                // Collect PROPERTIES
                                String propertiesXMLSnippetsStr = 
                                        this.getPropertiesXMLSnippet(expEntity, facetFeatureIDs, 2);
                                
                                Utils.info(propertiesXMLSnippetsStr);
                                
                                String entityXMLSnip = this.getEntityXMLSnippet(expEntity, 
                                                facetXMLSnippetsStr, propertiesXMLSnippetsStr, 1);
                                
                                Utils.info(entityXMLSnip);
                                
                                entityXMLSnippets.add(entityXMLSnip);
                        }
                }
                
                        
                        /*
                        // For each entity 
                        for(Entity entity : entities) {
                                ExpandedEntity expEntity = 
                                        ents.getExpandedEntity(entity.getID());
                                //Skip if entity doesnt exist
                                if(expEntity == null){
                                        continue;
                                }
                                List<String> facetXMLSnippets = new ArrayList<String>();
                                
                                // Collect features which have become FACETs
                                List<Long> facetFeatureIDs = new ArrayList<Long>();
                                
                                // For each facet execute Rule
                                for(ExpandedFacetRuleDefinition expFacetRuleDef : 
                                                expFacetRuleDefs) {
                                        String facetXMLSnip = 
                                                this.processFacet(expEntity, expFacetRuleDef);
                                        
                                        if(facetXMLSnip != null && 
                                                        !StringUtils.trim(facetXMLSnip).isEmpty()) {
                                                facetXMLSnippets.add(facetXMLSnip);
                                                Utils.info("1 facetXMLSnip=" + facetXMLSnip);
                                        }
                                        
                                        // Collect features already covered as Facet
                                        if(expFacetRuleDef.getFeatureDefinition() != null) {
                                                facetFeatureIDs.add(new Long(
                                                                expFacetRuleDef.getFeatureDefinitionID()));
                                        }
                                }
                                
                                // Handle borrowed slides
                                List<Slide> borrowedSlides = ents.getBorrowedSlides(entity);
                                
                                for (Slide borrowedSlide : borrowedSlides) {
                                        String facetXMLSnip = this.processBorrowedSlide(expEntity,
                                                        borrowedSlide);
                                        
                                        if(facetXMLSnip != null && 
                                                        !StringUtils.trim(facetXMLSnip).isEmpty()) {
                                                facetXMLSnippets.add(facetXMLSnip);
                                        }
                                        
                                        Utils.info("2 facetXMLSnip=" + facetXMLSnip);
                                }
                                
                                String facetXMLSnippetsStr = 
                                        StringUtils.join(facetXMLSnippets, "\n");
                                        
                                Utils.info("facetXMLSnippetsStr=" + facetXMLSnippetsStr);
                                
                                // Collect PROPERTIES
                                String propertiesXMLSnippetsStr = 
                                        this.getPropertiesXMLSnippet(expEntity, facetFeatureIDs, 2);
                                
                                Utils.info(propertiesXMLSnippetsStr);
                                
                                String entityXMLSnip = this.getEntityXMLSnippet(expEntity, 
                                                facetXMLSnippetsStr, propertiesXMLSnippetsStr, 1);
                                
                                Utils.info(entityXMLSnip);
                                
                                entityXMLSnippets.add(entityXMLSnip);
                        }
                }
                */
                        
                // </IRData>
                entityXMLSnippets.add("</IRData>");
                
                String irDataXML = StringUtils.join(entityXMLSnippets, "\n");
                Utils.info(irDataXML);
                
                // Write it to file
                String irDataFilename = Utils.EXPORT_IR_PATH + "irdata.xml";
                DBUtils.store(irDataXML, irDataFilename);
                
                Utils.info("this.facetIDFacetValues=" + this.facetIDFacetValues);
                CreationUtils.storeFacetValues(facetIDFacetValues);
                // Store facet values
                
        }
        
        /**
         * 
         * @param borrowedSlides
         * @return
         */
        private String processBorrowedSlide(ExpandedEntity expEntity, 
                        Slide borrowedSlide, Set<Long> processedFacets) throws Exception {
                // Borrowed category ID
                long borrowedCategoryID = borrowedSlide.getBorrowedCategoryID();
                Utils.info("borrowedCategoryID=" + borrowedCategoryID);
                        
                // Slide definition ID
                long slideDefID = borrowedSlide.getSlideDefinitionID();
                Utils.info("borrowed slideDefID=" + slideDefID);
                
                DefinitionsContainer defs = 
                        Catalog.getInstance().getDefinitionsContainer();
                
                // Get IR Data Rule
                FacetRuleDefinition facetRuleDef = 
                        defs.getFacetRuleDefinitionForSlide(borrowedCategoryID, slideDefID);
                
                Utils.info("borrowed facetRuleDef=" + facetRuleDef);
                String facetXMLSnippet = null; 
                
                // If there is direct IR data rule defined for borrowed slide
                if(facetRuleDef != null) {
                        ExpandedFacetRuleDefinition expFacetRuleDef = 
                                new ExpandedFacetRuleDefinition(facetRuleDef);
                        

                        if(!processedFacets.contains(expFacetRuleDef.getFacetDefinitionID())){
                                //      Return XML snippet                      
                                facetXMLSnippet = this.processFacet(expEntity, expFacetRuleDef);
                                processedFacets.add(expFacetRuleDef.getFacetDefinitionID());
                        }
                }
                else {
                        List<String> facetXMLSnippets = new ArrayList<String>();
                        
                        // Get FacetRuleDefinition objects for all features in 
                        // borrowed slide for which IR rule is defined
                        List<Feature> features = borrowedSlide.getFeatures();
                        for (Feature feature : features) {
                                String featureFacetXMLSnippet = 
                                        this.processBorrowedFeature(borrowedCategoryID, expEntity, feature, processedFacets);
                                
                                if(featureFacetXMLSnippet != null && !StringUtils.trim(
                                                featureFacetXMLSnippet).isEmpty()) {
                                        facetXMLSnippets.add(featureFacetXMLSnippet);
                                }
                        }
                        
                        // Get FacetRuleDefinition objects for all children slides in 
                        // borrowed slide for which IR rule is defined
                        if(borrowedSlide.hasChildrenSlides()) {
                                String childrenSlidesFacetXMLSnippet = 
                                        this.processBorrowedChildrenSlides(borrowedCategoryID, 
                                                        expEntity, borrowedSlide, processedFacets);

                                if(childrenSlidesFacetXMLSnippet != null && !StringUtils.trim(
                                                childrenSlidesFacetXMLSnippet).isEmpty()) {
                                        facetXMLSnippets.add(childrenSlidesFacetXMLSnippet);
                                }
                        }
                        
                        facetXMLSnippet = StringUtils.join(facetXMLSnippets, "\n");
                }

                
                if(facetXMLSnippet == null || StringUtils.trim(facetXMLSnippet).isEmpty()) {
                        return null;
                }
                
                return facetXMLSnippet;
        }
        
        /**
         * 
         * @param borrowedCategoryID
         * @param expEntity
         * @param feature
         * @return
         * @throws Exception 
         */
        private String processBorrowedFeature(long borrowedCategoryID, 
                        ExpandedEntity expEntity, Feature feature, Set<Long> processedFacets) throws Exception {
        
                long featureDefID = feature.getFeatureDefinitionID();
                Utils.info("borrowed featureDefID=" + featureDefID);
                
                DefinitionsContainer defs = 
                        Catalog.getInstance().getDefinitionsContainer();
                
                FacetRuleDefinition facetRuleDefForFeature = 
                        defs.getFacetRuleDefinitionForFeature(borrowedCategoryID, 
                                featureDefID);
                
                List<String> facetXMLSnippets = new ArrayList<String>();
                
                Utils.info("borrowed facetRuleDefForFeature=" + 
                                facetRuleDefForFeature);
                
                if(facetRuleDefForFeature != null) {
                        ExpandedFacetRuleDefinition expFacetRuleDefForFeature = 
                                new ExpandedFacetRuleDefinition(facetRuleDefForFeature);
                        if(!processedFacets.contains(expFacetRuleDefForFeature.getFacetDefinitionID())){
                                        
                                String snip = this.processFacet(expEntity, 
                                                expFacetRuleDefForFeature);
                                
                                if(snip != null) {
                                        facetXMLSnippets.add(snip);
                                        processedFacets.add(expFacetRuleDefForFeature.getFacetDefinitionID());
                                }
                        }
                }


                String xmlSnip =  StringUtils.join(facetXMLSnippets, "\n");
                
                if(StringUtils.trim(xmlSnip).isEmpty()) {
                        return null;
                }
                
                return xmlSnip;
        }
        
        /**
         * 
         * @param expEntity
         * @param borrowedSlide
         * @return
         * @throws Exception 
         */
        private String processBorrowedChildrenSlides(long borrowedCategoryID, 
                        ExpandedEntity expEntity, Slide borrowedSlide, Set<Long> processedFacets) throws Exception {
                DefinitionsContainer defs = 
                        Catalog.getInstance().getDefinitionsContainer();
        
                List<Slide> childrenSlides = borrowedSlide.getChildrenSlides();
                List<String> facetXMLSnippets = new ArrayList<String>();
                
                for (Slide childSlide : childrenSlides) {
                        long childSlideDefID = childSlide.getSlideDefinitionID();
                        Utils.info("borrowed childSlideDefID=" + childSlideDefID);
                        
                        FacetRuleDefinition facetRuleDefForSlide = 
                                defs.getFacetRuleDefinitionForSlide(borrowedCategoryID, 
                                                childSlideDefID);
                        
                        Utils.info("borrowed facetRuleDefForSlide=" + 
                                        facetRuleDefForSlide);
                        
                        if(facetRuleDefForSlide != null) {
                                ExpandedFacetRuleDefinition expFacetRuleDefForSlide = 
                                        new ExpandedFacetRuleDefinition(
                                                        facetRuleDefForSlide);
                                if(!processedFacets.contains(expFacetRuleDefForSlide.getFacetDefinitionID())){
                                        String snip = this.processFacet(expEntity, 
                                                        expFacetRuleDefForSlide);
                                        
                                        if(snip != null && !StringUtils.trim(snip).isEmpty()) {
                                                facetXMLSnippets.add(snip);
                                                processedFacets.add(expFacetRuleDefForSlide.getFacetDefinitionID());
                                        }
                                }
                        }
                        
                        // Features?
                        if(childSlide.hasFeatures()) {
                                List<Feature> features = childSlide.getFeatures();
                                for(Feature feature : features) {
                                        String childrenSlideFeatureFacetXMLSnippet = 
                                                this.processBorrowedFeature(borrowedCategoryID, expEntity, 
                                                        feature, processedFacets);
                                        
                                        if(childrenSlideFeatureFacetXMLSnippet != null &&
                                                !StringUtils.trim(childrenSlideFeatureFacetXMLSnippet).
                                                        isEmpty()) {
                                                
                                                facetXMLSnippets.add(
                                                                childrenSlideFeatureFacetXMLSnippet);
                                                
                                        }
                                }
                        }
                        
                        // Children slides
                        if(childSlide.hasChildrenSlides()) {
                                String childrenSlidesFacetXMLSnippet = 
                                        this.processBorrowedChildrenSlides(borrowedCategoryID, 
                                                        expEntity, childSlide, processedFacets);

                                if(childrenSlidesFacetXMLSnippet != null &&
                                        !StringUtils.trim(childrenSlidesFacetXMLSnippet).
                                                isEmpty()) {
                                        
                                        facetXMLSnippets.add(childrenSlidesFacetXMLSnippet);
                                }
                        }
                }

                String xmlSnip =  StringUtils.join(facetXMLSnippets, "\n");
                
                if(StringUtils.trim(xmlSnip).isEmpty()) {
                        return null;
                }
                
                return xmlSnip;
        }

        
        /**
         * 
         * @param expEntity
         * @param expFacetDef
         * @return
         * @throws Exception 
         */
        @SuppressWarnings("unchecked")
        private String processFacet(ExpandedEntity expEntity, 
                        ExpandedFacetRuleDefinition expFacetRuleDef) throws Exception {
                
                Utils.info("expFacetRuleDef=" + expFacetRuleDef);

                Utils.info("expEntity.getID()=" + expEntity.getID());
                
                // to get the price from services and pass it on to fill in solr
                
//              CatalogServiceClient catalogServiceClient = new CatalogServiceClient();
//              in.shop2020.model.v1.catalog.InventoryService.Client client = catalogServiceClient.getClient();
                
                double itemPrice = 0;
                List<Item> items = client.getItemsByCatalogId(expEntity.getID());
                Date todate = new Date();
                for(Item item: items){
                        if(item.getItemStatus() != status.ACTIVE || todate.getTime() < item.getStartDate()){
                                continue;
                        }
                        if(itemPrice == 0){
                                itemPrice = item.getSellingPrice();
                        }else if(itemPrice>item.getSellingPrice()){
                                itemPrice = item.getSellingPrice();
                        }
                        
                }
                
                EntityContainer ents = 
                        Catalog.getInstance().getEntityContainer();
                
                DefinitionsContainer defs = 
                        Catalog.getInstance().getDefinitionsContainer();
                
                IRDataJythonWrapper jw = new IRDataJythonWrapper();
                
                jw.setExpandedEntity(expEntity);
                jw.setExpandedFacetRuleDefinition(expFacetRuleDef);
                jw.setEntityPrice(itemPrice);
                
                // Set FeatureDefinition
                FeatureDefinition featureDef = 
                        expFacetRuleDef.getFeatureDefinition();
                
                if(featureDef != null) {
                        jw.setFeatureDefinition(featureDef);
                        
                        // Set Feature
                        Utils.info("featureDef.getID()=" + featureDef.getID());
                        
                        Feature feature = 
                                ents.getFeature(expEntity.getID(), featureDef.getID());
                        
                        // Special case for Brand
                        if(expFacetRuleDef.getFacetDefinitionID() == 50001 ||
                           expFacetRuleDef.getFacetDefinitionID() == 50010 ||
                                expFacetRuleDef.getFacetDefinitionID() == 50011) {
                                
                                // Execute Python script
                                jw.executeRule();
                                
                        }
                        
                        // Can happen when to slide's feature which is dropped in favor 
                        // of a borrowed slide
                        else if(feature != null) {
                                
                                // Normalize
                                if(defs.needsNormalization(feature.getFeatureDefinitionID())) {
                                        Utils.info("needsNormalization feature=" + feature);
                                        
                                        feature = this.normalize(feature);
                                }
                                
                                ExpandedFeature expFeature = new ExpandedFeature(feature);
                                
                                jw.setExpandedFeature(expFeature);
                                
                                // Execute Python script
                                jw.executeRule();
                        }
                }
                
                

                
                // Set SlideDefinition
                SlideDefinition slideDef = expFacetRuleDef.getSlideDefinition();
                if(slideDef != null) {
                        jw.setSlideDefinition(slideDef);
                        
                        // Set Slide
                        Utils.info("slideDef.getID()=" + slideDef.getID());
                        
                        Slide slide = ents.getSlide(expEntity.getID(), slideDef.getID());
                        
                        // Slide may not have been included infavor of a borrowed slide
                        if(slide == null) {
                                return null;
                        }
                        
                        ExpandedSlide expSlide = new ExpandedSlide(slide);
                        
                        jw.setExpandedSlide(expSlide);
                        
                        // Execute Python script
                        jw.executeRule();
                }
                
                //
                
                if(slideDef == null && featureDef == null){
                        if(expFacetRuleDef.getIRDataRuleDefinition().getScript() != null){
                                jw.executeRule();
                        }
                }
                
                
                List<Object> values = (List<Object>)jw.getValues();
                Utils.info("values=" + values);
                
                // Append to facet values
                long facetDefID = expFacetRuleDef.getFacetDefinitionID();
                Utils.info("facetDefID=" + facetDefID);
                
                List<String> facetValues = this.facetIDFacetValues.get(
                                new Long(facetDefID));
                
                if(facetValues == null) {
                        facetValues = new ArrayList<String>();
                        this.facetIDFacetValues.put(new Long(facetDefID), facetValues);
                }
                
                if(values != null) {
                        for(Object value : values) {
                                String strValue = value.toString();
                                
                                if(!facetValues.contains(strValue)) {
                                        facetValues.add(strValue);
                                }
                        }
                }

                // Parse returned Python list
                String facetXMLSnip = null;
                if(values != null) {
                        
                        // Get IR Data XML snippet for this entity and facet
                        facetXMLSnip = this.getFacetXMLSnippet(expFacetRuleDef, values, 2);
                }
                
                Utils.info("0 facetXMLSnip=" + facetXMLSnip);
                return facetXMLSnip;
        }
        
        /**
         * 
         * @param feature
         * @return Feature
         * @throws Exception 
         */
        private Feature normalize(Feature feature) throws Exception {
                ExpandedFeature expFeature = new ExpandedFeature(feature);

                BulletDefinition bulletDef = 
                        expFeature.getFeatureDefinition().getBulletDefinition();
                
                ExpandedBulletDefinition expBulletDef = new ExpandedBulletDefinition(
                                bulletDef);
                
                if(expBulletDef.isPrimitive()) {
                        PrimitiveNormalizationJythonWrapper jy = 
                                new PrimitiveNormalizationJythonWrapper();
                        
                        jy.setExpandedFeature(expFeature);
                        
                        jy.excuteRule();
                        
                        String newValue = jy.getNewValue();
                        long newUnitID = jy.getNewUnitID();
                        
                        List<Bullet> newBullets = new ArrayList<Bullet>();
                        Bullet newBullet = new Bullet(new PrimitiveDataObject(newValue));
                        newBullet.setUnitID(newUnitID);
                        
                        newBullets.add(newBullet);
                        
                        feature.setBullets(newBullets);
                }
                else {
                        Utils.severe("Normalization not defined for non-primitives");
                }
                
                return feature;
        }

        /**
         * 
         * @param expEntity
         * @param facetFeatureIDs
         * @param indent
         * @return
         */
        private String getPropertiesXMLSnippet(ExpandedEntity expEntity, 
                        List<Long> facetFeatureIDs, int indent) {
                
                List<String> xmlSnippet = new ArrayList<String>();
                
                // Collect all free-form content here
                List<String> ffc = new ArrayList<String>();
                
                // Features
                List<ExpandedSlide> expSlides = expEntity.getExpandedSlides();
                
                for(ExpandedSlide expSlide : expSlides) {
                        List<ExpandedFeature> expFeatures = expSlide.getExpandedFeatures();
                        
                        if(expSlide.getFreeformContent() != null) {
                                ffc.add(expSlide.getFreeformContent().getFreeformText());
                        }
                        
                        if(expFeatures == null) {
                                continue;
                        }
                        
                        for(ExpandedFeature expFeature : expFeatures) {
                                Long featureDefID = 
                                        new Long(expFeature.getFeatureDefinitionID());
                                
                                // FFC at feature level
                                if(expFeature.getFreeformContent() != null) {
                                        ffc.add(expFeature.getFreeformContent().getFreeformText());
                                }
                                
                                // Exclude those who are already covered as facets
                                if(facetFeatureIDs.contains(featureDefID)) {
                                        continue;
                                }
                
                                List<ExpandedBullet> expBullets = 
                                        expFeature.getExpandedBullets();
                                
                                if(expBullets == null) {
                                        continue;
                                }
                                
                                //<Property Label="">
                                xmlSnippet.add(this.xmlIndentation[indent] + 
                                                "<Property ID=\"" + expFeature.getFeatureDefinitionID() 
                                                + "\" Label=\"" + StringEscapeUtils.escapeXml(
                                                                expFeature.getFeatureDefinition().getLabel()) + 
                                                "\">");

                                //<Value></Value>
                                for(ExpandedBullet bullet : expBullets) {
                                        
                                        // FFC at bullet level
                                        if(bullet.getFreeformContent() != null) {
                                                ffc.add(bullet.getFreeformContent().getFreeformText());
                                        }
                                        
                                        xmlSnippet.add(this.xmlIndentation[indent + 1] + "<Value>" + 
                                                        StringEscapeUtils.escapeXml(bullet.getValue()) + 
                                                        "</Value>");
                                }
                                
                                //</Property>
                                xmlSnippet.add(this.xmlIndentation[indent] + "</Property>");
                        }
                }
                
                // FFC as Label="Free-form Content"
                if(!ffc.isEmpty()) {
                        xmlSnippet.add(this.xmlIndentation[indent] + 
                                        "<Property ID=\"000000\" Label=\"Free-form Content\">");
                        
                        for(String f : ffc) {
                                if(f != null) {
                                        f = StringEscapeUtils.escapeXml(f);
                                        
                                        xmlSnippet.add(this.xmlIndentation[indent + 1] + "<Value>" + 
                                                f + "</Value>");
                                }
                        }
                        
                        xmlSnippet.add(this.xmlIndentation[indent] + "</Property>");
                }
                
                // Children slides
                // TODO
                
                return StringUtils.join(xmlSnippet, "\n");
        }
        
        /**
         * 
         * @param expEntity
         * @param facetXMLSnippets
         * @param indent
         * @return
         */
        private String getEntityXMLSnippet(ExpandedEntity expEntity, 
                        String facetXMLSnippets, String propertiesXMLSnippets, int indent) {

                //<Entity ID="40001">
                List<String> xmlSnippet = new ArrayList<String>();
                
                String entityID = new Long(expEntity.getID()).toString();
                xmlSnippet.add(this.xmlIndentation[indent] + "<Entity ID=\""+ entityID + 
                                "\">");

                //<Category>Business Phones</Category>
                String parentCategory = expEntity.getCategory().getLabel();
                xmlSnippet.add(this.xmlIndentation[indent + 1] + "<Category>" + 
                                StringEscapeUtils.escapeXml(parentCategory) + "</Category>");

                /*
                //<Category>Business Phones</Category>
                String category = expEntity.getCategory().getLabel();
                xmlSnippet.add(this.xmlIndentation[indent + 1] + "<SubCategory>" + 
                                StringEscapeUtils.escapeXml(category) + "</SubCategory>");
                 */
                
                //<Title>Nokia E71</Title>
                String title = StringEscapeUtils.escapeXml(expEntity.getBrand()) + " " +
                StringEscapeUtils.escapeXml(expEntity.getModelName()) + 
                        ((expEntity.getModelNumber() != null) ? 
                                        (" " + 
                                                StringEscapeUtils.escapeXml(expEntity.getModelNumber()))
                                        : "");

                xmlSnippet.add(this.xmlIndentation[indent + 1] + 
                                "<Title>" + StringEscapeUtils.escapeXml(title) + "</Title>");
                
                xmlSnippet.add(facetXMLSnippets);

                xmlSnippet.add(propertiesXMLSnippets);
                
                //</Entity>
                xmlSnippet.add(this.xmlIndentation[indent] + "</Entity>");
                
                return StringUtils.join(xmlSnippet, "\n");
        }
        
        /**
         * 
         * @param expFacetDef
         * @param values
         * @param indent
         * @return String XML Snippet
         */
        private String getFacetXMLSnippet(
                        ExpandedFacetRuleDefinition expFacetRuleDef, 
                        List<Object> values, int indent) {
                
                // <Facet Label="Form Factor">
                List<String> xmlSnippet = new ArrayList<String>();
                String target = expFacetRuleDef.getFacetDefinition().getTarget();
                
                xmlSnippet.add(this.xmlIndentation[indent] + "<Facet ID=\"" + 
                                expFacetRuleDef.getFacetDefinitionID() + "\" Label=\"" + 
                                target + "\">");
                
                //<Value>Candybar</Value>
                for(Object value : values) {
                        xmlSnippet.add(this.xmlIndentation[indent + 1] + "<Value>" + 
                                        value.toString() + "</Value>");
                }
                
                //</Facet>
                xmlSnippet.add(this.xmlIndentation[indent] + "</Facet>");
                
                return StringUtils.join(xmlSnippet, "\n");
        }
}