blob: 3b7a529e449dac1513ee68556d8754ce7fc22145 [file] [log] [blame]
/*
* 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.felix.ipojo.manipulator.metadata.annotation.model.literal;
import static java.lang.String.format;
import static org.objectweb.asm.Type.getType;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.felix.ipojo.manipulator.metadata.annotation.model.AnnotationDiscovery;
import org.apache.felix.ipojo.manipulator.metadata.annotation.model.Playback;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
/**
* User: guillaume
* Date: 08/07/13
* Time: 17:15
*/
public class AnnotationPlayback implements Playback {
public static final List<? extends Class<? extends Serializable>> BOXED_TYPES = Arrays.asList(Byte.class, Long.class, Character.class, Boolean.class, Double.class, Float.class, Integer.class, Short.class);
private final Annotation m_annotation;
private final Type m_annotationType;
public AnnotationPlayback(final Annotation annotation) {
m_annotation = annotation;
m_annotationType = Type.getType(annotation.annotationType());
}
private Map<String, Object> getValues() {
Map<String, Object> values = new HashMap<String, Object>();
for (Method method : m_annotation.annotationType().getDeclaredMethods()) {
try {
values.put(method.getName(), method.invoke(m_annotation));
} catch (Throwable t) {
throw new IllegalStateException(
format("Cannot get value of the %s.%s attribute",
m_annotation.annotationType().getSimpleName(),
method.getName()),
t
);
}
}
return values;
}
public void accept(final FieldVisitor visitor) {
AnnotationVisitor av = visitor.visitAnnotation(m_annotationType.getDescriptor(),
true);
if (av != null) {
accept(av);
}
}
public void accept(final ClassVisitor visitor) {
AnnotationVisitor av = visitor.visitAnnotation(m_annotationType.getDescriptor(),
true);
if (av != null) {
accept(av);
}
}
public void accept(final MethodVisitor visitor) {
AnnotationVisitor av = visitor.visitAnnotation(m_annotationType.getDescriptor(),
true);
if (av != null) {
accept(av);
}
}
public void accept(final MethodVisitor visitor, final int index) {
AnnotationVisitor av = visitor.visitParameterAnnotation(index,
m_annotationType.getDescriptor(),
true);
if (av != null) {
accept(av);
}
}
public void accept(final AnnotationDiscovery visitor) {
AnnotationVisitor av = visitor.visitAnnotation(m_annotationType.getDescriptor());
if (av != null) {
accept(av);
}
}
private void accept(final AnnotationVisitor visitor) {
// As per the ASM doc, visit methods must be called in a given order:
// 1. visit()
// 2. visitEnum()
// 3. visitAnnotation()
// 4. visitArray()
// So values must be sorted
Map<String, Object> values = getValues();
accept(values, visitor);
acceptEnum(values, visitor);
acceptAnnotation(values, visitor);
acceptArray(values, visitor);
// Do not forget to visitEnd()
visitor.visitEnd();
// TODO This should disappear, only useful for testing
if (!values.isEmpty()) {
// We missed something during serialization
throw new IllegalStateException(
format("Attributes of @%s could not be serialized: %s",
m_annotation.annotationType().getSimpleName(),
values.keySet())
);
}
}
private void acceptAnnotation(final Map<String, Object> values, final AnnotationVisitor visitor) {
Map<String, Object> copy = new HashMap<String, Object>(values);
for (Map.Entry<String, Object> entry : copy.entrySet()) {
Class<?> type = entry.getValue().getClass();
if (Annotation.class.isAssignableFrom(type)) {
Annotation annotation = (Annotation) entry.getValue();
AnnotationVisitor annotationVisitor = visitor.visitAnnotation(entry.getKey(),
getType(annotation.annotationType()).getDescriptor());
if (annotationVisitor != null) {
AnnotationPlayback playback = new AnnotationPlayback(annotation);
playback.accept(annotationVisitor);
}
values.remove(entry.getKey());
}
}
}
private void acceptEnum(final Map<String, Object> values, final AnnotationVisitor visitor) {
Map<String, Object> copy = new HashMap<String, Object>(values);
for (Map.Entry<String, Object> entry : copy.entrySet()) {
Class<?> type = entry.getValue().getClass();
if (type.isEnum()) {
Enum<?> enumValue = (Enum<?>) entry.getValue();
visitor.visitEnum(entry.getKey(),
getType(type).getDescriptor(),
enumValue.name());
values.remove(entry.getKey());
}
}
}
private void accept(final Map<String, Object> values, final AnnotationVisitor visitor) {
Map<String, Object> copy = new HashMap<String, Object>(values);
for (Map.Entry<String, Object> entry : copy.entrySet()) {
Class<?> type = entry.getValue().getClass();
if (isSimpleType(type)) {
// Accept Byte, Boolean, Character, Short, Integer, Long, Float, Double
// Accept String
// Accept Array of byte, boolean, char, short, int, long, float, double
visitor.visit(entry.getKey(), transform(entry.getValue()));
values.remove(entry.getKey());
}
}
}
private boolean isSimpleType(final Class<?> type) {
return isPrimitive(type) ||
String.class.equals(type) ||
Class.class.equals(type) ||
(type.isArray() && isPrimitive(type.getComponentType()));
}
private boolean isPrimitive(final Class<?> type) {
if (type.isPrimitive()) {
return true;
}
if (BOXED_TYPES.contains(type)) {
return true;
}
return false;
}
private void acceptArray(final Map<String, Object> values, final AnnotationVisitor visitor) {
Map<String, Object> copy = new HashMap<String, Object>(values);
for (Map.Entry<String, Object> entry : copy.entrySet()) {
Class<?> type = entry.getValue().getClass();
if (type.isArray()) {
// Simple arrays have been visited using AnnotationVisitor.visit(String, Object)
AnnotationVisitor arrayVisitor = visitor.visitArray(entry.getKey());
if (arrayVisitor != null) {
Object[] array = (Object[]) entry.getValue();
Class<?> componentType = array.getClass().getComponentType();
Type asmType = Type.getType(componentType);
if (componentType.isEnum()) {
for (Object o : array) {
Enum eValue = (Enum) o;
arrayVisitor.visitEnum(null, asmType.getDescriptor(), eValue.name());
}
} else if (componentType.isAnnotation()) {
for (Object o : array) {
Annotation annotation = (Annotation) o;
AnnotationVisitor annotationVisitor = arrayVisitor.visitAnnotation(null, asmType.getDescriptor());
if (annotationVisitor != null) {
AnnotationPlayback playback = new AnnotationPlayback(annotation);
playback.accept(annotationVisitor);
}
}
} else {
for (Object o : array) {
arrayVisitor.visit(null, transform(o));
}
}
arrayVisitor.visitEnd();
}
values.remove(entry.getKey());
}
}
}
private Object transform(final Object value) {
if (value instanceof Class) {
return getType((Class) value);
}
return value;
}
}