    001    /*
    002     * Copyright (c) 2007-2014 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.regex;
    022
    023    import java.beans.ConstructorProperties;
    024    import java.util.regex.Matcher;
    025
    026    import cascading.flow.FlowProcess;
    027    import cascading.management.annotation.Property;
    028    import cascading.management.annotation.PropertyDescription;
    029    import cascading.management.annotation.Visibility;
    030    import cascading.operation.Function;
    031    import cascading.operation.FunctionCall;
    032    import cascading.operation.OperationCall;
    033    import cascading.tuple.Fields;
    034    import cascading.tuple.Tuple;
    035    import cascading.util.Pair;
    036
    037    /**
    038     * Class RegexReplace is used to replace a matched regex with a replacement value.
    039     * <p/>
    040     * RegexReplace only expects one field value. If more than one argument value is passed, only the
    041     * first is handled, the remainder are ignored.
    042     */
    043    public class RegexReplace extends RegexOperation<Pair<Matcher, Tuple>> implements Function<Pair<Matcher, Tuple>>
    044      {
    045      /** Field replacement */
    046      private final String replacement;
    047      /** Field replaceAll */
    048      private boolean replaceAll = true;
    049
    050      /**
    051       * Constructor RegexReplace creates a new RegexReplace instance,
    052       *
    053       * @param fieldDeclaration of type Fields
    054       * @param patternString    of type String
    055       * @param replacement      of type String
    056       * @param replaceAll       of type boolean
    057       */
    058      @ConstructorProperties({"fieldDeclaration", "patternString", "replacement", "replaceAll"})
    059      public RegexReplace( Fields fieldDeclaration, String patternString, String replacement, boolean replaceAll )
    060        {
    061        this( fieldDeclaration, patternString, replacement );
    062        this.replaceAll = replaceAll;
    063        }
    064
    065      /**
    066       * Constructor RegexReplace creates a new RegexReplace instance.
    067       *
    068       * @param fieldDeclaration of type Fields
    069       * @param patternString    of type String
    070       * @param replacement      of type String
    071       */
    072      @ConstructorProperties({"fieldDeclaration", "patternString", "replacement"})
    073      public RegexReplace( Fields fieldDeclaration, String patternString, String replacement )
    074        {
    075        super( 1, fieldDeclaration, patternString );
    076        this.replacement = replacement;
    077        }
    078
    079      @Property(name = "replacement", visibility = Visibility.PUBLIC)
    080      @PropertyDescription("The string replacement value.")
    081      public String getReplacement()
    082        {
    083        return replacement;
    084        }
    085
    086      @Property(name = "replaceAll", visibility = Visibility.PUBLIC)
    087      @PropertyDescription("Will replace all occurrences of pattern.")
    088      public boolean isReplaceAll()
    089        {
    090        return replaceAll;
    091        }
    092
    093      @Override
    094      public void prepare( FlowProcess flowProcess, OperationCall<Pair<Matcher, Tuple>> operationCall )
    095        {
    096        operationCall.setContext( new Pair<Matcher, Tuple>( getPattern().matcher( "" ), Tuple.size( 1 ) ) );
    097        }
    098
    099      @Override
    100      public void operate( FlowProcess flowProcess, FunctionCall<Pair<Matcher, Tuple>> functionCall )
    101        {
    102        // coerce to string
    103        String value = functionCall.getArguments().getString( 0 );
    104
    105        // make safe
    106        if( value == null )
    107          value = "";
    108
    109        Tuple output = functionCall.getContext().getRhs();
    110        Matcher matcher = functionCall.getContext().getLhs().reset( value );
    111
    112        if( replaceAll )
    113          output.set( 0, matcher.replaceAll( replacement ) );
    114        else
    115          output.set( 0, matcher.replaceFirst( replacement ) );
    116
    117        functionCall.getOutputCollector().add( output );
    118        }
    119
    120      @Override
    121      public boolean equals( Object object )
    122        {
    123        if( this == object )
    124          return true;
    125        if( !( object instanceof RegexReplace ) )
    126          return false;
    127        if( !super.equals( object ) )
    128          return false;
    129
    130        RegexReplace that = (RegexReplace) object;
    131
    132        if( replaceAll != that.replaceAll )
    133          return false;
    134        if( replacement != null ? !replacement.equals( that.replacement ) : that.replacement != null )
    135          return false;
    136
    137        return true;
    138        }
    139
    140      @Override
    141      public int hashCode()
    142        {
    143        int result = super.hashCode();
    144        result = 31 * result + ( replacement != null ? replacement.hashCode() : 0 );
    145        result = 31 * result + ( replaceAll ? 1 : 0 );
    146        return result;
    147        }
    148      }