package japicmp.model; import com.google.common.base.Optional; import javassist.bytecode.annotation.Annotation; import javassist.bytecode.annotation.MemberValue; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlTransient; import java.util.*; public class JApiAnnotation implements JApiHasChangeStatus, JApiCompatibility { private final String fullyQualifiedName; private final Optional<Annotation> oldAnnotation; private final Optional<Annotation> newAnnotation; private final List<JApiAnnotationElement> elements = new LinkedList<>(); private JApiChangeStatus changeStatus; public JApiAnnotation(String fullyQualifiedName, Optional<Annotation> oldAnnotation, Optional<Annotation> newAnnotation, JApiChangeStatus changeStatus) { this.fullyQualifiedName = fullyQualifiedName; this.oldAnnotation = oldAnnotation; this.newAnnotation = newAnnotation; computeElements(this.elements, oldAnnotation, newAnnotation); this.changeStatus = evaluateChangeStatus(changeStatus); } private void computeElements(List<JApiAnnotationElement> elements, Optional<Annotation> oldAnnotationOptional, Optional<Annotation> newAnnotationOptional) { if (oldAnnotationOptional.isPresent() && newAnnotationOptional.isPresent()) { Annotation oldAnnotation = oldAnnotationOptional.get(); Annotation newAnnotation = newAnnotationOptional.get(); Map<String, Optional<MemberValue>> oldMemberValueMap = buildMemberValueMap(oldAnnotation); Map<String, Optional<MemberValue>> newMemberValueMap = buildMemberValueMap(newAnnotation); for (String memberName : oldMemberValueMap.keySet()) { Optional<MemberValue> foundOptional = newMemberValueMap.get(memberName); if (foundOptional == null) { JApiAnnotationElement jApiAnnotationElement = new JApiAnnotationElement(memberName, oldMemberValueMap.get(memberName), Optional.<MemberValue>absent(), JApiChangeStatus.REMOVED); elements.add(jApiAnnotationElement); } else { JApiAnnotationElement jApiAnnotationElement = new JApiAnnotationElement(memberName, oldMemberValueMap.get(memberName), foundOptional, JApiChangeStatus.UNCHANGED); elements.add(jApiAnnotationElement); } } for (String memberName : newMemberValueMap.keySet()) { Optional<MemberValue> foundOptional = oldMemberValueMap.get(memberName); if (foundOptional == null) { JApiAnnotationElement jApiAnnotationElement = new JApiAnnotationElement(memberName, Optional.<MemberValue>absent(), newMemberValueMap.get(memberName), JApiChangeStatus.NEW); elements.add(jApiAnnotationElement); } } } else { if (oldAnnotationOptional.isPresent()) { Annotation oldAnnotation = oldAnnotationOptional.get(); Map<String, Optional<MemberValue>> oldMemberValueMap = buildMemberValueMap(oldAnnotation); for (String memberName : oldMemberValueMap.keySet()) { JApiAnnotationElement jApiAnnotationElement = new JApiAnnotationElement(memberName, oldMemberValueMap.get(memberName), Optional.<MemberValue>absent(), JApiChangeStatus.REMOVED); elements.add(jApiAnnotationElement); } } if (newAnnotationOptional.isPresent()) { Annotation newAnnotation = newAnnotationOptional.get(); Map<String, Optional<MemberValue>> newMemberValueMap = buildMemberValueMap(newAnnotation); for (String memberName : newMemberValueMap.keySet()) { JApiAnnotationElement jApiAnnotationElement = new JApiAnnotationElement(memberName, Optional.<MemberValue>absent(), newMemberValueMap.get(memberName), JApiChangeStatus.NEW); elements.add(jApiAnnotationElement); } } } } private Map<String, Optional<MemberValue>> buildMemberValueMap(Annotation annotation) { Map<String, Optional<MemberValue>> map = new HashMap<>(); @SuppressWarnings("unchecked") Set<String> memberNames = annotation.getMemberNames(); if (memberNames != null) { for (String memberName : memberNames) { MemberValue memberValue = annotation.getMemberValue(memberName); if (memberValue == null) { map.put(memberName, Optional.<MemberValue>absent()); } else { map.put(memberName, Optional.of(memberValue)); } } } return map; } private JApiChangeStatus evaluateChangeStatus(JApiChangeStatus changeStatus) { if (changeStatus == JApiChangeStatus.UNCHANGED) { for (JApiAnnotationElement annotationElement : elements) { if (annotationElement.getChangeStatus() != JApiChangeStatus.UNCHANGED) { changeStatus = JApiChangeStatus.MODIFIED; } } } return changeStatus; } @Override @XmlAttribute(name = "changeStatus") public JApiChangeStatus getChangeStatus() { return this.changeStatus; } @XmlAttribute(name = "fullyQualifiedName") public String getFullyQualifiedName() { return fullyQualifiedName; } @XmlTransient public Optional<Annotation> getOldAnnotation() { return oldAnnotation; } @XmlTransient public Optional<Annotation> getNewAnnotation() { return newAnnotation; } @XmlElementWrapper(name = "elements") @XmlElement(name = "element") public List<JApiAnnotationElement> getElements() { return elements; } @Override @XmlAttribute public boolean isBinaryCompatible() { return true; } @Override @XmlAttribute public boolean isSourceCompatible() { return true; } @XmlElementWrapper(name = "compatibilityChanges") @XmlElement(name = "compatibilityChange") public List<JApiCompatibilityChange> getCompatibilityChanges() { return Collections.EMPTY_LIST; } }