/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.solr.analytics.request;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import org.apache.solr.analytics.request.FieldFacetRequest.FacetSortDirection;
import org.apache.solr.analytics.request.FieldFacetRequest.FacetSortSpecification;
import org.apache.solr.common.params.FacetParams.FacetRangeInclude;
import org.apache.solr.common.params.FacetParams.FacetRangeOther;
import org.apache.solr.schema.IndexSchema;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
/**
* Handles the parsing of the AnalysisRequestEnvelope elements if passed in through XML.
*/
public class AnalyticsContentHandler implements ContentHandler {
// XML Element/Attribute Name Constants
public static final String ANALYTICS_REQUEST_ENVELOPE="analyticsRequestEnvelope";
public static final String ANALYTICS_REQUEST="analyticsRequest";
public static final String NAME="name";
public static final String STATISTIC="statistic";
public static final String EXPRESSION="expression";
public static final String FIELD_FACET="fieldFacet";
public static final String FIELD="field";
public static final String SHOW_MISSING="showMissing";
public static final String LIMIT="limit";
public static final String MIN_COUNT="minCount";
public static final String SORT_SPECIFICATION="sortSpecification";
public static final String STAT_NAME="statName";
public static final String DIRECTION="direction";
public static final String RANGE_FACET="rangeFacet";
public static final String START="start";
public static final String END="end";
public static final String GAP="gap";
public static final String INCLUDE_BOUNDARY="includeBoundary";
public static final String OTHER_RANGE="otherRange";
public static final String HARD_END="hardend";
public static final String QUERY_FACET="queryFacet";
public static final String QUERY="query";
// Default Values
public static final int DEFAULT_FACET_LIMIT = -1;
public static final boolean DEFAULT_FACET_HARDEND = false;
public static final int DEFAULT_FACET_MINCOUNT = 0;
public static final boolean DEFAULT_FACET_FIELD_SHOW_MISSING = false;
boolean inEnvelope = false;
boolean inRequest = false;
boolean inStatistic = false;
boolean inFieldFacet = false;
boolean inSortSpecification = false;
boolean inQueryFacet = false;
boolean inRangeFacet = false;
private final IndexSchema schema;
// Objects to use while building the Analytics Requests
String currentElementText;
List<AnalyticsRequest> requests;
AnalyticsRequest analyticsRequest;
List<ExpressionRequest> expressionList;
List<FieldFacetRequest> fieldFacetList;
List<RangeFacetRequest> rangeFacetList;
List<QueryFacetRequest> queryFacetList;
ExpressionRequest expression;
FieldFacetRequest fieldFacet;
int limit;
int minCount;
boolean showMissing;
FacetSortSpecification sortSpecification;
RangeFacetRequest rangeFacet;
boolean hardend;
List<String> gaps;
EnumSet<FacetRangeInclude> includeBoundaries;
EnumSet<FacetRangeOther> otherRanges;
String queryName;
List<String> queries;
public AnalyticsContentHandler(IndexSchema schema) {
this.schema = schema;
}
@Override
public void setDocumentLocator(Locator locator) { }
@Override
public void startDocument() throws SAXException { }
@Override
public void endDocument() throws SAXException { }
@Override
public void startPrefixMapping(String prefix, String uri) throws SAXException { }
@Override
public void endPrefixMapping(String prefix) throws SAXException { }
@Override
public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
currentElementText = "";
if (inEnvelope) {
if (inRequest) {
if (localName.equals(STATISTIC)) {
// Start a Statistic Request
inStatistic = true;
} else if (inFieldFacet) {
if (localName.equals(SORT_SPECIFICATION)) {
// Start a Sort Specification
inSortSpecification = true;
sortSpecification = new FacetSortSpecification();
}
} else if (localName.equals(FIELD_FACET)) {
// Start a Field Facet Request
// Get attributes (limit, minCount, showMissing)
String att = atts.getValue(uri,LIMIT);
if (att!=null) {
limit = Integer.parseInt(att);
} else {
limit = DEFAULT_FACET_LIMIT;
}
att = atts.getValue(uri,MIN_COUNT);
if (att!=null) {
minCount = Integer.parseInt(att);
} else {
minCount = DEFAULT_FACET_MINCOUNT;
}
att = atts.getValue(uri,SHOW_MISSING);
if (att!=null) {
showMissing = Boolean.parseBoolean(att);
} else {
showMissing = DEFAULT_FACET_FIELD_SHOW_MISSING;
}
inFieldFacet = true;
} else if (localName.equals(RANGE_FACET)) {
// Start a Range Facet Request
// Get attributes (hardEnd)
String att = atts.getValue(uri,HARD_END);
if (att!=null) {
hardend = Boolean.parseBoolean(att);
} else {
hardend = false;
}
// Initiate Range Facet classes
gaps = new ArrayList<>();
includeBoundaries = EnumSet.noneOf(FacetRangeInclude.class);
otherRanges = EnumSet.noneOf(FacetRangeOther.class);
inRangeFacet = true;
} else if (localName.equals(QUERY_FACET)) {
// Start a Query Facet Request
queries = new ArrayList<>();
inQueryFacet = true;
}
} else if (localName.equals(ANALYTICS_REQUEST)){
// Start an Analytics Request
// Renew each list.
fieldFacetList = new ArrayList<>();
rangeFacetList = new ArrayList<>();
queryFacetList = new ArrayList<>();
expressionList = new ArrayList<>();
inRequest = true;
}
} else if (localName.equals(ANALYTICS_REQUEST_ENVELOPE)){
//Begin the parsing of the Analytics Requests
requests = new ArrayList<>();
inEnvelope = true;
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (inEnvelope) {
if (inRequest) {
if (inStatistic) {
if (localName.equals(EXPRESSION)) {
expression = new ExpressionRequest(currentElementText,currentElementText);
} else if (localName.equals(NAME)) {
expression.setName(currentElementText);
} else if (localName.equals(STATISTIC)) {
// Finished Parsing the Statistic Request
expressionList.add(expression);
inStatistic = false;
}
} else if (inFieldFacet) {
if (inSortSpecification) {
if (localName.equals(STAT_NAME)) {
sortSpecification.setStatistic(currentElementText);
} else if (localName.equals(DIRECTION)) {
sortSpecification.setDirection(FacetSortDirection.fromExternal(currentElementText));
} else if (localName.equals(SORT_SPECIFICATION)) {
// Finished Parsing the Sort Specification
fieldFacet.setSort(sortSpecification);
inSortSpecification = false;
}
} else if (localName.equals(FIELD)) {
fieldFacet = new FieldFacetRequest(schema.getField(currentElementText));
} else if (localName.equals(FIELD_FACET)) {
// Finished Parsing the Field Facet Request
fieldFacet.setLimit(limit);
fieldFacet.showMissing(showMissing);
fieldFacetList.add(fieldFacet);
inFieldFacet = false;
}
} else if (inRangeFacet) {
if (localName.equals(FIELD)) {
rangeFacet = new RangeFacetRequest(schema.getField(currentElementText), "", "", new String[1]);
} else if (localName.equals(START)) {
rangeFacet.setStart(currentElementText);
} else if (localName.equals(END)) {
rangeFacet.setEnd(currentElementText);
} else if (localName.equals(GAP)) {
gaps.add(currentElementText);
} else if (localName.equals(INCLUDE_BOUNDARY)) {
includeBoundaries.add(FacetRangeInclude.get(currentElementText));
} else if (localName.equals(OTHER_RANGE)) {
otherRanges.add(FacetRangeOther.get(currentElementText));
} else if (localName.equals(RANGE_FACET)) {
// Finished Parsing the Range Facet Request
rangeFacet.setHardEnd(hardend);
rangeFacet.setGaps(gaps.toArray(new String[1]));
rangeFacet.setInclude(includeBoundaries);
rangeFacet.setOthers(otherRanges);
inRangeFacet = false;
rangeFacetList.add(rangeFacet);
}
} else if (inQueryFacet) {
if (localName.equals(NAME)) {
queryName = currentElementText;
} else if (localName.equals(QUERY)) {
queries.add(currentElementText);
} else if (localName.equals(QUERY_FACET)) {
// Finished Parsing the Query Facet Request
QueryFacetRequest temp = new QueryFacetRequest(queryName);
temp.setQueries(queries);
queryFacetList.add(temp);
inQueryFacet = false;
}
} else if (localName.equals(NAME)) {
analyticsRequest = new AnalyticsRequest(currentElementText);
} else if (localName.equals(ANALYTICS_REQUEST)){
// Finished Parsing the Analytics Request
analyticsRequest.setExpressions(expressionList);
analyticsRequest.setFieldFacets(fieldFacetList);
analyticsRequest.setRangeFacets(rangeFacetList);
analyticsRequest.setQueryFacets(queryFacetList);
requests.add(analyticsRequest);
inRequest = false;
}
} else if (localName.equals(ANALYTICS_REQUEST_ENVELOPE)){
// Finished Parsing
inEnvelope = false;
}
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
currentElementText += new String(ch,start,length);
}
@Override
public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { }
@Override
public void processingInstruction(String target, String data) throws SAXException { }
@Override
public void skippedEntity(String name) throws SAXException { }
/**
* Returns the list of Analytics Requests built during parsing.
*
* @return List of {@link AnalyticsRequest} objects specified by the given XML file
*/
public List<AnalyticsRequest> getAnalyticsRequests() {
return requests;
}
}