KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jruby > RubyMatchData


1 /***** BEGIN LICENSE BLOCK *****
2  * Version: CPL 1.0/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Common Public
5  * License Version 1.0 (the "License"); you may not use this file
6  * except in compliance with the License. You may obtain a copy of
7  * the License at http://www.eclipse.org/legal/cpl-v10.html
8  *
9  * Software distributed under the License is distributed on an "AS
10  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11  * implied. See the License for the specific language governing
12  * rights and limitations under the License.
13  *
14  * Copyright (C) 2001 Alan Moore <alan_moore@gmx.net>
15  * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
16  * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
17  * Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
18  * Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
19  * Copyright (C) 2004 Charles O Nutter <headius@headius.com>
20  * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
21  *
22  * Alternatively, the contents of this file may be used under the terms of
23  * either of the GNU General Public License Version 2 or later (the "GPL"),
24  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
25  * in which case the provisions of the GPL or the LGPL are applicable instead
26  * of those above. If you wish to allow use of your version of this file only
27  * under the terms of either the GPL or the LGPL, and not to allow others to
28  * use your version of this file under the terms of the CPL, indicate your
29  * decision by deleting the provisions above and replace them with the notice
30  * and other provisions required by the GPL or the LGPL. If you do not delete
31  * the provisions above, a recipient may use your version of this file under
32  * the terms of any one of the CPL, the GPL or the LGPL.
33  ***** END LICENSE BLOCK *****/

34 package org.jruby;
35
36 import org.jruby.runtime.CallbackFactory;
37 import org.jruby.runtime.ObjectAllocator;
38 import org.jruby.runtime.builtin.IRubyObject;
39
40 /**
41  *
42  * @author amoore
43  */

