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