FacetedBrowserApple.java :  » Content-Management-System » daisy » org » outerj » daisy » frontend » Java Open Source

Java Open Source » Content Management System » daisy 
daisy » org » outerj » daisy » frontend » FacetedBrowserApple.java
/*
 * Copyright 2004 Outerthought bvba and Schaubroeck nv
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.outerj.daisy.frontend;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.cocoon.components.flow.apples.AppleRequest;
import org.apache.cocoon.components.flow.apples.AppleResponse;
import org.apache.cocoon.components.flow.apples.StatelessAppleController;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.xml.SaxBuffer;
import org.apache.xmlbeans.XmlError;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlOptions;
import org.outerj.daisy.frontend.components.siteconf.SiteConf;
import org.outerj.daisy.frontend.util.AbstractDaisyApple;
import org.outerj.daisy.frontend.util.XmlObjectXMLizable;
import org.outerj.daisy.frontend.util.GenericPipeConfig;
import org.outerj.daisy.navigation.NavigationManager;
import org.outerj.daisy.navigation.NavigationParams;
import org.outerj.daisy.repository.Repository;
import org.outerj.daisy.repository.query.FacetConf;
import org.outerj.daisy.repository.query.QueryHelper;
import org.outerj.daisy.xmlutil.LocalSAXParserFactory;
import org.outerx.daisy.x10.FacetedQueryResultDocument;
import org.outerx.daisy.x10Facetednavdef.FacetedNavigationDefinitionDocument;
import org.outerx.daisy.x10Facetednavdef.FacetedNavigationDefinitionDocument.FacetedNavigationDefinition.Facets.Facet.Properties;
import org.outerx.daisy.x10Facetednavdef.FacetedNavigationDefinitionDocument.FacetedNavigationDefinition.Facets.Facet.Properties.Property;
import org.outerx.daisy.x10Facetednavdef.OptionsDocument.Options;

public class FacetedBrowserApple extends AbstractDaisyApple implements StatelessAppleController {

    protected void processRequest(AppleRequest appleRequest, AppleResponse appleResponse) throws Exception {
        Locale locale = frontEndContext.getLocale();
        SiteConf siteConf = frontEndContext.getSiteConf();
        Repository repository = frontEndContext.getRepository();
        String optionsId = request.getParameter("options");

        String definitionName = appleRequest.getSitemapParameter("definitionName");
        FacetedNavigationDefinition facetedNavDef = loadFacetDefinitions(definitionName, siteConf, optionsId, request);
        FacetDefinition[] facetDefs = facetedNavDef.getFacetDefinitions();        
        
        Filter[] filters = parseFilters(request, facetedNavDef);
        Condition[] conditions = filtersToConditions(filters, facetedNavDef);
        boolean limitToSiteCollection = RequestUtil.getBooleanParameter(request, "limitToSiteCollection", facetedNavDef.getLimitToSiteCollection());
        boolean limitToSiteVariant = RequestUtil.getBooleanParameter(request, "limitToSiteVariant", facetedNavDef.getLimitToSiteVariant());
        String orderBy = RequestUtil.getStringParameter(request, "orderBy", facetedNavDef.getDefaultOrder());
        String activeNavigationPath = request.getParameter("activeNavPath");

        StringBuilder query = new StringBuilder();
        query.append("select ");
        String[] auxiliaryFields = facetedNavDef.getAuxiliaryFields();
        for (int i = 0; i < auxiliaryFields.length; i++) {
            if (i > 0)
                query.append(", ");
            query.append(auxiliaryFields[i]);
        }
        for (int i = 0; i < facetDefs.length; i++) {
            if (i > 0 || auxiliaryFields.length > 0)
                query.append(", ");
            query.append(facetDefs[i].getExpression());
        }
        query.append(" where ");

        //The default conditions need to be at the start to allow FullText() in the defaultConditions
        if (facetedNavDef.getDefaultConditions() == null) {
            query.append("true");
        } else {
            query.append(facetedNavDef.getDefaultConditions());
        }
        if (conditions.length != 0) {
            query.append(" and (");
            for (int i = 0; i < conditions.length; i++) {
                if (i > 0)
                    query.append(") and (");
                query.append(conditions[i].toString());
            }
            query.append(")");
        }
        if (limitToSiteCollection) {
            String collectionName = repository.getCollectionManager().getCollection(siteConf.getCollectionId(), false).getName();
            query.append(" and InCollection(");
            query.append(QueryHelper.formatString(collectionName));
            query.append(")");
        }
        if (limitToSiteVariant) {
            String branchName = repository.getVariantManager().getBranch(siteConf.getBranchId(), false).getName();
            String languageName = repository.getVariantManager().getLanguage(siteConf.getLanguageId(), false).getName();
            query.append(" and branch = ");
            query.append(QueryHelper.formatString(branchName));
            query.append(" and language = ");
            query.append(QueryHelper.formatString(languageName));
        }

        query.append(" order by ").append(orderBy);
        
        FacetConf[] facetConfs = parseFacetConfs(request, facetedNavDef);
        
        Map<String,String> queryOptions = new HashMap<String,String>();
        queryOptions.put("chunk_offset", RequestUtil.getStringParameter(request, "chunkOffset", "1"));
        queryOptions.put("chunk_length", RequestUtil.getStringParameter(request, "chunkLength", "10"));

        FacetedQueryResultDocument result = repository.getQueryManager().performFacetedQuery(query.toString(), facetConfs , queryOptions, locale);
        
        SaxBuffer navigationBuffer = null;        
        if (activeNavigationPath != null) {
            navigationBuffer = new SaxBuffer();
            NavigationManager navigationManager = (NavigationManager)repository.getExtension("NavigationManager");
            NavigationParams navigationParams = new NavigationParams(siteConf.getNavigationDoc(),
                    frontEndContext.getVersionMode().getNavigationVersionMode(), activeNavigationPath,
                    siteConf.contextualizedTree(), siteConf.getNavigationDepth(), locale);  
            navigationManager.generateNavigationTree(navigationBuffer, navigationParams, null, true);
        }

        HashMap<String, Object> viewData = new HashMap<String,Object>();
        viewData.put("facetedQueryResult", new XmlObjectXMLizable(result));
        viewData.put("filters", filters);
        viewData.put("facetedNavDef", facetedNavDef);
        viewData.put("facetConfs", facetConfs);
        viewData.put("limitToSiteCollection", String.valueOf(limitToSiteCollection));
        viewData.put("limitToSiteVariant", String.valueOf(limitToSiteVariant));
        viewData.put("orderBy", orderBy);
        viewData.put("pageContext", frontEndContext.getPageContext());
        viewData.put("path", getMountPoint() + "/" + siteConf.getName() + "/facetedBrowser/" + definitionName);
        viewData.put("navigationTree", navigationBuffer);

        GenericPipeConfig pipeConf = new GenericPipeConfig();
        pipeConf.setTemplate("resources/xml/faceted_browser.xml");
        pipeConf.setStylesheet(facetedNavDef.getStylesheetSrc());
        viewData.put("pipeConf", pipeConf);

        appleResponse.sendPage("internal/genericPipe", viewData);
    }

    private FacetConf[] parseFacetConfs(Request request, FacetedNavigationDefinition facetedNavDef) throws Exception {
        FacetDefinition[] facetDefs = facetedNavDef.getFacetDefinitions();
        String[] auxiliaryFields = facetedNavDef.getAuxiliaryFields();
        FacetConf[] facetConfs = new FacetConf[auxiliaryFields.length + facetDefs.length];
        for (int i = 0; i < auxiliaryFields.length; i++) {
            facetConfs[i] = new FacetConf(false);            
        }

        for (int i = 0; i < facetDefs.length; i++) {
            String prefix = "facetConf." + (i + 1) + ".";
            int maxValues = RequestUtil.getIntParameter(request, prefix + "maxValues", 10);
            boolean sortOnValue = RequestUtil.getBooleanParameter(request, prefix + "sortOnValue", true);
            boolean sortAscending = RequestUtil.getBooleanParameter(request, prefix + "sortAscending", true);
            facetConfs[auxiliaryFields.length + i] = new FacetConf(true, maxValues, sortOnValue, sortAscending, facetDefs[i].getType());
            facetConfs[auxiliaryFields.length + i].setProperties(facetDefs[i].getFacetProperties());
        }

        return facetConfs;
    }

    private Filter[] parseFilters(Request request, FacetedNavigationDefinition facetedNavDef) throws Exception {
        List<Filter> filters = new ArrayList<Filter>();
        int i = 1;
        while (true) {
            String facetIndex = "filter." + i;
            String facetName = request.getParameter(facetIndex + ".facetName");
            if (facetName == null)
                break;
            facetedNavDef.getFacetDefinition(facetName);
            String queryValue = RequestUtil.getStringParameter(request, facetIndex + ".queryValue");
            String displayValue = RequestUtil.getStringParameter(request, facetIndex + ".displayValue");
            String operatorKey = RequestUtil.getStringParameter(request, facetIndex + ".operator");
            boolean isDiscrete = RequestUtil.getBooleanParameter(request, facetIndex + ".isDiscrete", false);
            Operator operator = operators.get(operatorKey);
            if (operator == null)
                throw new Exception("Non-existing operator: " + operatorKey);

            Filter filter = new Filter(facetName, operator, queryValue, displayValue, isDiscrete);
            filters.add(filter);
            i++;
        }
        return filters.toArray(new Filter[filters.size()]);
    }

    public static class Filter {
        private String facetName;
        private Operator operator;
        private String queryValue;
        private String displayValue;
        private boolean isDiscrete;

        public Filter(String facetName, Operator operator, String queryValue, String displayValue, boolean isDiscrete) {
            this.facetName = facetName;
            this.operator = operator;
            this.queryValue = queryValue;
            this.displayValue = displayValue;
            this.isDiscrete = isDiscrete;
        }

        public String getFacetName() {
            return facetName;
        }

        public Operator getOperator() {
            return operator;
        }

        public String getQueryValue() {
            return queryValue;
        }

        public String getDisplayValue() {
            return displayValue;
        }

        public boolean isDiscrete() {
            return isDiscrete;
        }
    }

    private Condition[] filtersToConditions(Filter[] filters, FacetedNavigationDefinition facetedNavDef) {
        Map<String, Condition> conditions = new HashMap<String, Condition>();

        for (Filter filter : filters) {
            Condition condition = conditions.get(filter.getFacetName());
            FacetDefinition facetDef = facetedNavDef.getFacetDefinition(filter.getFacetName());
            if (condition == null || facetDef.isDiscrete() || filter.getOperator().equals(Operator.HAS_PATH)) {
                condition = new Condition(facetDef.getExpression(), filter.getOperator(), filter.getQueryValue());
                conditions.put(filter.getFacetName(), condition);
            } else {
                condition.addValue(filter.getQueryValue());
            }
        }

        return conditions.values().toArray(new Condition[conditions.size()]);
    }

    public static class Condition {
        private String identifier;
        private String value;
        private List<String> values;
        private Operator operator;

        public Condition(String identifier, Operator operator, String value) {
            this.identifier = identifier;
            this.operator = operator;
            this.value = value;
        }

        public void addValue(String value) {
            if (values == null) {
                values = new ArrayList<String>();
                values.add(this.value);
                this.value = null;
            }
            values.add(value);
        }

        public String toString() {
            if (values != null) {
                StringBuilder result = new StringBuilder();
                result.append(identifier);
                result.append(" has all(");                
                for (int i = 0; i < values.size(); i++) {
                    if (i > 0)
                        result.append(", ");
                    result.append(values.get(i));
                }
                result.append(")");
                return result.toString();
            } else {
                return identifier + " " + operator.getSyntax() + " " + value;
            }
        }
    }

    private static Map<String, Operator> operators = new HashMap<String, Operator>();
    static {
        operators.put(Operator.EQUALS.getKey(), Operator.EQUALS);
        operators.put(Operator.LIKE.getKey(), Operator.LIKE);
        operators.put(Operator.BETWEEN.getKey(), Operator.BETWEEN);
        operators.put(Operator.HAS_PATH.getKey(), Operator.HAS_PATH);
    }

    public static class Operator {
        private final String key;
        private final String syntax;

        private Operator(String key, String syntax) {
            this.key = key;
            this.syntax = syntax;
        }

        public String getKey() {
            return key;
        }

        public String getSyntax() {
            return syntax;
        }

        public String toString() {
            return key;
        }

        public static final Operator EQUALS = new Operator("equals", "=");
        public static final Operator LIKE = new Operator("like", "like");
        public static final Operator BETWEEN = new Operator("between", "between");
        public static final Operator HAS_PATH = new Operator ("has path", "");
    }

    private FacetedNavigationDefinition loadFacetDefinitions(String name, SiteConf siteConf, String optionsId, Request request) throws Exception {
        // TODO NEED CACHING HERE so we don't need to load/parse the definitions on each request
        File file = new File(new File(siteConf.getDirectory(), "facetednavdefs"), name + ".xml");
        if (!file.exists())
            throw new Exception("Faceted navigation definition does not exist: \"" + name + "\"");

        List errors = new ArrayList();
        XmlOptions xmlOptions = new XmlOptions();
        xmlOptions.setLoadUseXMLReader(LocalSAXParserFactory.newXmlReader());
        xmlOptions.setErrorListener(errors);
        xmlOptions.setLoadLineNumbers();
        boolean valid;
        FacetedNavigationDefinitionDocument facetedNavDefDoc;
        try {
            facetedNavDefDoc = FacetedNavigationDefinitionDocument.Factory.parse(file, xmlOptions);

            xmlOptions = new XmlOptions();
            xmlOptions.setErrorListener(errors);
            valid = facetedNavDefDoc.validate(xmlOptions);
        } catch (XmlException e) {
            throw new Exception("Error loading faceted navigation definition: " + formatXmlErrors(errors), e);
        } catch (Exception e) {
            throw new Exception("Error loading faceted navigation definition.", e);
        }

        if (!valid)
            throw new Exception("Error validating faceted navigation definition: " + formatXmlErrors(errors));

        HashSet<String> names = new HashSet<String>();
        List<FacetedNavigationDefinitionDocument.FacetedNavigationDefinition.Facets.Facet> facetDefsXml = facetedNavDefDoc.getFacetedNavigationDefinition().getFacets().getFacetList();
        FacetDefinition[] facetDefs = new FacetDefinition[facetDefsXml.size()];
        for (int i = 0; i < facetDefsXml.size(); i++) {
            FacetedNavigationDefinitionDocument.FacetedNavigationDefinition.Facets.Facet facetDefXml = facetDefsXml.get(i);
            String expression = facetDefsXml.get(i).getExpression();
            String type = facetDefXml.getType();
            Map<String, String> facetProperties = new HashMap<String, String>();
            
            Properties propertiesXml = facetDefXml.getProperties();
            if (propertiesXml != null) {
                for (Property prop : propertiesXml.getPropertyList()) {
                    facetProperties.put(prop.getName(), prop.getValue());
                }
            }
            
            String facetName = makeFacetName(expression);
            if (names.contains(facetName)) {
                String baseFacetName = facetName;
                int s = 1;
                while (names.contains(facetName)) {
                    facetName = baseFacetName + "-" + s;
                    s++;
                }
            }
            names.add(facetName);
            if (type != null)
                facetDefs[i] = new FacetDefinition(facetName, facetDefXml.getExpression(), type);
            else
                facetDefs[i] = new FacetDefinition(facetName, facetDefXml.getExpression());

            facetDefs[i].setFacetProperties(facetProperties);
        }

        FacetedNavigationDefinitionDocument.FacetedNavigationDefinition.OptionsList optionsListXml = facetedNavDefDoc.getFacetedNavigationDefinition().getOptionsList();
        Options options = facetedNavDefDoc.getFacetedNavigationDefinition().getOptions();
        
        if (optionsListXml != null) {
            Map<String, Options> idOptionsMap = new HashMap<String, Options>();

            for (Options optionsList : optionsListXml.getOptionsList())
                idOptionsMap.put(optionsList.getId(), optionsList);
            
            if (!(optionsId != null && idOptionsMap.containsKey(optionsId)))
                optionsId = optionsListXml.getDefaultOptions();
                           
            options = idOptionsMap.get(optionsId);
        }
        
        String[] auxilarySelects = new String[2 + (options.isSetAdditionalSelects() ? options.getAdditionalSelects().sizeOfExpressionArray() : 0)];
        auxilarySelects[0] = "name";
        auxilarySelects[1] = "summary";        
        if (options.isSetAdditionalSelects()) {
            List<String> expressions = options.getAdditionalSelects().getExpressionList();
            for (int i = 0; i < expressions.size(); i++) {
                auxilarySelects[2+i] = expressions.get(i);
            }          
        } 
        
        FacetedNavigationDefinition navDef = new FacetedNavigationDefinition(facetDefs, auxilarySelects,
                options.getLimitToSiteCollection(), options.getLimitToSiteVariant(), options.getDefaultConditions(), options.getDefaultOrder(), optionsId, request);
        
        if (facetedNavDefDoc.getFacetedNavigationDefinition().getStylesheet() != null)
            navDef.setStylesheetSrc(facetedNavDefDoc.getFacetedNavigationDefinition().getStylesheet().getSrc());
        
        return navDef;
    }

    private String formatXmlErrors(Collection xmlErrors) {
        StringBuilder message = new StringBuilder();
        for (Object xmlError : xmlErrors) {
            XmlError error = (XmlError)xmlError;
            if (message.length() > 0)
                message.append("\n");
            message.append(error.getMessage());
        }
        return message.toString();
    }

    private String makeFacetName(String expression) {
        StringBuilder name = new StringBuilder(expression.length());
        for (int i = 0; i < expression.length(); i++) {
            char c = expression.charAt(i);
            if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'))
                name.append(c);
        }
        return name.toString();
    }

    public static class FacetedNavigationDefinition {
        private FacetDefinition[] facetDefinitions;
        private Map<String, FacetDefinition> facetsByName = new HashMap<String, FacetDefinition>();
        private Map<String, String> foundRequestParams = new HashMap<String, String>();
        private String[] auxiliaryFields;
        private boolean limitToSiteCollection;
        private boolean limitToSiteVariant;
        private String defaultConditions;
        private String defaultOrder = "name ASC";
        private String selectedOptions;
        private String stylesheetSrc = "daisyskin:xslt/faceted_browser.xsl";

        public FacetedNavigationDefinition(FacetDefinition[] facetDefinitions, String[] auxiliaryFields,
                boolean limitToSiteCollection, boolean limitToSiteVariant, String defaultConditions, String defaultOrder, String selectedOptions, Request request) throws Exception{
            this.facetDefinitions = facetDefinitions;
            for (FacetDefinition facetDefinition : facetDefinitions) {
                if (facetsByName.containsKey(facetDefinition.getName()))
                    throw new RuntimeException("Duplicate facet name: " + facetDefinition.getName());
                facetsByName.put(facetDefinition.getName(), facetDefinition);
            }
            
            RequestParamParser parser = new RequestParamParser(request);
            
            this.auxiliaryFields = auxiliaryFields;
            this.limitToSiteCollection = limitToSiteCollection;
            this.limitToSiteVariant = limitToSiteVariant;
            this.defaultConditions = parser.parse(defaultConditions, foundRequestParams);
            if (defaultOrder != null)
                this.defaultOrder = defaultOrder;
            this.selectedOptions = selectedOptions;
        }

        public FacetDefinition[] getFacetDefinitions() {
            return facetDefinitions;
        }

        public FacetDefinition getFacetDefinition(String name) {
            FacetDefinition facetDefinition = facetsByName.get(name);
            if (facetDefinition == null)
                throw new RuntimeException("Non-existing facet name: " + name);
            return facetDefinition;
        }

        public String[] getAuxiliaryFields() {
            return auxiliaryFields;
        }

        public boolean getLimitToSiteCollection() {
            return limitToSiteCollection;
        }

        public boolean getLimitToSiteVariant() {
            return limitToSiteVariant;
        }

        public String getDefaultConditions() {
            return defaultConditions;
        }

        public String getSelectedOptions() {
            return selectedOptions;
        }

        public void setSelectedOptions(String selectedOptions) {
            this.selectedOptions = selectedOptions;
        }

        public Map getFoundRequestParams() {
            return foundRequestParams;
        }

        public String getStylesheetSrc() {
            return stylesheetSrc;
        }

        public void setStylesheetSrc(String stylesheetSrc) {
            this.stylesheetSrc = stylesheetSrc;
        }

        public String getDefaultOrder() {
            return defaultOrder;
        }
    }

    public static class FacetDefinition {
        private String name;
        private String expression;
        private String type;
        private boolean isDiscrete;
        private Map<String, String> facetProperties = new HashMap<String, String>();


        public FacetDefinition(String name, String identifier) {
            this(name, identifier, "DEFAULT");
        }

        public FacetDefinition(String name, String identifier, String type) {
            this.name = name;
            this.expression = identifier;
            this.type = type;
            this.isDiscrete = !type.equals("DEFAULT");
        }

        public String getName() {
            return name;
        }

        public String getExpression() {
            return expression;
        }

        public String getType() {
            return type;
        }

        public boolean isDiscrete() {
            return isDiscrete;
        }

        public Map<String, String> getFacetProperties() {
            return facetProperties;
        }

        public void setFacetProperties(Map<String, String> facetProperties) {
            this.facetProperties = facetProperties;
        }
    }

    private static class RequestParamParser {
        private static String paramRegex = "\\{request-param:(.*?)\\|(.*?)\\}";
        
        private Request request;
        private Pattern pattern;
        
        public RequestParamParser(Request request) {
            this.request = request;
            pattern = Pattern.compile(paramRegex);
        }
        
        public String parse(String s, Map<String, String> foundRequestParams) throws Exception{            
            if (s != null) {
                StringBuffer out = new StringBuffer();
                Matcher m = pattern.matcher(s);            
                while (m.find()) {
                    String requestParam = m.group(1);
                    String defaultValue = m.group(2);
                    String paramValue = RequestUtil.getStringParameter(request, requestParam, defaultValue);
                    m.appendReplacement(out, paramValue);
                    foundRequestParams.put(requestParam, paramValue);
                }
                m.appendTail(out);
                return out.toString();
            } else {
                return null;
            }
        }    
    }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.