001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.make;
031:
032: import com.caucho.util.Crc64;
033: import com.caucho.vfs.PersistentDependency;
034:
035: import java.lang.reflect.Field;
036: import java.lang.reflect.Method;
037: import java.lang.reflect.Modifier;
038: import java.util.Arrays;
039: import java.util.Comparator;
040: import java.util.logging.Level;
041: import java.util.logging.Logger;
042:
043: /**
044: * Representing a class that might change.
045: */
046: public class ClassDependency implements PersistentDependency {
047: private final static Logger log = Logger
048: .getLogger(ClassDependency.class.getName());
049:
050: private Class _cl;
051:
052: private boolean _checkFields = true;
053: private boolean _checkStatic = true;
054: private boolean _checkProtected = true;
055: private boolean _checkPrivate = true;
056:
057: private boolean _isDigestModified;
058: private long _newDigest;
059:
060: /**
061: * Creates the class dependency.
062: */
063: public ClassDependency(Class cl) {
064: _cl = cl;
065: }
066:
067: /**
068: * Create a new dependency with a given digest.
069: *
070: * @param cl the source class
071: * @param digest the MD5 digest
072: */
073: public ClassDependency(Class cl, long digest) {
074: _cl = cl;
075:
076: long newDigest = getDigest();
077:
078: if (newDigest != digest) {
079: if (log.isLoggable(Level.FINE))
080: log.fine(_cl.getName() + " digest is modified (old="
081: + digest + ",new=" + newDigest + ")");
082:
083: _isDigestModified = true;
084: }
085: }
086:
087: /**
088: * Returns true if the underlying resource has changed.
089: */
090: public boolean isModified() {
091: return _isDigestModified;
092: }
093:
094: /**
095: * Returns true if the underlying resource has changed.
096: */
097: public boolean logModified(Logger log) {
098: if (isModified()) {
099: log.info(_cl.getName() + " digest is modified");
100: return true;
101: } else
102: return false;
103: }
104:
105: /**
106: * Calculates a MD5 digest of the class.
107: */
108: public long getDigest() {
109: try {
110: if (_newDigest != 0)
111: return _newDigest;
112:
113: if (_cl == null)
114: return -1;
115:
116: long digest = 37;
117:
118: digest = addDigest(digest, _cl);
119:
120: _newDigest = digest;
121: } catch (Throwable e) {
122: log.log(Level.FINER, e.toString(), e);
123:
124: _newDigest = -1;
125: }
126:
127: return _newDigest;
128: }
129:
130: /**
131: * Calculates a MD5 digest of the class.
132: */
133: private long addDigest(long digest, Class cl) throws Exception {
134: if (_cl == null)
135: return digest;
136:
137: digest = addDigest(digest, cl.getName());
138:
139: digest = addDigest(digest, cl.getModifiers());
140:
141: Class super Class = cl.getSuperclass();
142: if (super Class != null
143: && super Class.getName().startsWith("java.")
144: && !super Class.getName().startsWith("javax.")) {
145: digest = addDigest(digest, super Class);
146: }
147:
148: Class[] interfaces = cl.getInterfaces();
149: Arrays.sort(interfaces, ClassComparator.CMP);
150: for (int i = 0; i < interfaces.length; i++)
151: digest = addDigest(digest, interfaces[i].getName());
152:
153: if (_checkFields) {
154: Field[] fields = cl.getDeclaredFields();
155:
156: Arrays.sort(fields, FieldComparator.CMP);
157:
158: for (int i = 0; i < fields.length; i++) {
159: int modifiers = fields[i].getModifiers();
160:
161: if (Modifier.isPrivate(modifiers) && !_checkPrivate)
162: continue;
163: if (Modifier.isProtected(modifiers) && !_checkProtected)
164: continue;
165:
166: digest = addDigest(digest, fields[i].getName());
167: digest = addDigest(digest, fields[i].getModifiers());
168: digest = addDigest(digest, fields[i].getType()
169: .getName());
170: }
171: }
172:
173: Method[] methods = cl.getDeclaredMethods();
174: Arrays.sort(methods, MethodComparator.CMP);
175:
176: for (int i = 0; i < methods.length; i++) {
177: Method method = methods[i];
178: int modifiers = method.getModifiers();
179:
180: if (Modifier.isPrivate(modifiers) && !_checkPrivate)
181: continue;
182: if (Modifier.isProtected(modifiers) && !_checkProtected)
183: continue;
184: if (Modifier.isStatic(modifiers) && !_checkStatic)
185: continue;
186:
187: digest = addDigest(digest, method.getName());
188: digest = addDigest(digest, method.getModifiers());
189: digest = addDigest(digest, method.getName());
190:
191: Class[] param = method.getParameterTypes();
192: for (int j = 0; j < param.length; j++)
193: digest = addDigest(digest, param[j].getName());
194:
195: digest = addDigest(digest, method.getReturnType().getName());
196:
197: Class[] exn = method.getExceptionTypes();
198: Arrays.sort(exn, ClassComparator.CMP);
199: for (int j = 0; j < exn.length; j++)
200: digest = addDigest(digest, exn[j].getName());
201: }
202:
203: return digest;
204: }
205:
206: /**
207: * Returns a string which will recreate the dependency.
208: */
209: public String getJavaCreateString() {
210: return ("new com.caucho.make.ClassDependency("
211: + _cl.getName().replace('$', '.') + ".class, "
212: + getDigest() + "L)");
213: }
214:
215: /**
216: * Adds the int to the digest.
217: */
218: private static long addDigest(long digest, long v) {
219: digest = Crc64.generate(digest, (byte) (v >> 24));
220: digest = Crc64.generate(digest, (byte) (v >> 16));
221: digest = Crc64.generate(digest, (byte) (v >> 8));
222: digest = Crc64.generate(digest, (byte) v);
223:
224: return digest;
225: }
226:
227: /**
228: * Adds the string to the digest using a UTF8 encoding.
229: */
230: private static long addDigest(long digest, String string) {
231: return Crc64.generate(digest, string);
232: }
233:
234: public int hashCode() {
235: return _cl.hashCode();
236: }
237:
238: public boolean equals(Object o) {
239: if (o == this )
240: return true;
241:
242: if (!(o instanceof ClassDependency))
243: return false;
244:
245: ClassDependency depend = (ClassDependency) o;
246:
247: return _cl.equals(depend._cl);
248: }
249:
250: static class ClassComparator implements Comparator<Class> {
251: static final ClassComparator CMP = new ClassComparator();
252:
253: public int compare(Class a, Class b) {
254: if (a == b)
255: return 0;
256: else if (a == null)
257: return -1;
258: else if (b == null)
259: return 1;
260:
261: return a.getName().compareTo(b.getName());
262: }
263: }
264:
265: static class FieldComparator implements Comparator<Field> {
266: static final FieldComparator CMP = new FieldComparator();
267:
268: public int compare(Field a, Field b) {
269: if (a == b)
270: return 0;
271: else if (a == null)
272: return -1;
273: else if (b == null)
274: return 1;
275:
276: int cmp = a.getName().compareTo(b.getName());
277: if (cmp != 0)
278: return cmp;
279:
280: cmp = a.getDeclaringClass().getName().compareTo(
281: b.getDeclaringClass().getName());
282: if (cmp != 0)
283: return cmp;
284:
285: return a.getType().getName().compareTo(
286: b.getType().getName());
287: }
288: }
289:
290: static class MethodComparator implements Comparator<Method> {
291: static final MethodComparator CMP = new MethodComparator();
292:
293: public int compare(Method a, Method b) {
294: if (a == b)
295: return 0;
296: else if (a == null)
297: return -1;
298: else if (b == null)
299: return 1;
300:
301: int cmp = a.getName().compareTo(b.getName());
302: if (cmp != 0)
303: return cmp;
304:
305: Class[] paramA = a.getParameterTypes();
306: Class[] paramB = b.getParameterTypes();
307:
308: if (paramA.length < paramB.length)
309: return -1;
310: else if (paramB.length < paramA.length)
311: return 1;
312:
313: for (int i = 0; i < paramA.length; i++) {
314: cmp = paramA[i].getName()
315: .compareTo(paramB[i].getName());
316: if (cmp != 0)
317: return cmp;
318: }
319:
320: cmp = a.getDeclaringClass().getName().compareTo(
321: b.getDeclaringClass().getName());
322: if (cmp != 0)
323: return cmp;
324:
325: return a.getReturnType().getName().compareTo(
326: b.getReturnType().getName());
327: }
328: }
329: }
|