/interpreter/tags/reactive-pattern-matching/src/edu/vub/at/objects/symbiosis/JavaConstructor.java
Java | 270 lines | 162 code | 40 blank | 68 comment | 23 complexity | abcaf0021118dd062b88df556789d597 MD5 | raw file
1/** 2 * AmbientTalk/2 Project 3 * JavaConstructor.java created on May 20, 2008 at 4:56:32 PM 4 * (c) Programming Technology Lab, 2006 - 2007 5 * Authors: Tom Van Cutsem & Stijn Mostinckx 6 * 7 * Permission is hereby granted, free of charge, to any person 8 * obtaining a copy of this software and associated documentation 9 * files (the "Software"), to deal in the Software without 10 * restriction, including without limitation the rights to use, 11 * copy, modify, merge, publish, distribute, sublicense, and/or 12 * sell copies of the Software, and to permit persons to whom the 13 * Software is furnished to do so, subject to the following 14 * conditions: 15 * 16 * The above copyright notice and this permission notice shall be 17 * included in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 * OTHER DEALINGS IN THE SOFTWARE. 27 */ 28package edu.vub.at.objects.symbiosis; 29 30import edu.vub.at.eval.Evaluator; 31import edu.vub.at.exceptions.InterpreterException; 32import edu.vub.at.exceptions.XIllegalOperation; 33import edu.vub.at.exceptions.XNotInstantiatable; 34import edu.vub.at.exceptions.XTypeMismatch; 35import edu.vub.at.objects.ATBoolean; 36import edu.vub.at.objects.ATClosure; 37import edu.vub.at.objects.ATContext; 38import edu.vub.at.objects.ATMethod; 39import edu.vub.at.objects.ATObject; 40import edu.vub.at.objects.ATTable; 41import edu.vub.at.objects.coercion.NativeTypeTags; 42import edu.vub.at.objects.grammar.ATBegin; 43import edu.vub.at.objects.grammar.ATSymbol; 44import edu.vub.at.objects.mirrors.NativeClosure; 45import edu.vub.at.objects.natives.NATBoolean; 46import edu.vub.at.objects.natives.NATByRef; 47import edu.vub.at.objects.natives.NATContext; 48import edu.vub.at.objects.natives.NATNil; 49import edu.vub.at.objects.natives.NATNumber; 50import edu.vub.at.objects.natives.NATTable; 51import edu.vub.at.objects.natives.NATText; 52import edu.vub.at.objects.natives.grammar.AGBegin; 53 54import java.lang.reflect.Array; 55import java.lang.reflect.Constructor; 56import java.util.Vector; 57 58/** 59 * JavaConstructor is a wrapper class encapsulating one or more java.lang.reflect.Constructor objects. 60 * All methods in the choices array are overloaded constructors for the same class. The choices array 61 * should never be empty! 62 * 63 * Since constructors do not need an receiver to become a full closure, this class implements both the 64 * ATMethod and the ATJavaClosure interface. The latter ensures that constructors can be cast properly. 65 * 66 * @author smostinc 67 */ 68public class JavaConstructor extends NATByRef implements ATMethod, ATJavaClosure { 69 70 71 protected final Class class_; 72 protected final Constructor[] choices_; 73 74 private ATContext context_ = null; // lazily initialized 75 76 public JavaConstructor(Class clazz) { 77 this(clazz, clazz.getConstructors()); 78 } 79 80 public JavaConstructor(Class clazz, Constructor[] choices) { 81 // assertion 82 class_ = clazz; 83 choices_ = choices; 84 } 85 86 // ATObject Interface implementation 87 88 public ATBoolean base__opeql__opeql_(ATObject comparand) throws InterpreterException { 89 return NATBoolean.atValue(this.equals(comparand)); 90 } 91 92 /** 93 * Two JavaConstructor instances are equal if they both represent a set of constructors 94 * from the same declaring class. 95 */ 96 public boolean equals(Object other) { 97 if (other instanceof JavaConstructor) { 98 JavaConstructor mth = (JavaConstructor) other; 99 return (mth.class_ == this.class_); 100 } else { 101 return false; 102 } 103 } 104 105 public ATTable meta_typeTags() throws InterpreterException { 106 return NATTable.of(NativeTypeTags._METHOD_, NativeTypeTags._CLOSURE_); 107 } 108 109 110 // ATMethod Interface implementation 111 112 public ATObject base_apply(ATTable arguments, ATContext ctx) throws InterpreterException { 113 return base_apply(arguments); 114 } 115 116 public ATObject base_applyInScope(ATTable arguments, ATContext ctx) throws InterpreterException { 117 return base_apply(arguments); 118 } 119 120 public ATBegin base_bodyExpression() throws InterpreterException { 121 // list all of the method signatures of the (possibly overloaded) Java method 122 StringBuffer buff = new StringBuffer("Java implementation of: "); 123 for (int i = 0; i < choices_.length; i++) { 124 buff.append("\n"); 125 buff.append(choices_[i].toString()); 126 } 127 buff.append("\n"); 128 return new AGBegin(NATTable.atValue(new ATObject[] { NATText.atValue(buff.toString()) })); 129 } 130 131 public ATSymbol base_name() throws InterpreterException { 132 return NATNil._NEW_NAME_; 133 } 134 135 public ATTable base_parameters() throws InterpreterException { 136 return Evaluator._ANON_MTH_ARGS_; 137 } 138 139 public ATTable base_annotations() throws InterpreterException { 140 return NATTable.EMPTY; 141 } 142 143 public NATText meta_print() throws InterpreterException { 144 return NATText.atValue("<java constructor:"+class_+">"); 145 } 146 147 public ATMethod asMethod() throws XTypeMismatch { 148 return this; 149 } 150 151 /** 152 * When selected from a JavaClass object, the constructor needs to be wrapped in a dedicated 153 * closure object. For constructors the JavaConstructor instance itself can be used. 154 */ 155 public ATClosure base_wrap(ATObject lexicalScope, ATObject dynamicReceiver) { 156 return this; 157 } 158 159 // ATJavaClosure Interface implementation 160 161 public ATClosure asClosure() throws InterpreterException { 162 return this; 163 } 164 165 public ATObject base_apply(ATTable arguments) throws InterpreterException { 166 return Symbiosis.symbioticInstanceCreation(this, arguments.asNativeTable().elements_); 167 } 168 169 public ATObject base_applyInScope(ATTable arguments, ATObject scope) throws InterpreterException { 170 return base_apply(arguments); 171 } 172 173 public ATContext base_context() throws InterpreterException { 174 if(context_ == null) { 175 JavaClass atClass = JavaClass.wrapperFor(class_); 176 context_ = new NATContext(atClass, atClass); 177 } 178 179 return context_; 180 } 181 182 public ATObject base_escape() throws InterpreterException { 183 throw new XIllegalOperation("Cannot call escape() in a Java Constructor"); 184 } 185 186 public ATMethod base_method() throws InterpreterException { 187 return this; 188 } 189 190 public ATObject base_whileTrue_(final ATClosure body) throws InterpreterException { 191 final ATClosure test = this; 192 ATBoolean cond; 193 194 while (true) { 195 // cond = self.apply() 196 cond = this.base_apply(NATTable.EMPTY).asBoolean(); 197 if(cond.isNativeBoolean()) { 198 // cond is a native boolean, perform the conditional ifTrue: test natively 199 if (cond.asNativeBoolean().javaValue) { 200 // execute body and continue while loop 201 body.base_apply(NATTable.EMPTY); 202 continue; 203 } else { 204 // return nil 205 return Evaluator.getNil(); 206 } 207 } else { 208 // cond is a user-defined boolean, do a recursive send 209 return cond.base_ifTrue_(new NativeClosure(this) { 210 public ATObject base_apply(ATTable args) throws InterpreterException { 211 // if user-defined bool is true, execute body and recurse 212 body.base_apply(NATTable.EMPTY); 213 return test.base_whileTrue_(body); 214 } 215 }); 216 } 217 } } 218 219 /** 220 * For each Method in the wrapped JavaMethod's choices_, check whether it is compatible with 221 * the given types. If so, add it to the choices_ array of the new JavaMethod. 222 */ 223 public ATClosure base_cast(ATObject[] types) throws InterpreterException { 224 // unwrap the JavaClass wrappers 225 Class[] actualTypes = new Class[types.length]; 226 for (int i = 0; i < actualTypes.length; i++) { 227 // Array types may be represented as one-arg tables of a type: [Type] 228 // TODO: properly refactor the instanceof test 229 // problem: cannot do base_isTable because JavaObject/JavaClass objects will say yes! 230 if (types[i] instanceof NATTable) { 231 // Array.newInstance([Type][1],0).getClass() 232 actualTypes[i] = Array.newInstance(types[i].asTable(). 233 base_at(NATNumber.ONE).asJavaClassUnderSymbiosis().getWrappedClass(), 0).getClass(); 234 } else { 235 actualTypes[i] = types[i].asJavaClassUnderSymbiosis().getWrappedClass(); 236 } 237 } 238 Vector matchingMethods = new Vector(); 239 240 for (int i = 0; i < choices_.length; i++) { 241 if(matches(choices_[i].getParameterTypes(), actualTypes)) { 242 matchingMethods.add(choices_[i]); 243 } 244 } 245 246 Constructor[] matches = (Constructor[]) matchingMethods.toArray(new Constructor[matchingMethods.size()]); 247 248 if (matches.length > 0) { 249 return new JavaConstructor(class_, matches); 250 } else { 251 throw new XNotInstantiatable(class_); 252 } 253 } 254 255 /** 256 * Compares two Class arrays and returns true iff both arrays have equal size and all members are the same. 257 */ 258 private static final boolean matches(Class[] formals, Class[] actuals) { 259 if (formals.length != actuals.length) 260 return false; 261 262 for (int i = 0; i < formals.length; i++) { 263 if (!(formals[i] == actuals[i])) { 264 return false; 265 } 266 } 267 268 return true; 269 } 270}