Java tutorial
/* * Copyright 2015 the original author or authors. * * 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 com.heliosapm.aa4h.parser; import java.io.IOException; import java.io.StringReader; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Stack; import java.util.concurrent.TimeoutException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.dom4j.QName; import org.dom4j.tree.DefaultElement; import org.hibernate.CacheMode; import org.hibernate.Criteria; import org.hibernate.FetchMode; import org.hibernate.FlushMode; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.criterion.CriteriaSpecification; import org.hibernate.criterion.Criterion; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Order; import org.hibernate.criterion.Projection; import org.hibernate.criterion.ProjectionList; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Restrictions; import org.hibernate.internal.CriteriaImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; // import org.hibernate.criterion.UnionExpression; // what happened to this guy import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.ParserAdapter; import com.heliosapm.aa4h.criterion.LiteralInExpression; /** * <p>Title: XMLQueryParser</p> * <p>Description: Parses and executes an XML Query. The class is basically a pseudo-stateful SAX parser.</p> * <p>Company: Helios Development Group LLC</p> * @author Whitehead (nwhitehead AT heliosdev DOT org) * <p><code>com.heliosapm.aa4h.parser.XMLQueryParser</code></p> */ public class XMLQueryParser extends DefaultHandler { @SuppressWarnings("unchecked") protected static Class[] ADD_ARGS_TYPE = new Class[] { Criterion.class }; protected Session session = null; protected long startTime = 0L; protected long endTime = 0L; protected String queryName = null; protected Stack<CriteriaSpecification> criteriaStack = new Stack<CriteriaSpecification>(); protected Stack<ProjectionList> projectionStack = new Stack<ProjectionList>(); @SuppressWarnings("unchecked") protected Stack opStack = new Stack(); @SuppressWarnings("unchecked") protected Stack notStack = new Stack(); protected List<DefaultElement> result = null; protected String rootElementName = null; protected Logger log = LoggerFactory.getLogger(getClass()); protected boolean inJunction = false; protected TimeoutException toe = null; protected String sql = ""; protected boolean inDetached = false; protected String className = null; protected String[] columnNames = null; protected boolean isNamedQuery = false; protected String namedQuery = ""; protected Query query = null; protected Boolean isNamedParams = null; protected long parseTime = 0L; protected String[] aliases = new String[] {}; protected String projectionsName = null; /** This query parser's sax parser */ protected final SAXParser saxParser; /** The parser's InputSource to read the query to parse */ protected final InputSource inputSource = new InputSource(); /** Provides the SAX parser for query executions */ protected static final SAXParserFactory spf = SAXParserFactory.newInstance(); /** Static Thread Local to contain the calling thread's parser */ protected static ThreadLocal<WeakReference<XMLQueryParser>> parser = new ThreadLocal<WeakReference<XMLQueryParser>>() { @Override protected WeakReference<XMLQueryParser> initialValue() { return new WeakReference<XMLQueryParser>(new XMLQueryParser()); } }; public static List<?> execute(final String xmlQuery, final Session session) throws IOException, SAXException, HibernateException { if (xmlQuery == null || xmlQuery.trim().isEmpty()) throw new IllegalArgumentException("The passed XmlQuery was null or empty"); if (session == null) throw new IllegalArgumentException("The passed Session was null"); WeakReference<XMLQueryParser> weakRef = parser.get(); XMLQueryParser xmlQueryParser = weakRef.get(); if (xmlQueryParser == null) { parser.remove(); xmlQueryParser = parser.get().get(); } xmlQueryParser.setSession(session); StringReader sr = new StringReader(xmlQuery); xmlQueryParser.inputSource.setCharacterStream(sr); xmlQueryParser.saxParser.parse(new InputSource(new StringReader(xmlQuery)), xmlQueryParser); sr.close(); return xmlQueryParser.getResult(); } /** Thread Local to contain object/thread exclusive rowCountOnly flag */ protected ThreadLocal<Boolean> rowCountOnlyQuery = new ThreadLocal<Boolean>(); /** * Parameterless XMLQueryParser constructor. */ private XMLQueryParser() { try { saxParser = spf.newSAXParser(); } catch (Exception ex) { throw new RuntimeException("Failed to create a SAX parser", ex); } } /** * Sets the XMLQueryParser's Hibernate Session. * @param session A Hibernate Session used to query the datasource. */ public void setSession(Session session) { this.session = session; } @Override public void error(SAXParseException e) throws SAXException { e.printStackTrace(System.err); super.error(e); } @Override public void fatalError(SAXParseException e) throws SAXException { e.printStackTrace(System.err); super.fatalError(e); } @Override public void warning(SAXParseException e) throws SAXException { e.printStackTrace(System.err); super.warning(e); } /** * The parsed out name of the query processed. * Returns null before a query has been parsed. * @return The assigned name of the query that was just parsed. */ public String getQueryName() { return queryName; } /** * Sets the value of the rowCountOnlyQuery ThreadLocal * @param rc true if query is rowCount only. */ public void setRowCountOnly(boolean rc) { rowCountOnlyQuery.set(Boolean.valueOf(rc)); } /** * Returns the rowCountOnly ThreadLocal value. * @return true if query is rowCount only. */ public boolean isRowCountOnly() { return rowCountOnlyQuery.get().booleanValue(); } /** * Unimplemented SAX ContentHandler Method. * @param arg0 * @see org.xml.sax.ContentHandler#setDocumentLocator(org.xml.sax.Locator) */ public void setDocumentLocator(Locator arg0) { } /** * Start Document SAX ContentHandler Method. * Initializes all stateful fields to prepare for a query parse. * @see org.xml.sax.ContentHandler#startDocument() * @throws SAXException */ public void startDocument() throws SAXException { startTime = System.currentTimeMillis(); opStack.clear(); notStack.clear(); criteriaStack.clear(); projectionStack.clear(); toe = null; queryName = ""; setRowCountOnly(false); namedQuery = ""; isNamedQuery = false; query = null; isNamedParams = null; aliases = new String[] {}; projectionsName = null; parseTime = System.currentTimeMillis(); } /** * End Document SAX ContentHandler Method. * Fired at the end of the document parsing event. * At this point the query has been fully parsed and the named or criteria query is complete. * This method executes the query and sets the result field, handling any errors. * @see org.xml.sax.ContentHandler#endDocument() * @throws SAXException * TODO Clean up the timeout handling. The current one is Oracle specific. We need the timeout handling to differentiate between standard DB Errors and timeouts as a result of a user requested timeout. */ @SuppressWarnings("unchecked") public void endDocument() throws SAXException { parseTime = System.currentTimeMillis() - parseTime; try { if (isNamedQuery) { result = query.list(); } else { Criteria finalCriteria = (Criteria) criteriaStack.pop(); Projection p = ((CriteriaImpl) finalCriteria).getProjection(); if (p != null) { aliases = p.getAliases(); } result = finalCriteria.list(); } } catch (Exception ex) { try { if (ex.getCause().getMessage().startsWith("ORA-01013")) { toe = new TimeoutException("Query " + queryName + " Timed Out"); QName q = new QName("Error"); DefaultElement de = new DefaultElement(q); de.addAttribute("reason", "Query Time Out"); result = new ArrayList<DefaultElement>(); result.add(de); endTime = System.currentTimeMillis(); } else { throw new SAXException("Unknown Exception At List Time for Query:" + queryName, ex); } } catch (Exception e) { log.error("Unknown Exception At List Time for Query:" + queryName, ex); throw new SAXException("Unknown Exception At List Time for Query:" + queryName, ex); } } endTime = System.currentTimeMillis(); if (log.isDebugEnabled()) { log.info(new StringBuffer("CATSQuery[").append(queryName).append("] Returned ").append(result.size()) .append(" elements in ").append((endTime - startTime)).append(" ms.").toString()); log.info("Elapsed Time For [" + queryName + "]:" + (endTime - startTime) + " ms."); } } /** * Unimplemented SAX ContentHandler Method. * @param arg0 * @param arg1 * @throws SAXException * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String) */ public void startPrefixMapping(String arg0, String arg1) throws SAXException { } /** * Unimplemented SAX ContentHandler Method. * @param arg0 * @throws SAXException * @see org.xml.sax.ContentHandler#endPrefixMapping(java.lang.String) */ public void endPrefixMapping(String arg0) throws SAXException { } /** * Adds a new projection list * @param attrs The attributes of the processed node. */ protected void startProjection(Attributes attrs) { projectionsName = attrs.getValue("name"); projectionStack.push(Projections.projectionList()); } /** * Initializes a Criteria Query. * Mandatory Attributes:<ul> * <li><b>name</b>: The unqualified class name driving the criteria query.</li> * </ul> * Optional Attributes:<ul> * <li><b>prefix</b>: The package name of the class driving the criteria query. If null, no package is assumed.</li> * <li><b>maxSize</b>: The maximum number of rows to return from the database.</li> * <li><b>fetchSize</b>: The number of rows to fetch when rows are requested. Usually not useful for AA4H.</li> * <li><b>cacheEnabled</b>: Enables or disables caching for the queried objects.</li> * <li><b>cacheMode</b>: The cache options for the queried objects.</li> * <li><b>flushMode</b>: The session flush options.</li> * <li><b>fetchMode</b>: The collection fetch options for the query.</li> * <li><b>lockMode</b>: The row lock options for the queried rows.</li> * <li><b>timeOut</b>: The query timeout option.</li> * <li><b>rowCountOnly</b>: Returns a count of the query rows only.</li> * </ul> * @param attrs The attributes of the processed node. * @return An appended or new CriteriaSpecification * @throws SAXException */ protected CriteriaSpecification processCriteria(Attributes attrs) throws SAXException { if (inDetached) { return criteriaStack.peek(); } String name = attrs.getValue("name"); String prefix = attrs.getValue("prefix"); if (prefix != null) { className = prefix + "." + name; } else { className = name; } String maxSize = attrs.getValue("maxSize"); String fetchSize = attrs.getValue("fetchSize"); String firstResult = attrs.getValue("firstResult"); String cacheEnabled = attrs.getValue("cacheEnabled"); String cacheMode = attrs.getValue("cacheMode"); String flushMode = attrs.getValue("flushMode"); String fetchMode = attrs.getValue("fetchMode"); String lockMode = attrs.getValue("lockMode"); String timeOut = attrs.getValue("timeOut"); String rowCountOnly = attrs.getValue("rowCountOnly"); Criteria newCriteria = null; try { if (criteriaStack.size() == 0) { newCriteria = session.createCriteria(className); } else { newCriteria = ((Criteria) criteriaStack.peek()).createCriteria(className); } criteriaStack.push(newCriteria); if ("true".equalsIgnoreCase(rowCountOnly)) { newCriteria.setProjection(Projections.projectionList().add(Projections.rowCount()) ); setRowCountOnly(true); } if (maxSize != null && isRowCountOnly() == false) { newCriteria.setMaxResults(Integer.parseInt(maxSize)); } if (fetchSize != null && isRowCountOnly() == false) { newCriteria.setFetchSize(Integer.parseInt(fetchSize)); } if (firstResult != null && isRowCountOnly() == false) { newCriteria.setFirstResult(Integer.parseInt(firstResult)); } if (timeOut != null) { newCriteria.setTimeout(Integer.parseInt(timeOut)); } if ("true".equalsIgnoreCase(cacheEnabled)) { newCriteria.setCacheable(true); } else if ("false".equalsIgnoreCase(cacheEnabled)) { newCriteria.setCacheable(false); } if (fetchMode != null && fetchMode.length() > 0) { if ("JOIN".equalsIgnoreCase(fetchMode)) { newCriteria.setFetchMode(name, FetchMode.JOIN); } else if ("SELECT".equalsIgnoreCase(fetchMode)) { newCriteria.setFetchMode(name, FetchMode.SELECT); } else { newCriteria.setFetchMode(name, FetchMode.DEFAULT); } } else { newCriteria.setFetchMode(name, FetchMode.DEFAULT); } if (cacheMode != null && cacheMode.length() > 0) { if ("GET".equalsIgnoreCase(cacheMode)) { newCriteria.setCacheMode(CacheMode.GET); } else if ("IGNORE".equalsIgnoreCase(cacheMode)) { newCriteria.setCacheMode(CacheMode.IGNORE); } else if ("NORMAL".equalsIgnoreCase(cacheMode)) { newCriteria.setCacheMode(CacheMode.NORMAL); } else if ("PUT".equalsIgnoreCase(cacheMode)) { newCriteria.setCacheMode(CacheMode.PUT); } else if ("REFRESH".equalsIgnoreCase(cacheMode)) { newCriteria.setCacheMode(CacheMode.REFRESH); } else { newCriteria.setCacheMode(CacheMode.NORMAL); } } if (lockMode != null && lockMode.length() > 0) { if ("NONE".equalsIgnoreCase(lockMode)) { newCriteria.setLockMode(LockMode.NONE); } else if ("READ".equalsIgnoreCase(lockMode)) { newCriteria.setLockMode(LockMode.READ); } else if ("UPGRADE".equalsIgnoreCase(lockMode)) { newCriteria.setLockMode(LockMode.UPGRADE); } else if ("UPGRADE_NOWAIT".equalsIgnoreCase(lockMode)) { newCriteria.setLockMode(LockMode.UPGRADE_NOWAIT); } else if ("WRITE".equalsIgnoreCase(lockMode)) { newCriteria.setLockMode(LockMode.WRITE); } else { throw new SAXException("lockMode[" + lockMode + "] Not Recognized"); } } if (flushMode != null && flushMode.length() > 0) { if ("ALWAYS".equalsIgnoreCase(flushMode)) { newCriteria.setFlushMode(FlushMode.ALWAYS); } else if ("AUTO".equalsIgnoreCase(flushMode)) { newCriteria.setFlushMode(FlushMode.AUTO); } else if ("COMMIT".equalsIgnoreCase(flushMode)) { newCriteria.setFlushMode(FlushMode.COMMIT); } else if ("NEVER".equalsIgnoreCase(flushMode)) { // NEVER is deprecated, so we won't throw an exception but we'll ignore it. } else { throw new SAXException("flushMode[" + flushMode + "] Not Recognized"); } } return newCriteria; } catch (Exception e) { throw new SAXException("Unable to configure class " + className, e); } } /** * Adds a new projection to the current projectionList stack. * Mandatory Attributes:<ul> * <li><b>name</b>: The field to create a projection on.</li> * <li><b>type</b>: The projection type.</li> * <li><b>alias</b>: The name applied to the projection returned field.</li> * </ul> * @param attrs The attributes of the processed node. * @throws SAXException * TODO: Implement checks for mandatory attributes. */ public void addProjection(Attributes attrs) throws SAXException { ProjectionList projectionList = projectionStack.peek(); if (projectionList == null) { throw new SAXException( "Attempted to add Projection and no ProjectionList Exists On The Stack. (Projection must be embedded inside a Projections)"); } String type = attrs.getValue("type"); String name = attrs.getValue("name"); String alias = attrs.getValue("alias"); if ("avg".equalsIgnoreCase(type)) { projectionList.add(Projections.avg(name), alias); } else if ("count".equalsIgnoreCase(type)) { projectionList.add(Projections.count(name), alias); } else if ("countDistinct".equalsIgnoreCase(type)) { projectionList.add(Projections.countDistinct(name), alias); } else if ("groupProperty".equalsIgnoreCase(type)) { projectionList.add(Projections.groupProperty(name), alias); } else if ("max".equalsIgnoreCase(type)) { projectionList.add(Projections.max(name), alias); } else if ("min".equalsIgnoreCase(type)) { projectionList.add(Projections.min(name), alias); } else if ("sum".equalsIgnoreCase(type)) { projectionList.add(Projections.sum(name), alias); } else if ("rowCount".equalsIgnoreCase(type)) { projectionList.add(Projections.rowCount(), alias); } } /** * Processes the bind of an argument against a named query. * Mandatory Attributes:<ul> * <li><b>name</b> or <b>id</b>: The name or bind sequence id of the parameter.</li> * <li><b>type</b>: The data type of the parameter.</li> * <li><b>value</b>: The the value of the parameter to be bound.</li> * </ul> * Optional Attributes:<ul> * <li><b>format</b>: The format of the value being passed. Required if type is a Date/Time or array.</li> * </ul> * @param attrs The attributes of the processed node. * @throws SAXException */ protected void processQueryBind(Attributes attrs) throws SAXException { if (query == null) { throw new SAXException("Encountered Query Param Bind With No Initialized Query"); } boolean isNamed = false; String id = attrs.getValue("id"); int pid = -1; String name = attrs.getValue("name"); String value = attrs.getValue("value"); String type = attrs.getValue("type"); String format = attrs.getValue("format"); if (type == null) throw new SAXException("Query Param Bind Has No Type"); if (id == null && name == null) throw new SAXException("Query Param Bind Has No Name or Id"); if (id == null) { isNamed = true; } else { isNamed = false; try { pid = Integer.parseInt(id); } catch (Exception e) { throw new SAXException("Query Param Bind Has Invalid Id:" + id); } } if (isNamedParams == null) { isNamedParams = new Boolean(isNamed); } else { if (isNamedParams.booleanValue() && (!isNamed)) { throw new SAXException("Query Param Binds Combined Names and Ids"); } } Operator op = new Operator(isNamed ? name : id, type, value, format); try { if (isNamed) { query.setParameter(name, op.getNativeValue()); } else { query.setParameter(pid, op.getNativeValue()); } } catch (Exception e) { throw new SAXException("Failed to Process Query Param Bind[" + (isNamed ? name : id) + "]", e); } } /** * Element Start SAX ContentHandler Method. * @param uri * @param localName * @param qName * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) * @throws SAXException * TODO: Document supported tags in javadoc. */ @SuppressWarnings({ "unchecked" }) public void startElement(String uri, String localNameX, String qName, Attributes attrs) throws SAXException { if ("Query".equalsIgnoreCase(qName)) { queryName = attrs.getValue("name"); rootElementName = queryName; } else if ("Class".equalsIgnoreCase(qName)) { processCriteria(attrs); } else if ("Union".equalsIgnoreCase(qName)) { inDetached = true; DetachedCriteria dc = DetachedCriteria.forEntityName(className); criteriaStack.push(dc); } else if ("NamedQuery".equalsIgnoreCase(qName)) { isNamedQuery = true; namedQuery = attrs.getValue("name"); rootElementName = namedQuery; try { query = session.getNamedQuery(namedQuery); } catch (HibernateException he) { throw new SAXException("Failed to retrieve named query[" + namedQuery + "]", he); } } else if ("qparam".equalsIgnoreCase(qName)) { processQueryBind(attrs); } else if ("Join".equalsIgnoreCase(qName)) { processCriteria(attrs); } else if ("Projections".equalsIgnoreCase(qName)) { startProjection(attrs); } else if ("Projection".equalsIgnoreCase(qName)) { addProjection(attrs); } else if ("Order".equalsIgnoreCase(qName)) { if (isRowCountOnly() == false) { try { String name = attrs.getValue("name"); String type = attrs.getValue("type"); ((Criteria) criteriaStack.peek()) .addOrder(type.equalsIgnoreCase("asc") ? Order.asc(name) : Order.desc(name)); } catch (Exception e) { throw new SAXException("Unable To Parse GreaterThan:" + attrs.getValue("name"), e); } } } else if ("GreaterThan".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); addCriterion(Restrictions.gt(operator.getName(), operator.getNativeValue())); } catch (Exception e) { throw new SAXException("Unable To Parse GreaterThan:" + attrs.getValue("name"), e); } } else if ("GreaterThanOrEqual".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); addCriterion(Restrictions.ge(operator.getName(), operator.getNativeValue())); } catch (Exception e) { throw new SAXException("Unable To Parse GreaterThanOrEqual:" + attrs.getValue("name"), e); } } else if ("LessThan".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); addCriterion(Restrictions.lt(operator.getName(), operator.getNativeValue())); } catch (Exception e) { throw new SAXException("Unable To Parse LessThan:" + attrs.getValue("name"), e); } } else if ("LessThanOrEqual".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); addCriterion(Restrictions.le(operator.getName(), operator.getNativeValue())); } catch (Exception e) { throw new SAXException("Unable To Parse LessThanOrEqual:" + attrs.getValue("name"), e); } } else if ("Equals".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); if ("true".equalsIgnoreCase(attrs.getValue("not"))) { addCriterion(Restrictions.not(Restrictions.eq(operator.getName(), operator.getNativeValue()))); } else { addCriterion(Restrictions.eq(operator.getName(), operator.getNativeValue())); } } catch (Exception e) { throw new SAXException("Unable To Parse Equals:" + attrs.getValue("name"), e); } } else if ("Alias".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); ((Criteria) criteriaStack.peek()).createAlias(operator.getName(), operator.getValue()); } catch (Exception e) { throw new SAXException("Unable To Create Alias:" + attrs.getValue("name"), e); } } else if ("GreaterThanProperty".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); addCriterion(Restrictions.gtProperty(operator.getName(), operator.getName2())); } catch (Exception e) { throw new SAXException("Unable To Parse GreaterThanProperty:" + attrs.getValue("name"), e); } } else if ("GreaterThanOrEqualProperty".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); addCriterion(Restrictions.geProperty(operator.getName(), operator.getName2())); } catch (Exception e) { throw new SAXException("Unable To Parse GreaterThanOrEqualProperty:" + attrs.getValue("name"), e); } } else if ("LessThanProperty".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); addCriterion(Restrictions.ltProperty(operator.getName(), operator.getName2())); } catch (Exception e) { throw new SAXException("Unable To Parse LessThanProperty:" + attrs.getValue("name"), e); } } else if ("LessThanOrEqualProperty".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); addCriterion(Restrictions.leProperty(operator.getName(), operator.getName2())); } catch (Exception e) { throw new SAXException("Unable To Parse LessThanOrEqualProperty:" + attrs.getValue("name"), e); } } else if ("EqualsProperty".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); if ("true".equalsIgnoreCase(attrs.getValue("not"))) { addCriterion( Restrictions.not(Restrictions.eqProperty(operator.getName(), operator.getName2()))); } else { addCriterion(Restrictions.eqProperty(operator.getName(), operator.getName2())); } } catch (Exception e) { throw new SAXException("Unable To Parse EqualsProperty:" + attrs.getValue("name"), e); } } else if ("Like".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); addCriterion(Restrictions.like(operator.getName(), operator.getNativeValue())); } catch (Exception e) { throw new SAXException("Unable To Parse Like:" + attrs.getValue("name"), e); } } else if ("Between".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); addCriterion(Restrictions.between(operator.getName(), operator.getNativeValue(), operator.getNativeValue2())); } catch (Exception e) { throw new SAXException("Unable To Parse Between:" + attrs.getValue("name"), e); } } else if ("IsEmpty".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); addCriterion(Restrictions.isEmpty(operator.getName())); } catch (Exception e) { throw new SAXException("Unable To Parse IsEmpty:" + attrs.getValue("name"), e); } } else if ("IsNotEmpty".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); addCriterion(Restrictions.isNotEmpty(operator.getName())); } catch (Exception e) { throw new SAXException("Unable To Parse IsNotEmpty:" + attrs.getValue("name"), e); } } else if ("IsNull".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); addCriterion(Restrictions.isNull(operator.getName())); } catch (Exception e) { throw new SAXException("Unable To Parse IsNull:" + attrs.getValue("name"), e); } } else if ("IsNotNull".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); addCriterion(Restrictions.isNotNull(operator.getName())); } catch (Exception e) { throw new SAXException("Unable To Parse IsNotNull:" + attrs.getValue("name"), e); } } else if ("In".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); if (operator.isLiteral()) { addCriterion(new LiteralInExpression(operator.getName(), (String) operator.getNativeValue())); } else { addCriterion(Restrictions.in(operator.getName(), (Collection) operator.getNativeValue())); } } catch (Exception e) { throw new SAXException("Unable To Parse In:" + attrs.getValue("name"), e); } } else if ("SizeEquals".equalsIgnoreCase(qName)) { try { Operator operator = new Operator(attrs); int i = ((Integer) operator.getNativeValue()).intValue(); addCriterion(Restrictions.sizeEq(operator.getName(), i)); } catch (Exception e) { throw new SAXException("Unable To Parse SizeEquals:" + attrs.getValue("name"), e); } } else if ("Not".equalsIgnoreCase(qName)) { notStack.push(new Object()); } else if ("Or".equalsIgnoreCase(qName)) { opStack.push(Restrictions.disjunction()); } else if ("And".equalsIgnoreCase(qName)) { opStack.push(Restrictions.conjunction()); } else { throw new SAXException("Element Name[" + qName + "] Not Recognized."); } } /** * Adds a criterion to the current item on the opStack. * If there are no items on the opStack, the criterion is added to the master criteria. * If the master criteria is added to, the notStack is checked to see if the added criterion should be negated. * @param criterion */ protected void addCriterion(Criterion criterion) { if (opStack.size() > 0) { try { Object peekedObject = opStack.peek(); peekedObject.getClass().getMethod("add", ADD_ARGS_TYPE).invoke(peekedObject, new Object[] { criterion }); } catch (Exception e) { log.error("XMLQueryBuilder addCriterion Method Failed", e); throw new RuntimeException("XMLQueryBuilder addCriterion Method Failed", e); } } else { if (notStack.size() > 0) { if (inDetached) { ((DetachedCriteria) criteriaStack.peek()).add(Restrictions.not(criterion)); } else { ((Criteria) criteriaStack.peek()).add(Restrictions.not(criterion)); } } else { if (inDetached) { ((DetachedCriteria) criteriaStack.peek()).add(criterion); } else { ((Criteria) criteriaStack.peek()).add(criterion); } } } } /** * End element SAX ContentHandler Method. * @param uri * @param localName * @param qName * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String) */ public void endElement(String uri, String localName, String qName) throws SAXException { if ("Or".equalsIgnoreCase(localName) || "And".equalsIgnoreCase(localName)) { Criterion criterion = (Criterion) opStack.pop(); addCriterion(criterion); } else if ("Join".equalsIgnoreCase(localName)) { criteriaStack.pop(); // FIXME: Unions no longer supported ? // } else if("Union".equalsIgnoreCase(localName)) { // DetachedCriteria dc = (DetachedCriteria)criteriaStack.pop(); // inDetached = false; // ((Criteria)criteriaStack.peek()).add(UnionExpression.getInstance(className, session, dc)); } else if ("Projections".equalsIgnoreCase(localName)) { if (inDetached) { ((DetachedCriteria) criteriaStack.peek()).setProjection(projectionStack.pop()); } else { ((Criteria) criteriaStack.peek()).setProjection(projectionStack.pop()); } } else if ("Projection".equalsIgnoreCase(localName)) { } else if ("Not".equalsIgnoreCase(localName)) { notStack.pop(); } } /** * Returns the result of the query. * Value is null before SAX processing is complete. * @return The result of the query. */ @SuppressWarnings("unchecked") public List getResult() { return result; } /** * Unimplemented SAX ContentHandler Method. * @param arg0 * @param arg1 * @param arg2 * @throws SAXException * @see org.xml.sax.ContentHandler#characters(char[], int, int) */ public void characters(char[] arg0, int arg1, int arg2) throws SAXException { } /** * Unimplemented SAX ContentHandler Method. * @param arg0 * @param arg1 * @param arg2 * @throws SAXException * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int) */ public void ignorableWhitespace(char[] arg0, int arg1, int arg2) throws SAXException { } /** * Unimplemented SAX ContentHandler Method. * @param arg0 * @param arg1 * @throws SAXException * @see org.xml.sax.ContentHandler#processingInstruction(java.lang.String, java.lang.String) */ public void processingInstruction(String arg0, String arg1) throws SAXException { } /** * Unimplemented SAX ContentHandler Method. * @param arg0 * @throws SAXException * @see org.xml.sax.ContentHandler#skippedEntity(java.lang.String) */ public void skippedEntity(String arg0) throws SAXException { } /** * Returns the name of the query. * Value is null before SAX processing is complete. * @return The query name. */ public String getRootElementName() { return rootElementName; } /** * Returns true if a non-projected query was forced to rowcount. * @return true if query returns a forced rowcount only. */ public boolean isForceRowCountOnly() { return isRowCountOnly(); } public void setForceRowCountOnly(boolean forceRowCountOnly) { setRowCountOnly(forceRowCountOnly); } /** * Returns the column names for projection queries. * Value is null before SAX processing is complete. * @return the columnNames */ public String[] getColumnNames() { return columnNames; } /** * Returns the SAX Parse time. * Value is invalid before SAX processing is complete. * @return the parseTime */ public long getParseTime() { return parseTime; } /** * Returns the alias list for the query. * Value is null before SAX processing is complete. * @return the aliases */ public String[] getAliases() { return aliases; } /** * Returns the pseudo-entity name of the projection list for projection queries. * Value is null before SAX processing is complete. * @return the projectionsName */ public String getProjectionsName() { return projectionsName; } /** * Returns the class name for the criteria/named query * Value is null before SAX processing is complete. * @return the className */ public String getClassName() { return className; } }