001: // Copyright (c) 2003, 2006 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.kawa.reflect;
005:
006: import gnu.bytecode.*;
007: import gnu.mapping.Procedure;
008: import gnu.mapping.Values;
009: import gnu.lists.*;
010: import java.io.*;
011: import gnu.expr.*;
012:
013: /** A type that matches some number of repetitions of a basetype. */
014:
015: public class OccurrenceType extends ObjectType implements
016: Externalizable, TypeValue {
017: Type base;
018: int minOccurs;
019: int maxOccurs;
020:
021: public Type getBase() {
022: return base;
023: }
024:
025: public int minOccurs() {
026: return minOccurs;
027: }
028:
029: public int maxOccurs() {
030: return maxOccurs;
031: }
032:
033: public OccurrenceType(Type base, int minOccurs, int maxOccurs) {
034: this .base = base;
035: this .minOccurs = minOccurs;
036: this .maxOccurs = maxOccurs;
037: }
038:
039: public static Type getInstance(Type base, int minOccurs,
040: int maxOccurs) {
041: if (minOccurs == 1 && maxOccurs == 1)
042: return base;
043: if (minOccurs == 0
044: && maxOccurs < 0
045: && (base == SingletonType.instance || base == Type.pointer_type))
046: return Type.pointer_type;
047: return new OccurrenceType(base, minOccurs, maxOccurs);
048: }
049:
050: public static final Type emptySequenceType = OccurrenceType
051: .getInstance(SingletonType.instance, 0, 0);
052:
053: public Type getImplementationType() {
054: return Type.pointer_type;
055: }
056:
057: public int compare(Type other) {
058: if (other instanceof OccurrenceType) {
059: OccurrenceType occOther = (OccurrenceType) other;
060: if (minOccurs == occOther.minOccurs
061: && maxOccurs == occOther.maxOccurs)
062: return base.compare(occOther.getBase());
063: }
064: /*
065: Type primeThis = itemPrimeType(getBase());
066: Type primeOther = itemPrimeType(other);
067: FIXME: Compare primThis with primOther AND the occurrence numbers.
068: */
069: return -2;
070: }
071:
072: public Object coerceFromObject(Object obj) {
073: if (obj instanceof Values) {
074: } else {
075: // Assumes that base is an item type. FIXME.
076: if (minOccurs <= 1 && maxOccurs != 0)
077: return base.coerceFromObject(obj);
078: }
079: // FIXME
080: if (!isInstance(obj))
081: throw new ClassCastException();
082: return obj;
083: }
084:
085: public boolean isInstance(Object obj) {
086: if (obj instanceof Values) {
087: Values vals = (Values) obj;
088: int pos = vals.startPos();
089: int n = 0;
090: if (base instanceof ItemPredicate) {
091: ItemPredicate pred = (ItemPredicate) base;
092: for (;;) {
093: boolean matches;
094: matches = pred.isInstancePos(vals, pos);
095: pos = vals.nextPos(pos);
096: if (pos == 0) {
097: return n >= minOccurs
098: && (maxOccurs < 0 || n <= maxOccurs);
099: }
100: if (!matches)
101: return false;
102: n++;
103: }
104: } else {
105:
106: for (;;) {
107: pos = vals.nextPos(pos);
108: if (pos == 0) {
109: return n >= minOccurs
110: && (maxOccurs < 0 || n <= maxOccurs);
111: }
112: Object value = vals.getPosPrevious(pos);
113: if (!base.isInstance(value))
114: return false;
115: n++;
116: }
117: }
118: } else {
119: if (minOccurs > 1 || maxOccurs == 0)
120: return false;
121: return base.isInstance(obj);
122: }
123: }
124:
125: public void emitTestIf(Variable incoming, Declaration decl,
126: Compilation comp) {
127: CodeAttr code = comp.getCode();
128: if (incoming != null)
129: code.emitLoad(incoming);
130: if (decl != null) {
131: code.emitDup();
132: decl.compileStore(comp);
133: }
134: comp.compileConstant(this );
135: code.emitSwap();
136: code.emitInvokeVirtual(isInstanceMethod);
137: code.emitIfIntNotZero();
138: }
139:
140: public void emitIsInstance(Variable incoming, Compilation comp,
141: Target target) {
142: gnu.kawa.reflect.InstanceOf.emitIsInstance(this , incoming,
143: comp, target);
144: }
145:
146: public Procedure getConstructor() {
147: return null;
148: }
149:
150: /** Return a conservative estimage on the min/max number of items of a type.
151: * @return {@code maxCount << 12 | minCount & 0xFFF},
152: * where a {@code maxCount} of -1 means unbounded.
153: */
154: public static int itemCountRange(Type type) {
155: if (type instanceof SingletonType)
156: return (1 << 12) | 1;
157: if (type instanceof OccurrenceType) {
158: OccurrenceType occ = (OccurrenceType) type;
159: int min = occ.minOccurs();
160: int max = occ.maxOccurs();
161: int bnum = itemCountRange(occ.getBase());
162: if ((min == 1 && max == 1) || bnum == 0)
163: return bnum;
164: if (max > 0xfffff)
165: max = -1;
166: if (max == 0)
167: return 0;
168: int bmin = bnum & 0xfff;
169: int bmax = bnum >> 12;
170: if (bnum != 0x1001) {
171: if (min > 0xfff)
172: min = 0xfff;
173: min = min * bmin;
174: if (min > 0xfff)
175: min = 0xfff;
176: if (max < 0 || bmax < 0)
177: max = -1;
178: else
179: max = max * bmax;
180: if (max > 0xfffff)
181: max = -1;
182: }
183: return (max << 12) | min;
184: }
185: if (type instanceof PrimType)
186: return type.isVoid() ? 0 : 0x1001;
187: if (type instanceof ArrayType)
188: return 0x1001;
189: if (type instanceof ObjectType) {
190: int cmp = type.compare(Compilation.typeValues);
191: if (cmp == -3)
192: return 0x1001;
193: }
194: return -1 << 12;
195: }
196:
197: /** Returna a quantifer kind for a sequence type.
198: * @return '0' if type is known to be a void (0-item) type;
199: * '1' if type is known to be a single-item type;
200: * '?' if type matches a sequence of 0 or 1 items;
201: * '+' if type matches a sequence of 1 or more items;
202: * '*' otherwise.
203: */
204: public static char itemCountCode(Type type) {
205: int num = itemCountRange(type);
206: int min = num & 0xFFF;
207: int max = num >> 12;
208: return max == 0 ? '0' : min == 0 ? (max == 1 ? '?' : '*')
209: : min == 1 && max == 1 ? '1' : '+';
210: }
211:
212: public static boolean itemCountIsZeroOrOne(Type type) {
213: // cute hack for: max == 0 || max == 1.
214: return (itemCountRange(type) >> 13) == 0;
215: }
216:
217: public static boolean itemCountIsOne(Type type) {
218: return itemCountRange(type) == 0x1001;
219: }
220:
221: /** QUery formal semantics "prime type"
222: */
223: public static Type itemPrimeType(Type type) {
224: while (type instanceof OccurrenceType)
225: type = ((OccurrenceType) type).getBase();
226: return itemCountIsOne(type) ? type : SingletonType.instance;
227:
228: }
229:
230: public void writeExternal(ObjectOutput out) throws IOException {
231: out.writeObject(base);
232: out.writeInt(minOccurs);
233: out.writeInt(maxOccurs);
234: }
235:
236: public String toString() {
237: String b = base.toString();
238: boolean parens = b == null || b.indexOf(' ') >= 0;
239: StringBuffer sbuf = new StringBuffer();
240: if (parens)
241: sbuf.append('(');
242: sbuf.append(b);
243: if (parens)
244: sbuf.append(')');
245: if (minOccurs == 1 && maxOccurs == 1)
246: ;
247: else if (minOccurs == 0 && maxOccurs == 1)
248: sbuf.append('?');
249: else if (minOccurs == 1 && maxOccurs == -1)
250: sbuf.append('+');
251: else if (minOccurs == 0 && maxOccurs == -1)
252: sbuf.append('*');
253: else {
254: sbuf.append('{');
255: sbuf.append(minOccurs);
256: sbuf.append(',');
257: if (maxOccurs >= 0)
258: sbuf.append(maxOccurs);
259: else
260: sbuf.append('*');
261: sbuf.append('}');
262: }
263: return sbuf.toString();
264: }
265:
266: public void readExternal(ObjectInput in) throws IOException,
267: ClassNotFoundException {
268: base = (Type) in.readObject();
269: minOccurs = in.readInt();
270: maxOccurs = in.readInt();
271: }
272:
273: public static final ClassType typeOccurrenceType = ClassType
274: .make("gnu.kawa.reflect.OccurrenceType");
275: static final Method isInstanceMethod = typeOccurrenceType
276: .getDeclaredMethod("isInstance", 1);
277: }
|