View Javadoc

1   ////////////////////////////////////////////////////////////////////////////////
2   // MillScript: an Open Spice interpreter and batch website creation tool
3   // Copyright (C) 2001-2005 Open World Ltd
4   // Copyright (C) 2005 Kevin Rogers
5   //
6   // This file is part of MillScript.
7   //
8   // MillScript is free software; you can redistribute it and/or modify it under
9   // the terms of the GNU General Public License as published by the Free
10  // Software Foundation; either version 2 of the License, or (at your option)
11  // any later version.
12  //
13  // MillScript is distributed in the hope that it will be useful, but WITHOUT
14  // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16  // more details.
17  //
18  // You should have received a copy of the GNU General Public License along with
19  // MillScript; if not, write to the Free Software Foundation, Inc., 59 Temple
20  // Place, Suite 330, Boston, MA  02111-1307  USA
21  ////////////////////////////////////////////////////////////////////////////////
22  package org.millscript.millscript.datatypes;
23  
24  import org.millscript.commons.util.list.IArrayList;
25  import org.millscript.millscript.alert.Alerts;
26  
27  import java.io.Serializable;
28  import java.util.regex.Matcher;
29  import java.util.regex.Pattern;
30  
31  /**
32   * This class represents a MillScript Binding, which itself reprsents the set
33   * of match variables for a given regular expression and string.
34   */
35  public final class Binding extends IArrayList< String > implements Serializable {
36  
37      /**
38       * This is the ID from the release 10.2.0 for future compatibility.
39       */
40      private static final long serialVersionUID = 3690755102965314870L;
41  
42      /**
43       * Returns a new binding for the specified matcher.
44       *
45       * @param s the original string that was matched against
46       * @param m the matcher to make a binding from
47       */
48      public static final Binding newBinding( final String s, final Matcher m ) {
49          // Initialise an array of the match variables
50          final String[] matchVars = new String[ m.groupCount() ];
51          // Initialise the array of match variable start indices
52          final int[] starts = new int[ m.groupCount() + 1 ];
53          // Initialise the array of match variable end indices
54          final int[] ends = new int[ m.groupCount() + 1 ];
55          // Initialise the match variable start and end index array elements
56          // with the relevant indices
57          // NOTE - During this process we must add one to the start due to Java
58          // using zero based indexing
59          starts[ 0 ] = m.start( 0 ) + 1;
60          ends[ 0 ] = m.end( 0 );
61          for ( int i = 1; i <= m.groupCount(); i++ ) {
62              starts[ i ] = m.start( i ) + 1;
63              ends[ i ] = m.end( i );
64              matchVars[ i - 1 ] = s.substring( m.start( i ), m.end( i ) );
65          }
66          return new Binding( m.group(), s, m.pattern(), matchVars, starts, ends );
67      }
68  
69      /**
70       * The section of the original string matched by the regular expression
71       * used to make this binding.
72       */
73      private final String matchedString;
74  
75      /**
76       * The original string the regex was matched against.
77       */
78      private final String originalString;
79  
80      /**
81       * The pattern that was interpreted to get this binding.
82       */
83      private final Pattern pattern;
84  
85      /**
86       * An array of the match variable start indices. Each element is the index
87       * of the start of the relevant match variable within the original string
88       * we matched against. Note the indices are 1 based, not 0.
89       */
90      private final int[] starts;
91  
92      /**
93       * An array of the match variable end indices. Each element is the index
94       * of the end of the relevant match variable within the original string we
95       * matched against. Note the indices are 1 based, not 0.
96       */
97      private final int[] ends;
98  
99      /**
100      * Constructs a new binding with the specified match variables. The correct
101      * way to construct a new binding is through the static
102      * {@link #newBinding(String, Matcher)} method.
103      *
104      * @param ms    the whole matched string(i.e. usually considered group
105      * zero)
106      * @param os    the original string that was matched against
107      * @param p the pattern matched against the original string
108      * @param matches   an array of match variable strings(i.e. groups within
109      * the pattern)
110      * @param st    an array of match variable string start indicies(one based)
111      * within the original string, the first position in the array should hold
112      * the index for the matched string
113      * @param en    an array of match variable string end indicies(one based)
114      * within the original string, the first position in the array should hold
115      * the index for the matched string
116      */
117     private Binding( final String ms, final String os, final Pattern p, final String[] matches, final int[] st, final int[] en ) {
118         super( matches, true );
119         this.matchedString = ms;
120         this.originalString = os;
121         this.pattern = p;
122         this.starts = st;
123         this.ends = en;
124     }
125 
126     /**
127      * Returns the section of the original string matched by the regular
128      * expression used to make this binding.
129      *
130      * @return  a String which is this bindings section of the original string
131      */
132     public String getMatchedString() {
133         return matchedString;
134     }
135 
136     /**
137      * Returns the index in the original string of the last character of the
138      * matched section. Note the index is 1 based, not 0.
139      *
140      * @return  the index in the original string of the last character of the
141      * matched section
142      */
143     public Integer getMatchedStringEnd() {
144         return new Integer( this.ends[ 0 ] );
145     }
146 
147     /**
148      * Returns the index in the original string of the first character of the
149      * matched section. Note the index is 1 based, not 0.
150      *
151      * @return  the index in the original string of the first character of the
152      * matched section
153      */
154     public Integer getMatchedStringStart() {
155         return new Integer( this.starts[ 0 ] );
156     }
157 
158     /**
159      * Returns the binding for the specified match variable.
160      *
161      * @param n the match variable to get
162      * @return  a String holding the binding for the specified match variable
163      */
164     public String getMatchVar( final int n ) {
165         // Ensure the reference is valid
166         this.validateMatchVarReference( n );
167         return this.originalString.substring( this.starts[n] - 1, this.ends[n] );
168     }
169 
170     /**
171      * Returns the index in the original string of the last character of the
172      * specified match variable. Note the index is 1 based, not 0.
173      *
174      * @return  the index in the original string of the last character of the
175      * specified match variable
176      */
177     public Integer getMatchVarEnd( final int n ) {
178         // Ensure the reference is valid
179         this.validateMatchVarReference( n );
180         return new Integer( this.ends[n] );
181     }
182 
183     /**
184      * Returns the index in the original string of the first character of the
185      * specified match variable. Note the index is 1 based, not 0.
186      *
187      * @return  the index in the original string of the first character of the
188      * specified match variable
189      */
190     public Integer getMatchVarStart( final int n ) {
191         // Ensure the reference is valid
192         this.validateMatchVarReference( n );
193         return new Integer( this.starts[n] );
194     }
195 
196     /**
197      * Returns the original string the regex was matched against.
198      *
199      * @return  the original String the regex was matched against
200      */
201     public String getOriginalString() {
202         return originalString;
203     }
204 
205     /**
206      * Returns the pattern used to make this binding.
207      *
208      * @return  the Pattern used to make this binding.
209      */
210     public Pattern getPattern() {
211         return this.pattern;
212     }
213 
214     /**
215      * Validates the specified integer as a match variable reference,
216      * generating an alert if the refernce is not valid. A valid refernce is a
217      * non-zero positive integer that is less than or equal to the number of
218      * match variables in this binding.
219      *
220      * @param n the match variable reference to validate
221      */
222     public void validateMatchVarReference( final int n ) {
223         if ( n < 1  ) {
224             // You can't have a zero or negative reference for a match variable
225             throw Alerts.eval(
226                 "Illegal match variable reference",
227                 "Match variable references must be non-zero positive integers"
228             ).culprit( "match variable reference", new Integer( n ) ).mishap();
229         } else if ( n > this.size() ) {
230             throw Alerts.eval(
231                 "Invalid match variable reference",
232                 "There aren't that many match variables in this binding"
233             ).
234             culprit( "match variable reference", new Integer( n ) ).
235             culprit( "match count", this.size() ).
236             mishap();
237         }
238     }
239 
240 }