001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */
019: package org.apache.openjpa.lib.meta;
020:
021: import java.io.IOException;
022:
023: import org.apache.openjpa.lib.log.Log;
024: import org.apache.openjpa.lib.util.Localizer;
025:
026: import serp.bytecode.lowlevel.ConstantPoolTable;
027:
028: /**
029: * Filter that looks for classes with one of a set of annotations.
030: * See JDK 1.5 JVM spec for details on annotation bytecode:<br />
031: * java.sun.com/docs/books/vmspec/2nd-edition/ClassFileFormat-final-draft.pdf
032: *
033: * @author Abe White
034: * @nojavadoc
035: */
036: public class ClassAnnotationMetaDataFilter implements MetaDataFilter {
037:
038: private final String[] _annos;
039:
040: private static final Localizer _loc = Localizer
041: .forPackage(ClassAnnotationMetaDataFilter.class);
042: private Log _log = null;
043:
044: /**
045: * Constructor; supply annotation to match against.
046: */
047: public ClassAnnotationMetaDataFilter(Class anno) {
048: this (new Class[] { anno });
049: }
050:
051: /**
052: * Constructor; supply annotations to match against.
053: */
054: public ClassAnnotationMetaDataFilter(Class[] annos) {
055: _annos = new String[annos.length];
056: for (int i = 0; i < annos.length; i++)
057: _annos[i] = "L" + annos[i].getName().replace('.', '/')
058: + ";";
059: }
060:
061: public boolean matches(Resource rsrc) throws IOException {
062: if (_annos.length == 0 || !rsrc.getName().endsWith(".class"))
063: return false;
064:
065: try {
066: ConstantPoolTable table = new ConstantPoolTable(rsrc
067: .getContent());
068: int idx = table.getEndIndex();
069: idx += 6; // skip access, cls, super
070:
071: // skip interfaces
072: int interfaces = table.readUnsignedShort(idx);
073: idx += 2 + interfaces * 2;
074:
075: // skip fields and methods
076: int fields = table.readUnsignedShort(idx);
077: idx += 2;
078: for (int i = 0; i < fields; i++)
079: idx += skipFieldOrMethod(table, idx);
080: int methods = table.readUnsignedShort(idx);
081: idx += 2;
082: for (int i = 0; i < methods; i++)
083: idx += skipFieldOrMethod(table, idx);
084:
085: // look for annotation attrs
086: int attrs = table.readUnsignedShort(idx);
087: idx += 2;
088: int name;
089: for (int i = 0; i < attrs; i++) {
090: name = table.readUnsignedShort(idx);
091: idx += 2;
092: if ("RuntimeVisibleAnnotations".equals(table
093: .readString(table.get(name))))
094: return matchAnnotations(table, idx + 4);
095: idx += 4 + table.readInt(idx);
096: }
097: } catch (ArrayIndexOutOfBoundsException e) {
098: /*
099: * This ArrayIndexOutOfBoundsException indicates an incorrectly
100: * formed .class file. We will eat the exception, log a trace
101: * message (if a log exists), and return "false" to indicate there
102: * was no match.
103: */
104: Error cfe = new ClassFormatError(rsrc.getName());
105: cfe.initCause(e);
106: if (_log != null && _log.isTraceEnabled())
107: _log.trace(_loc.get("class-arg", rsrc.getName()), cfe);
108: }
109: return false;
110: }
111:
112: /**
113: * Return whether the given annotations match our candidates.
114: */
115: private boolean matchAnnotations(ConstantPoolTable table, int idx) {
116: int annos = table.readUnsignedShort(idx);
117: idx += 2;
118:
119: int type;
120: int props;
121: for (int i = 0; i < annos; i++) {
122: type = table.readUnsignedShort(idx);
123: idx += 2;
124: if (matchAnnotation(table.readString(table.get(type))))
125: return true;
126:
127: props = table.readUnsignedShort(idx);
128: idx += 2;
129: for (int j = 0; j < props; j++) {
130: idx += 2; // name
131: idx += skipAnnotationPropertyValue(table, idx);
132: }
133: }
134: return false;
135: }
136:
137: /**
138: * Return whether the given annotation matches our candidates.
139: */
140: private boolean matchAnnotation(String name) {
141: for (int i = 0; i < _annos.length; i++)
142: if (name.equals(_annos[i]))
143: return true;
144: return false;
145: }
146:
147: /**
148: * Skip an annotation property value, returning the number of bytes skipped.
149: */
150: private static int skipAnnotationPropertyValue(
151: ConstantPoolTable table, int idx) {
152: int skipped = 0;
153: switch (table.readByte(idx + skipped++)) {
154: case 'Z': // bool
155: case 'B': // byte
156: case 'C': // char
157: case 'D': // double
158: case 'F': // float
159: case 'I': // int
160: case 'J': // long
161: case 'S': // short
162: case 's': // string
163: case 'c': // class
164: skipped += 2;
165: break;
166: case 'e': // enum ptr
167: skipped += 4;
168: break;
169: case '[': // array
170: int size = table.readUnsignedShort(idx + skipped);
171: skipped += 2;
172: for (int i = 0; i < size; i++)
173: skipped += skipAnnotationPropertyValue(table, idx
174: + skipped);
175: break;
176: case '@': // anno
177: skipped += 2; // type
178: int props = table.readUnsignedShort(idx + skipped);
179: skipped += 2;
180: for (int j = 0; j < props; j++) {
181: skipped += 2; // name
182: skipped += skipAnnotationPropertyValue(table, idx
183: + skipped);
184: }
185: break;
186: }
187: return skipped;
188: }
189:
190: /**
191: * Skip the current field or method, returning the number of bytes skipped.
192: */
193: private static int skipFieldOrMethod(ConstantPoolTable table,
194: int idx) {
195: int attrs = table.readUnsignedShort(idx + 6);
196: int skipped = 8;
197: int len;
198: for (int i = 0; i < attrs; i++) {
199: len = table.readInt(idx + skipped + 2);
200: skipped += 6 + len;
201: }
202: return skipped;
203: }
204:
205: public Log getLog() {
206: return _log;
207: }
208:
209: public void setLog(Log _log) {
210: this._log = _log;
211: }
212: }
|