001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.lib.contract.lang.type;
028:
029: import java.util.*;
030:
031: import org.cougaar.lib.contract.lang.*;
032: import org.cougaar.lib.contract.lang.cache.ClassCache;
033:
034: /**
035: * Holds [Not]Class Type information, for example "is:Not:List".
036: */
037: public class TypeImpl implements org.cougaar.lib.contract.lang.Type {
038:
039: private TypeImpl() {
040: }
041:
042: public static final Type getInstance(final String s) {
043: TypeImpl ti = new TypeImpl();
044: ti.setClass(s);
045: return ti;
046: }
047:
048: public static final Type getInstance(final boolean xnot,
049: final String classname) {
050: TypeImpl ti = new TypeImpl();
051: ti.setClass(xnot, classname);
052: return ti;
053: }
054:
055: public static final Type getInstance(final boolean xnot,
056: final Class xcl) {
057: TypeImpl ti = new TypeImpl();
058: ti.not = xnot;
059: ti.cl = xcl;
060: return ti;
061: }
062:
063: private boolean not;
064:
065: public final boolean isNot() {
066: return not;
067: }
068:
069: private Class cl;
070:
071: public final Class getClazz() {
072: return cl;
073: }
074:
075: /**
076: * Set the [Not]Class fields using the given String.
077: * <p>
078: * <pre>
079: * Accepts, in BNF:
080: * <code>
081: * "is:" ("Not:")* ("Null" | ([Package] Classname))
082: * </code>
083: * where "Not:" and "Null" can be any mix of uppercase/lowercase, but
084: * the "is:", Package and Classname must be case-sensitive.
085: *
086: * The ":" is useful for several reasons:
087: * 1) Passing any String not starting with "is:" will throw an
088: * exception -- this prevents confusion between methods starting
089: * with "is" (e.g. "Set.isEmpty()")
090: * 2) Indicating a classname starting with "Not"
091: * e.g. "NotSerializableException"
092: * 3) Indicating a class actually named "Null". (yuck!)
093: *
094: * FIXME classname "Null"/"Not" not implemented for now!
095: *
096: * Note that Null is actually treated as NotObject, since:
097: * 1) is:Not:Null == is:Object
098: * 2) is:Null == is:Not:Object
099: * This greatly simplifies the type reasoning...
100: *
101: * Examples:
102: *
103: * is:Object (instanceof Object)
104: * is:Not:Object (!(instanceof Object))
105: * is:Not:Not:Object (instanceof Object)
106: *
107: * is:Null (!(instanceof Object))
108: * is:null (!(instanceof Object))
109: * is:Not:Null (instanceof Object)
110: *
111: * is:java.util.List (instanceof java.util.List)
112: * is:Not:java.util.List (!(instanceof java.util.List))
113: * </pre>
114: */
115: private final void setClass(String s) {
116: if (!(s.startsWith("is:"))) {
117: throw new IllegalArgumentException(
118: "Type must start with \"is:\", e.g. "
119: + "\"is:java.util.List\", not \"" + s
120: + "\"");
121: }
122:
123: int i = 3;
124: int slen = s.length();
125:
126: boolean xnot = false;
127: while ((i < slen) && s.regionMatches(true, i, "not:", 0, 4)) {
128: xnot = (!(xnot));
129: i += 4;
130: }
131:
132: String classname = s.substring(i);
133: Class xcl;
134: if (s.regionMatches(true, i, "null", 0, 4)) {
135: // isNull == isNotObject
136: // isNotNull == isObject
137: xnot = !xnot;
138: xcl = Object.class;
139: } else {
140: xcl = ClassCache.lookup(classname);
141: if (xcl == null) {
142: throw new IllegalArgumentException(
143: "Type given unknown classname \""
144: + s.substring(i) + "\"");
145: }
146: }
147:
148: // FIXME move to getInstance
149: this .not = xnot;
150: this .cl = xcl;
151: }
152:
153: private final void setClass(boolean xnot, String classname) {
154: if (classname.equalsIgnoreCase("null")) {
155: // isNull == isNotObject
156: // isNotNull == isObject
157: this .not = !xnot;
158: this .cl = Object.class;
159: } else {
160: this .not = xnot;
161: this .cl = ClassCache.lookup(classname);
162: if (this .cl == null) {
163: throw new IllegalArgumentException(
164: "Type given unknown classname \"" + classname
165: + "\"");
166: }
167: }
168: }
169:
170: public final String toString() {
171: return toString(true);
172: }
173:
174: /**
175: * Convert fields to <code>setClass</code> format.
176: */
177: public final String toString(final boolean verbose) {
178: if (cl == Object.class) {
179: if (not) {
180: return "is:Null";
181: } else {
182: return "is:Not:Null";
183: }
184: } else {
185: String classname = ClassCache.toString(cl, verbose);
186: StringBuffer sb = new StringBuffer(3 + // "is:"
187: (not ? 4 : 0) + // "Not:"
188: classname.length()); // classname
189: sb.append("is:");
190: if (not) {
191: sb.append("Not:");
192: }
193: sb.append(classname);
194: return sb.toString();
195: }
196: }
197:
198: public final boolean equals(final Type xtype) {
199: if (xtype == null) {
200: return false;
201: }
202: if (xtype.isNot() != not) {
203: return false;
204: }
205: Class xcl = xtype.getClazz();
206: return ((cl == xcl) || (cl.equals(xcl)));
207: }
208:
209: public final boolean equals(final Object o) {
210: return ((o instanceof Type) ? equals((Type) o) : false);
211: }
212:
213: public final boolean implies(Type xtype) {
214: return TypeCompare.implies(not, cl, xtype.isNot(), xtype
215: .getClazz());
216: }
217:
218: public final boolean implies(final boolean xnot, final Class xcl) {
219: return TypeCompare.implies(not, cl, xnot, xcl);
220: }
221:
222: public final boolean impliedBy(final Type xtype) {
223: return TypeCompare.implies(xtype.isNot(), xtype.getClazz(),
224: not, cl);
225: }
226:
227: public final boolean impliedBy(final boolean xnot, final Class xcl) {
228: return TypeCompare.implies(xnot, xcl, not, cl);
229: }
230:
231: /**
232: * Create a random <code>Type</code> instance, using one of the
233: * classes listed in <code>randomClasses</code>.
234: */
235: public static Type random() {
236: boolean rnot = (Math.random() > 0.5);
237: int rclIdx = (int) (Math.random() * (double) randomClassNames.length);
238: if (rclIdx >= randomClassNames.length) {
239: rclIdx--;
240: }
241: String rclname = randomClassNames[rclIdx];
242: return getInstance(rnot, rclname);
243: }
244:
245: /**
246: * Bunch of random classes, intended for testing <code>TypeCompare</code>
247: */
248: private final static String[] randomClassNames = {
249: "java.lang.Object", "java.lang.Integer",
250: "java.lang.String", "java.lang.Exception",
251: "java.lang.IllegalArgumentException",
252: "java.lang.NullPointerException", "java.util.Collection",
253: "java.util.List", "java.util.AbstractList",
254: "java.util.ArrayList", "java.util.LinkedList",
255: "java.util.Vector", "java.util.Map", "java.util.HashMap",
256: "java.util.TreeMap", "java.util.Set", "java.util.HashSet",
257: "java.util.TreeSet" };
258:
259: }
|