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      }