001: /***** BEGIN LICENSE BLOCK *****
002: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
003: *
004: * The contents of this file are subject to the Common Public
005: * License Version 1.0 (the "License"); you may not use this file
006: * except in compliance with the License. You may obtain a copy of
007: * the License at http://www.eclipse.org/legal/cpl-v10.html
008: *
009: * Software distributed under the License is distributed on an "AS
010: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
011: * implied. See the License for the specific language governing
012: * rights and limitations under the License.
013: *
014: * Copyright (C) 2001-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
015: * Copyright (C) 2002 Benoit Cerrina <b.cerrina@wanadoo.fr>
016: * Copyright (C) 2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
017: * Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
018: * Copyright (C) 2005 Charles O Nutter <headius@headius.com>
019: * Copyright (C) 2006 Miguel Covarrubias <mlcovarrubias@gmail.com>
020: * Copyright (C) 2006 Thomas E Enebo <enebo@acm.org>
021: *
022: * Alternatively, the contents of this file may be used under the terms of
023: * either of the GNU General Public License Version 2 or later (the "GPL"),
024: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
025: * in which case the provisions of the GPL or the LGPL are applicable instead
026: * of those above. If you wish to allow use of your version of this file only
027: * under the terms of either the GPL or the LGPL, and not to allow others to
028: * use your version of this file under the terms of the CPL, indicate your
029: * decision by deleting the provisions above and replace them with the notice
030: * and other provisions required by the GPL or the LGPL. If you do not delete
031: * the provisions above, a recipient may use your version of this file under
032: * the terms of any one of the CPL, the GPL or the LGPL.
033: ***** END LICENSE BLOCK *****/package org.jruby;
034:
035: import org.jruby.exceptions.RaiseException;
036: import org.jruby.runtime.CallbackFactory;
037: import org.jruby.runtime.MethodIndex;
038: import org.jruby.runtime.ThreadContext;
039: import org.jruby.runtime.builtin.IRubyObject;
040:
041: /** Implementation of the Comparable module.
042: *
043: */
044: public class RubyComparable {
045: public static RubyModule createComparable(Ruby runtime) {
046: RubyModule comparableModule = runtime
047: .defineModule("Comparable");
048: CallbackFactory callbackFactory = runtime
049: .callbackFactory(RubyComparable.class);
050: comparableModule.defineFastMethod("==", callbackFactory
051: .getFastSingletonMethod("equal",
052: RubyKernel.IRUBY_OBJECT));
053: comparableModule.defineFastMethod(">", callbackFactory
054: .getFastSingletonMethod("op_gt",
055: RubyKernel.IRUBY_OBJECT));
056: comparableModule.defineFastMethod(">=", callbackFactory
057: .getFastSingletonMethod("op_ge",
058: RubyKernel.IRUBY_OBJECT));
059: comparableModule.defineFastMethod("<", callbackFactory
060: .getFastSingletonMethod("op_lt",
061: RubyKernel.IRUBY_OBJECT));
062: comparableModule.defineFastMethod("<=", callbackFactory
063: .getFastSingletonMethod("op_le",
064: RubyKernel.IRUBY_OBJECT));
065: comparableModule.defineFastMethod("between?", callbackFactory
066: .getFastSingletonMethod("between_p",
067: RubyKernel.IRUBY_OBJECT,
068: RubyKernel.IRUBY_OBJECT));
069:
070: return comparableModule;
071: }
072:
073: /* ================
074: * Utility Methods
075: * ================
076: */
077:
078: /** rb_cmpint
079: *
080: */
081: public static int cmpint(IRubyObject val, IRubyObject a,
082: IRubyObject b) {
083: if (val.isNil()) {
084: cmperr(a, b);
085: }
086: if (val instanceof RubyFixnum) {
087: return RubyNumeric.fix2int((RubyFixnum) val);
088: }
089: if (val instanceof RubyBignum) {
090: if (((RubyBignum) val).getValue().signum() == -1) {
091: return 1;
092: }
093: return -1;
094: }
095:
096: final Ruby runtime = val.getRuntime();
097: final ThreadContext tc = runtime.getCurrentContext();
098: final RubyFixnum zero = RubyFixnum.one(runtime);
099: if (val.callMethod(tc, MethodIndex.OP_GT, ">", zero).isTrue()) {
100: return 1;
101: }
102: if (val.callMethod(tc, MethodIndex.OP_LT, "<", zero).isTrue()) {
103: return -1;
104: }
105: return 0;
106: }
107:
108: /** rb_cmperr
109: *
110: */
111: public static void cmperr(IRubyObject recv, IRubyObject other) {
112: IRubyObject target;
113: if (other.isImmediate()
114: || !(other.isNil() || other.isTrue() || other == recv
115: .getRuntime().getFalse())) {
116: target = other.inspect();
117: } else {
118: target = other.getType();
119: }
120:
121: throw recv.getRuntime().newArgumentError(
122: "comparison of " + recv.getType() + " with " + target
123: + " failed");
124: }
125:
126: /* ================
127: * Module Methods
128: * ================
129: */
130:
131: /** cmp_equal (cmp_eq inlined here)
132: *
133: */
134: public static IRubyObject equal(IRubyObject recv, IRubyObject other) {
135: if (recv == other) {
136: return recv.getRuntime().getTrue();
137: }
138: Ruby runtime = recv.getRuntime();
139: IRubyObject result = null;
140: try {
141: result = recv.callMethod(runtime.getCurrentContext(),
142: MethodIndex.OP_SPACESHIP, "<=>", other);
143: } catch (RaiseException e) {
144: return recv.getRuntime().getFalse();
145: }
146:
147: if (result.isNil()) {
148: return result;
149: }
150:
151: return RubyBoolean.newBoolean(runtime, cmpint(result, recv,
152: other) == 0);
153: }
154:
155: /** cmp_gt
156: *
157: */
158: // <=> may return nil in many circumstances, e.g. 3 <=> NaN
159: public static RubyBoolean op_gt(IRubyObject recv, IRubyObject other) {
160: final Ruby runtime = recv.getRuntime();
161: IRubyObject result = recv.callMethod(runtime
162: .getCurrentContext(), MethodIndex.OP_SPACESHIP, "<=>",
163: other);
164:
165: if (result.isNil()) {
166: cmperr(recv, other);
167: }
168:
169: return RubyBoolean.newBoolean(runtime, cmpint(result, recv,
170: other) > 0);
171: }
172:
173: /** cmp_ge
174: *
175: */
176: public static RubyBoolean op_ge(IRubyObject recv, IRubyObject other) {
177: final Ruby runtime = recv.getRuntime();
178: IRubyObject result = recv.callMethod(runtime
179: .getCurrentContext(), MethodIndex.OP_SPACESHIP, "<=>",
180: other);
181:
182: if (result.isNil()) {
183: cmperr(recv, other);
184: }
185:
186: return RubyBoolean.newBoolean(runtime, cmpint(result, recv,
187: other) >= 0);
188: }
189:
190: /** cmp_lt
191: *
192: */
193: public static RubyBoolean op_lt(IRubyObject recv, IRubyObject other) {
194: final Ruby runtime = recv.getRuntime();
195: IRubyObject result = recv.callMethod(runtime
196: .getCurrentContext(), MethodIndex.OP_SPACESHIP, "<=>",
197: other);
198:
199: if (result.isNil()) {
200: cmperr(recv, other);
201: }
202:
203: return RubyBoolean.newBoolean(runtime, cmpint(result, recv,
204: other) < 0);
205: }
206:
207: /** cmp_le
208: *
209: */
210: public static RubyBoolean op_le(IRubyObject recv, IRubyObject other) {
211: final Ruby runtime = recv.getRuntime();
212: IRubyObject result = recv.callMethod(runtime
213: .getCurrentContext(), MethodIndex.OP_SPACESHIP, "<=>",
214: other);
215:
216: if (result.isNil()) {
217: cmperr(recv, other);
218: }
219:
220: return RubyBoolean.newBoolean(runtime, cmpint(result, recv,
221: other) <= 0);
222: }
223:
224: /** cmp_between
225: *
226: */
227: public static RubyBoolean between_p(IRubyObject recv,
228: IRubyObject first, IRubyObject second) {
229:
230: return recv.getRuntime().newBoolean(
231: op_lt(recv, first).isFalse()
232: && op_gt(recv, second).isFalse());
233: }
234: }
|