001    /*
002     * Copyright (c) 2007-2015 Concurrent, Inc. All Rights Reserved.
003     *
004     * Project and contact information: http://www.cascading.org/
005     *
006     * This file is part of the Cascading project.
007     *
008     * Licensed under the Apache License, Version 2.0 (the "License");
009     * you may not use this file except in compliance with the License.
010     * You may obtain a copy of the License at
011     *
012     *     http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing, software
015     * distributed under the License is distributed on an "AS IS" BASIS,
016     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017     * See the License for the specific language governing permissions and
018     * limitations under the License.
019     */
020    
021    package cascading.operation.xml;
022    
023    import javax.xml.parsers.DocumentBuilder;
024    import javax.xml.xpath.XPathConstants;
025    import javax.xml.xpath.XPathExpression;
026    import javax.xml.xpath.XPathExpressionException;
027    
028    import cascading.flow.FlowProcess;
029    import cascading.operation.Filter;
030    import cascading.operation.FilterCall;
031    import cascading.operation.OperationException;
032    import cascading.tuple.Tuple;
033    import cascading.util.Pair;
034    import org.slf4j.Logger;
035    import org.slf4j.LoggerFactory;
036    import org.w3c.dom.Document;
037    
038    /**
039     * XPathFilter will filter out a Tuple if the given XPath expression returns false. Set removeMatch to true
040     * if the filter should be reversed.
041     */
042    public class XPathFilter extends XPathOperation implements Filter<Pair<DocumentBuilder, Tuple>>
043      {
044      /** Field LOG */
045      private static final Logger LOG = LoggerFactory.getLogger( XPathFilter.class );
046    
047      /** Field removeMatch */
048      private boolean removeMatch = false;
049    
050      /**
051       * Constructor XPathFilter creates a new XPathFilter instance.
052       *
053       * @param namespaces of type String[][]
054       * @param path       of type String
055       */
056      public XPathFilter( String[][] namespaces, String path )
057        {
058        super( 1, namespaces, path );
059        }
060    
061      /**
062       * Constructor XPathFilter creates a new XPathFilter instance.
063       *
064       * @param removeMatch of type boolean
065       * @param namespaces  of type String[][]
066       * @param path        of type String
067       */
068      public XPathFilter( boolean removeMatch, String[][] namespaces, String path )
069        {
070        super( 1, namespaces, path );
071        this.removeMatch = removeMatch;
072        }
073    
074      @Override
075      public boolean isRemove( FlowProcess flowProcess, FilterCall<Pair<DocumentBuilder, Tuple>> filterCall )
076        {
077        String argument = filterCall.getArguments().getString( 0 );
078        Document document = parseDocument( filterCall.getContext().getLhs(), argument );
079        XPathExpression expression = getExpressions().get( 0 );
080    
081        try
082          {
083          boolean value = (Boolean) expression.evaluate( document, XPathConstants.BOOLEAN );
084    
085          LOG.debug( "xpath: {} matches: {}", paths[ 0 ], value );
086    
087          return value == removeMatch;
088          }
089        catch( XPathExpressionException exception )
090          {
091          throw new OperationException( "could not evaluate xpath expression: " + paths[ 0 ], exception );
092          }
093        }
094      }