*** empty log message ***
[IRC.git] / Robust / Transactions / dstm2src / factory / BaseFactory.java
1 /*
2  * BaseFactory.java
3  *
4  * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle, Santa
5  * Clara, California 95054, U.S.A.  All rights reserved.  
6  * 
7  * Sun Microsystems, Inc. has intellectual property rights relating to
8  * technology embodied in the product that is described in this
9  * document.  In particular, and without limitation, these
10  * intellectual property rights may include one or more of the
11  * U.S. patents listed at http://www.sun.com/patents and one or more
12  * additional patents or pending patent applications in the U.S. and
13  * in other countries.
14  * 
15  * U.S. Government Rights - Commercial software.
16  * Government users are subject to the Sun Microsystems, Inc. standard
17  * license agreement and applicable provisions of the FAR and its
18  * supplements.  Use is subject to license terms.  Sun, Sun
19  * Microsystems, the Sun logo and Java are trademarks or registered
20  * trademarks of Sun Microsystems, Inc. in the U.S. and other
21  * countries.  
22  * 
23  * This product is covered and controlled by U.S. Export Control laws
24  * and may be subject to the export or import laws in other countries.
25  * Nuclear, missile, chemical biological weapons or nuclear maritime
26  * end uses or end users, whether direct or indirect, are strictly
27  * prohibited.  Export or reexport to countries subject to
28  * U.S. embargo or to entities identified on U.S. export exclusion
29  * lists, including, but not limited to, the denied persons and
30  * specially designated nationals lists is strictly prohibited.
31  */
32
33 package dstm2.factory;
34
35 import dstm2.atomic;
36 import java.util.Map;
37 import java.util.Map.Entry;
38 import java.util.HashMap;
39 import java.util.Set;
40 import java.util.HashSet;
41 import java.io.IOException;
42 import java.io.ByteArrayOutputStream;
43
44 import dstm2.Transaction;
45 import TransactionalIO.exceptions.PanicException;
46
47 import org.apache.bcel.classfile.*;
48 import org.apache.bcel.generic.*;
49 import org.apache.bcel.util.*;
50 import org.apache.bcel.Constants;
51 import org.apache.bcel.Repository;
52 import org.apache.bcel.classfile.Method;
53
54 /**
55  * Provides utility methods for factories.
56  * @author Maurice Herlihy
57  */
58 public abstract class BaseFactory<T> implements Factory<T> {
59   /**
60    * BCEL is not thread-safe
61    **/
62   protected static Object lock = new Object();
63   /**
64    * BCEL instruction factory.
65    */
66   protected InstructionFactory _factory;
67   /**
68    * Constant pool for created class.
69    */
70   protected ConstantPoolGen    _cp;
71   /**
72    * Used to generate the class.
73    */
74   protected ClassGen           _cg;
75   /**
76    * The actual class we are building.
77    */
78   protected Class<T> theClass;
79   /**
80    * The name of the class we are building.
81    */
82   protected String className;
83   /**
84    * Interface exported by created class.
85    */
86   protected String interfaceName;
87   
88   /**
89    * Set of properties.
90    **/
91   protected Set<Property> properties = new HashSet<Property>();
92   /**
93    * Set of other methods.
94    **/
95   protected Set<Method> methods = new HashSet<Method>();
96   
97   /**
98    * set of interfaces satisfied by this class
99    */
100   protected Set<Class> interfaces = new HashSet<Class>();
101   
102   /**
103    * This constructor separates methods into properties and miscellaneous
104    * methods. For properties, it deduces type and name of properties, and
105    * stores them in the <CODE>properties</CODE> set. Other methods go into
106    * the <CODE>methods</CODE> field.
107    * @param interfaces additional interfaces satisfied by this class and understood by this factory.
108    * @param _class interface class
109    */
110   public BaseFactory(Class<T> _class) {
111     if (!_class.isInterface()) {
112       throw new PanicException("%s is not an interface", _class);
113     }
114     parseProperties(_class);
115   }
116   
117   /**
118    * Once methods and fields have been defined, this method turns the
119    * byte code into a Java class.
120    */
121   protected void seal() {
122     ByteArrayOutputStream out = new ByteArrayOutputStream();
123     try {
124       _cg.getJavaClass().dump(out);
125     } catch (IOException ex) {
126       throw new PanicException(ex);
127     }
128     byte[] bytes = out.toByteArray();
129     ClassLoader<T> classLoader = new ClassLoader<T>(bytes);
130     theClass = classLoader.make();
131   }
132   
133   /**
134    * Create a new field.
135    * @param type BCEL type of field.
136    * @param name field name
137    */
138   protected void createField(Type type, String name) {
139     FieldGen field = new FieldGen(0, type, name, _cp);
140     _cg.addField(field.getField());
141   }
142   
143   /**
144    * Extracts properties from interface definition. Does rudimentary type
145    * and sanity checking
146    * @param _class class to inspect.
147    */
148   private void parseProperties(Class _class) {
149     // Make sure implied field types are scalar or atomic.
150     // Enough to check return type of getField() methods.
151     for (java.lang.reflect.Method method : _class.getMethods()) {
152       if (method.getName().substring(0,3).equals("get")) {
153         if (!isAtomicOrScalar(method.getReturnType())) {
154           throw new PanicException("Method %s return type %s not scalar or atomic.",
155               method.getName(), method.getReturnType().getName());
156         }
157       }
158     }
159     JavaClass jc = null;
160     try {
161       jc = Repository.lookupClass(_class);
162     } catch (java.lang.ClassNotFoundException ex) {
163       throw new PanicException("Class not found: " + _class);
164     }
165     properties = new HashSet<Property>();
166     methods = new HashSet<Method>();
167     Map<String,Property> fieldMap = new HashMap<String,Property>();
168     for (Method m : jc.getMethods()) {
169       String methodName = m.getName();
170       Class returnClass = null; // need both class & type
171       try {
172         returnClass = _class.getMethod(methodName).getReturnType();
173       } catch (NoSuchMethodException e) {};
174       Type[] parameterTypes = m.getArgumentTypes();
175       // for get and set methods
176       String fieldName = unCapitalize(methodName.substring(3));
177       // for boolean is method
178       String fieldNameBool = unCapitalize(methodName.substring(2));
179       // get method?
180       if (methodName.substring(0,3).equals("get")) {
181         Property record = fieldMap.get(fieldName);
182         if (parameterTypes.length != 0) {
183           throw new PanicException("Class %s: method %s\n has bad signature\n",
184               jc.getClassName(), methodName);
185         }
186         Type returnType = m.getReturnType();
187         // first time?
188         if (record == null) {
189           Property newRecord = new Property(returnClass, returnType, fieldName);
190           properties.add(newRecord);
191           newRecord.getMethod = m;
192           fieldMap.put(fieldName, newRecord);
193         } else {  // not first time
194           if (!record.type.equals(returnType)) {
195             throw new PanicException("Class %s: inconsistent definitions for field type %s: %s vs %s\n",
196                 jc.getClassName(), fieldName, record.type, returnType);
197           } else if (record.getMethod != null) {
198             throw new PanicException("Class %s: multiple definitions for %s\n",
199                 jc.getClassName(), methodName);
200           }
201           record.getMethod = m;
202         }
203         // is method
204       } else if (methodName.substring(0,2).equals("is")) {
205         Property record = fieldMap.get(fieldNameBool);
206         if (parameterTypes.length != 0) {
207           throw new PanicException("Class %s: method %s\n has bad signature\n",
208               jc.getClassName(), methodName);
209         }
210         Type returnType = m.getReturnType();
211         if (!returnType.equals(Type.BOOLEAN)) {
212           throw new PanicException("Class %s: method %s\n must have Boolean argument\n",
213               jc.getClassName(), methodName);
214         }
215         // first time?
216         if (record == null) {
217           Property newRecord = new Property(returnClass, returnType, fieldNameBool);
218           properties.add(newRecord);
219           newRecord.getMethod = m;
220           fieldMap.put(fieldNameBool, newRecord);
221         } else {  // not first time
222           if (!record.type.equals(returnType)) {
223             throw new PanicException("Class %s: inconsistent definitions for field type %s: %s vs %s\n",
224                 jc.getClassName(), fieldNameBool, record.type, returnType);
225           } else if (record.getMethod != null) {
226             throw new PanicException("Class %s: multiple definitions for %s\n",
227                 jc.getClassName(), methodName);
228           }
229           record.getMethod = m;
230         }
231       } else if (methodName.substring(0,3).equals("set")) {
232         Property record = fieldMap.get(fieldName);
233         if (parameterTypes.length != 1) {
234           throw new PanicException("Class %s: method %s has bad signature\n",
235               jc.getClassName(), methodName);
236         }
237         Type argType = parameterTypes[0];
238         // first time?
239         if (record == null) {
240           Property newRecord = new Property(returnClass, argType, fieldName);
241           properties.add(newRecord);
242           newRecord.setMethod = m;
243           fieldMap.put(fieldName, newRecord);
244         } else {  // not first time
245           if (!record.type.equals(argType)) {
246             throw new PanicException("Class %s: inconsistent definitions for field arg type %s: %s vs %s\n",
247                 jc.getClassName(), fieldName, record.type, argType);
248           } else if (record.setMethod != null) {
249             throw new PanicException("Class %s: multiple definitions for %s\n",
250                 jc.getClassName(), methodName);
251           }
252           record.setMethod = m;
253         }
254       } else {
255         throw new PanicException("%s: not a get/set method\n", m.getName());
256       }
257     }
258     for (Entry<String, Property> entry : fieldMap.entrySet()) {
259       if (entry.getValue().getMethod == null) {
260         throw new PanicException("Class %s: method get%s not defined\n",
261             jc.getClassName(), entry.getKey());
262       } else if (entry.getValue().setMethod == null) {
263         throw new PanicException("Class %s: method set%s not defined\n",
264             jc.getClassName(), entry.getKey());
265       }
266     }
267   }
268   
269   /**
270    * Did caller declare a <CODE>void release()</CODE> method?
271    * @return whether to provide a release() implementation.
272    */
273   protected boolean releaseDefined() {
274     for (Method m : methods) {
275       if (m.getName().equals("release") &&
276           m.getReturnType() == Type.NULL &&
277           m.getArgumentTypes().length == 0) {
278         return true;
279       }
280     }
281     return false;
282   }
283   
284   /**
285    * Checks whether a field type is scalar or declared atomic.
286    * @param _class class to check
287    * @return whether field is legit type for an atomic object
288    */
289   public boolean isAtomicOrScalar(Class _class) {
290     return _class.isPrimitive() ||
291         _class.isEnum() ||
292         _class.isAnnotationPresent(atomic.class) ||
293         _class.equals(Boolean.class) ||
294         _class.equals(Character.class) ||
295         _class.equals(Byte.class) ||
296         _class.equals(Short.class) ||
297         _class.equals(Integer.class) ||
298         _class.equals(Long.class) ||
299         _class.equals(Float.class) ||
300         _class.equals(Double.class) ||
301         _class.equals(String.class);
302   }
303   
304   String unCapitalize(String s) {
305     StringBuffer sb = new StringBuffer(s);
306     sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));
307     return sb.toString();
308   }
309 }