44 public class RubyMatchData extends RubyObject {
45     private String JavaDoc str;
46     private int[] begin;
47     private int[] end;
48
49     public RubyMatchData(Ruby runtime, String JavaDoc str, int[] begin, int[] end) {
50         super(runtime, runtime.getClass("MatchData"));
51         this.str = str;
52         this.begin = begin;
53         this.end = end;
54     }
55
56     public static RubyClass createMatchDataClass(Ruby runtime) {
57         // TODO: Is NOT_ALLOCATABLE_ALLOCATOR ok here, since you can't actually instanriate MatchData directly?
58
RubyClass matchDataClass = runtime.defineClass("MatchData", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
59         runtime.defineGlobalConstant("MatchingData", matchDataClass);
60
61         CallbackFactory callbackFactory = runtime.callbackFactory(RubyMatchData.class);
62
63         matchDataClass.defineFastMethod("captures", callbackFactory.getFastMethod("captures"));
64         matchDataClass.defineFastMethod("inspect", callbackFactory.getFastMethod("inspect"));
65         matchDataClass.defineFastMethod("size", callbackFactory.getFastMethod("size"));
66         matchDataClass.defineFastMethod("length", callbackFactory.getFastMethod("size"));
67         matchDataClass.defineFastMethod("offset", callbackFactory.getFastMethod("offset", RubyFixnum.class));
68         matchDataClass.defineFastMethod("begin", callbackFactory.getFastMethod("begin", RubyFixnum.class));
69         matchDataClass.defineFastMethod("end", callbackFactory.getFastMethod("end", RubyFixnum.class));
70         matchDataClass.defineFastMethod("to_a", callbackFactory.getFastMethod("to_a"));
71         matchDataClass.defineFastMethod("[]", callbackFactory.getFastOptMethod("aref"));
72         matchDataClass.defineFastMethod("pre_match", callbackFactory.getFastMethod("pre_match"));
73         matchDataClass.defineFastMethod("post_match", callbackFactory.getFastMethod("post_match"));
74         matchDataClass.defineFastMethod("to_s", callbackFactory.getFastMethod("to_s"));
75         matchDataClass.defineFastMethod("string", callbackFactory.getFastMethod("string"));
76
77         matchDataClass.getMetaClass().undefineMethod("new");
78
79         return matchDataClass;
80     }
81     
82     public IRubyObject captures() {
83         RubyArray arr = getRuntime().newArray(begin.length);
84         for (long i = 1; i < begin.length; i++) {
85             arr.append(group(i));
86         }
87         return arr;
88     }
89
90     public IRubyObject subseq(long beg, long len) {
91         // Subsequence begins at a valid index and a positive length
92
if (beg < 0 || beg > getSize() || len < 0) {
93             getRuntime().getNil();
94         }
95
96         if (beg + len > getSize()) {
97             len = getSize() - beg;
98         }
99         if (len < 0) {
100             len = 0;
101         }
102         if (len == 0) {
103             return getRuntime().newArray();
104         }
105         
106         RubyArray arr = getRuntime().newArray(0);
107         for (long i = beg; i < beg + len; i++) {
108             arr.append(group(i));
109         }
110         return arr;
111     }
112
113     public long getSize() {
114         return begin.length;
115     }
116
117     public IRubyObject group(long n) {
118         // Request an invalid group OR group is an empty match
119
if (n < 0 || n >= getSize() || begin[(int) n] == -1) {
120             return getRuntime().getNil();
121         }
122         // Fix for JRUBY-97: Temporary fix pending
123
// decision on UTF8-based string implementation.
124
// String#substring reuses the storage of the original string
125
// <http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4513622>
126
// Wrapping the String#substring in new String prevents this.
127
// This wrapping alone was enough to fix the failing test cases in
128
// JRUBY-97, but at the same time the testcase remained very slow
129
// The additional minor optimizations to RubyString as part of the fix
130
// dramatically improve the performance.
131
return getRuntime().newString(
132                 new String JavaDoc(str.substring(begin[(int) n], end[(int) n])));
133     }
134
135     public int matchStartPosition() {
136         return begin[0];
137     }
138
139     public int matchEndPosition() {
140         return end[0];
141     }
142
143     private boolean outOfBounds(RubyFixnum index) {
144         return outOfBounds(index.getLongValue());
145     }
146     
147     // version to work with Java primitives for efficiency
148
private boolean outOfBounds(long n) {
149         return n < 0 || n >= getSize();
150     }
151
152     //
153
// Methods of the MatchData Class:
154
//
155

156     /** match_aref
157      *
158      */

159     public IRubyObject aref(IRubyObject[] args) {
160         int argc = checkArgumentCount(args, 1, 2);
161         if (argc == 2) {
162             int beg = RubyNumeric.fix2int(args[0]);
163             int len = RubyNumeric.fix2int(args[1]);
164             if (beg < 0) {
165                 beg += getSize();
166             }
167             return subseq(beg, len);
168         }
169         if (args[0] instanceof RubyFixnum) {
170             return group(RubyNumeric.fix2int(args[0]));
171         }
172         if (args[0] instanceof RubyBignum) {
173             throw getRuntime().newIndexError("index too big");
174         }
175         if (args[0] instanceof RubyRange) {
176             long[] begLen = ((RubyRange) args[0]).getBeginLength(getSize(), true, false);
177             if (begLen == null) {
178                 return getRuntime().getNil();
179             }
180             return subseq(begLen[0], begLen[1]);
181         }
182         return group(RubyNumeric.num2long(args[0]));
183     }
184
185     /** match_begin
186      *
187      */

188     public IRubyObject begin(RubyFixnum index) {
189         long lIndex = index.getLongValue();
190         long answer = begin(lIndex);
191         
192         return answer == -1 ? getRuntime().getNil() : getRuntime().newFixnum(answer);
193     }
194     
195     public long begin(long index) {
196         return outOfBounds(index) ? -1 : begin[(int) index];
197     }
198
199     /** match_end
200      *
201      */

202     public IRubyObject end(RubyFixnum index) {
203         int lIndex = RubyNumeric.fix2int(index);
204         long answer = end(lIndex);
205
206         return answer == -1 ? getRuntime().getNil() : getRuntime().newFixnum(answer);
207     }
208     
209     public long end(long index) {
210         return outOfBounds(index) ? -1 : end[(int) index];
211     }
212     
213     public IRubyObject inspect() {
214         return anyToString();
215     }
216
217     /** match_size
218      *
219      */

220     public RubyFixnum size() {
221         return getRuntime().newFixnum(getSize());
222     }
223
224     /** match_offset
225      *
226      */

227     public IRubyObject offset(RubyFixnum index) {
228         if (outOfBounds(index)) {
229             return getRuntime().getNil();
230         }
231         return getRuntime().newArrayNoCopy(new IRubyObject[] { begin(index), end(index)});
232     }
233
234     /** match_pre_match
235      *
236      */

237     public RubyString pre_match() {
238         return getRuntime().newString(str.substring(0, begin[0]));
239     }
240
241     /** match_post_match
242      *
243      */

244     public RubyString post_match() {
245         return getRuntime().newString(str.substring(end[0]));
246     }
247
248     /** match_string
249      *
250      */

251     public RubyString string() {
252         RubyString frozenString = getRuntime().newString(str);
253         frozenString.freeze();
254         return frozenString;
255     }
256
257     /** match_to_a
258      *
259      */

260     public RubyArray to_a() {
261         RubyArray arr = getRuntime().newArray(begin.length);
262         for (long i = 0; i < begin.length; i++) {
263             arr.append(group(i));
264         }
265         return arr;
266     }
267
268     /** match_to_s
269      *
270      */

271     public IRubyObject to_s() {
272         return getRuntime().newString(str.substring(begin[0], end[0]));
273     }
274
275     public IRubyObject doClone() {
276         int len = (int) getSize();
277         int[] begin_p = new int[len];
278         int[] end_p = new int[len];
279         System.arraycopy(begin, 0, begin_p, 0, len);
280         System.arraycopy(end, 0, end_p, 0, len);
281         return new RubyMatchData(getRuntime(), str, begin_p, end_p);
282     }
283 }
284
Popular Tags