| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.openjpa.lib.meta; |
| |
| import java.io.IOException; |
| |
| import org.apache.openjpa.lib.log.Log; |
| import org.apache.openjpa.lib.util.Localizer; |
| |
| import serp.bytecode.lowlevel.ConstantPoolTable; |
| |
| /** |
| * Filter that looks for classes with one of a set of annotations. |
| * See JDK 1.5 JVM spec for details on annotation bytecode:<br /> |
| * java.sun.com/docs/books/vmspec/2nd-edition/ClassFileFormat-final-draft.pdf |
| * |
| * @author Abe White |
| */ |
| public class ClassAnnotationMetaDataFilter implements MetaDataFilter { |
| |
| private final String[] _annos; |
| |
| private static final Localizer _loc = Localizer.forPackage |
| (ClassAnnotationMetaDataFilter.class); |
| private Log _log = null; |
| |
| /** |
| * Constructor; supply annotation to match against. |
| */ |
| public ClassAnnotationMetaDataFilter(Class<?> anno) { |
| this(new Class[]{ anno }); |
| } |
| |
| /** |
| * Constructor; supply annotations to match against. |
| */ |
| public ClassAnnotationMetaDataFilter(Class<?>[] annos) { |
| _annos = new String[annos.length]; |
| for (int i = 0; i < annos.length; i++) |
| _annos[i] = "L" + annos[i].getName().replace('.', '/') + ";"; |
| } |
| |
| @Override |
| public boolean matches(Resource rsrc) throws IOException { |
| if (_annos.length == 0 || !rsrc.getName().endsWith(".class")) |
| return false; |
| |
| try { |
| ConstantPoolTable table = new ConstantPoolTable(rsrc.getContent()); |
| int idx = table.getEndIndex(); |
| idx += 6; // skip access, cls, super |
| |
| // skip interfaces |
| int interfaces = table.readUnsignedShort(idx); |
| idx += 2 + interfaces * 2; |
| |
| // skip fields and methods |
| int fields = table.readUnsignedShort(idx); |
| idx += 2; |
| for (int i = 0; i < fields; i++) |
| idx += skipFieldOrMethod(table, idx); |
| int methods = table.readUnsignedShort(idx); |
| idx += 2; |
| for (int i = 0; i < methods; i++) |
| idx += skipFieldOrMethod(table, idx); |
| |
| // look for annotation attrs |
| int attrs = table.readUnsignedShort(idx); |
| idx += 2; |
| int name; |
| for (int i = 0; i < attrs; i++) { |
| name = table.readUnsignedShort(idx); |
| idx += 2; |
| if ("RuntimeVisibleAnnotations".equals(table.readString |
| (table.get(name)))) |
| return matchAnnotations(table, idx + 4); |
| idx += 4 + table.readInt(idx); |
| } |
| } catch (ArrayIndexOutOfBoundsException e) { |
| /* |
| * This ArrayIndexOutOfBoundsException indicates an incorrectly |
| * formed .class file. We will eat the exception, log a trace |
| * message (if a log exists), and return "false" to indicate there |
| * was no match. |
| */ |
| Error cfe = new ClassFormatError(rsrc.getName()); |
| cfe.initCause(e); |
| if (_log != null && _log.isTraceEnabled()) |
| _log.trace(_loc.get("class-arg", rsrc.getName()), cfe); |
| } |
| return false; |
| } |
| |
| /** |
| * Return whether the given annotations match our candidates. |
| */ |
| private boolean matchAnnotations(ConstantPoolTable table, int idx) { |
| int annos = table.readUnsignedShort(idx); |
| idx += 2; |
| |
| int type; |
| int props; |
| for (int i = 0; i < annos; i++) { |
| type = table.readUnsignedShort(idx); |
| idx += 2; |
| if (matchAnnotation(table.readString(table.get(type)))) |
| return true; |
| |
| props = table.readUnsignedShort(idx); |
| idx += 2; |
| for (int j = 0; j < props; j++) { |
| idx += 2; // name |
| idx += skipAnnotationPropertyValue(table, idx); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Return whether the given annotation matches our candidates. |
| */ |
| private boolean matchAnnotation(String name) { |
| for (String anno : _annos) |
| if (name.equals(anno)) |
| return true; |
| return false; |
| } |
| |
| /** |
| * Skip an annotation property value, returning the number of bytes skipped. |
| */ |
| private static int skipAnnotationPropertyValue(ConstantPoolTable table, |
| int idx) { |
| int skipped = 0; |
| switch (table.readByte(idx + skipped++)) { |
| case 'Z': // bool |
| case 'B': // byte |
| case 'C': // char |
| case 'D': // double |
| case 'F': // float |
| case 'I': // int |
| case 'J': // long |
| case 'S': // short |
| case 's': // string |
| case 'c': // class |
| skipped += 2; |
| break; |
| case 'e': // enum ptr |
| skipped += 4; |
| break; |
| case '[': // array |
| int size = table.readUnsignedShort(idx + skipped); |
| skipped += 2; |
| for (int i = 0; i < size; i++) |
| skipped += |
| skipAnnotationPropertyValue(table, idx + skipped); |
| break; |
| case '@': // anno |
| skipped += 2; // type |
| int props = table.readUnsignedShort(idx + skipped); |
| skipped += 2; |
| for (int j = 0; j < props; j++) { |
| skipped += 2; // name |
| skipped += |
| skipAnnotationPropertyValue(table, idx + skipped); |
| } |
| break; |
| } |
| return skipped; |
| } |
| |
| /** |
| * Skip the current field or method, returning the number of bytes skipped. |
| */ |
| private static int skipFieldOrMethod(ConstantPoolTable table, int idx) { |
| int attrs = table.readUnsignedShort(idx + 6); |
| int skipped = 8; |
| int len; |
| for (int i = 0; i < attrs; i++) { |
| len = table.readInt(idx + skipped + 2); |
| skipped += 6 + len; |
| } |
| return skipped; |
| } |
| |
| public Log getLog() { |
| return _log; |
| } |
| |
| public void setLog(Log _log) { |
| this._log = _log; |
| } |
| } |