org.zkoss.zk.ui.metainfo.AnnotationMap Maven / Gradle / Ivy
/* AnnotationMap.java
{{IS_NOTE
Purpose:
Description:
History:
Mon Dec 4 16:09:53 2006, Created by tomyeh
}}IS_NOTE
Copyright (C) 2006 Potix Corporation. All Rights Reserved.
{{IS_RIGHT
}}IS_RIGHT
*/
package org.zkoss.zk.ui.metainfo;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.zkoss.lang.Objects;
import org.zkoss.util.ArraysX;
import org.zkoss.util.resource.Location;
/**
* A map of annotations used with {@link ComponentDefinition} and
* {@link ComponentInfo}.
*
* Note: it is not thread safe.
*
* @author tomyeh
*/
public class AnnotationMap implements Cloneable, java.io.Serializable {
/** The annotations of properties,
* (String propName, Map(String annotName, Annotation)).
*/
private Map>> _annots;
/** Returns whether no annotation at all.
*/
public boolean isEmpty() {
return _annots == null || _annots.isEmpty();
}
/** Returns the annotation associated with the specified
* property, or null if not available.
*
* @param annotName the annotation name
* @param propName the property name, e.g., "value".
* If null, this method returns the annotation(s) associated with the
* component (rather than a particular property).
*/
public Annotation getAnnotation(String propName, String annotName) {
if (_annots != null) {
final Map> anmap = _annots.get(propName);
if (anmap != null) {
List ans = anmap.get(annotName);
if (ans != null) {
if (ans.size() == 1)
return ans.get(0);
//merge annotations into a single annotation
final AnnotImpl ai = new AnnotImpl(annotName, null);
for (Annotation an : ans) {
if (ai._loc == null)
ai._loc = an.getLocation();
ai.addAttributes(an);
}
return ai;
}
}
}
return null;
}
/** Returns the annotations associated with the specified
* property. It never returns null.
*
* @param annotName the annotation name
* @param propName the property name, e.g., "value".
* If null, this method returns the annotation(s) associated with the
* component (rather than a particular property).
* @since 6.0.0
*/
public Collection getAnnotations(String propName, String annotName) {
if (_annots != null) {
final Map> anmap = _annots.get(propName);
if (anmap != null) {
List ans = anmap.get(annotName);
if (ans != null)
return ans;
}
}
return Collections.emptyList();
}
/** Returns a read-only collection of all annotations associated with the
* the specified property.
*
* @param propName the property name, e.g., "value".
* If null, this method returns the annotation(s) associated with the
* component (rather than a particular property).
*/
public Collection getAnnotations(String propName) {
if (_annots != null) {
final Map> anmap = _annots.get(propName);
if (anmap != null) {
final List dst = new LinkedList();
for (List ans : anmap.values())
dst.addAll(ans);
return dst;
}
}
return Collections.emptyList();
}
/** Returns a read-only list of the names (String) of the properties
* that are associated with the specified annotation (never null).
*/
public List getAnnotatedPropertiesBy(String annotName) {
if (_annots != null) {
final List list = new LinkedList();
for (Map.Entry>> me : _annots.entrySet()) {
final String propName = me.getKey();
if (propName != null) {
final Map> anmap = me.getValue();
if (anmap.containsKey(annotName))
list.add(propName);
}
}
return list;
}
return Collections.emptyList();
}
/** Returns a read-only list of the name (String) of properties that
* are associated at least one annotation (never null).
*/
public List getAnnotatedProperties() {
final List list = new LinkedList();
if (_annots != null) {
for (String propName : _annots.keySet()) {
if (propName != null)
list.add(propName);
}
}
return list;
}
//Modification API//
/** Adds all annotations of the specified map to this map.
*/
public void addAll(AnnotationMap src) {
if (src != null && !src.isEmpty()) {
initAnnots();
for (Map.Entry>> me : src._annots.entrySet()) {
final String propName = me.getKey(); //may be null
Map> anmap = _annots.get(propName);
if (anmap == null)
_annots.put(propName, anmap = new LinkedHashMap>(4));
addAllAns(anmap, me.getValue());
}
}
}
/** Adds the annotations from one source to another.
* @param anmap the destination
* @param srcanmap the source
*/
private static void addAllAns(Map> anmap, Map> srcanmap) {
for (Map.Entry> me : srcanmap.entrySet()) {
final String annotName = me.getKey();
List ans = anmap.get(annotName);
if (ans == null)
anmap.put(annotName, ans = new LinkedList());
ans.addAll(me.getValue());
}
}
/** Adds an annotation.
*
* @param propName the property name.
* If null, this method returns the annotation(s) associated with the
* component (rather than a particular property).
* @since 6.0.0
*/
public void addAnnotation(String propName, String annotName, Map annotAttrs, Location loc) {
initAnnots();
Map> anmap = _annots.get(propName);
if (anmap == null)
_annots.put(propName, anmap = new LinkedHashMap>(4));
List ans = anmap.get(annotName);
if (ans == null)
anmap.put(annotName, ans = new LinkedList());
ans.add(new AnnotImpl(annotName, annotAttrs, loc));
}
/** Initializes _annots by creating and assigning a new map for it.
*/
private void initAnnots() {
if (_annots == null)
_annots = new LinkedHashMap>>(4);
}
//Cloneable//
/** Clones this annotation map.
*/
public Object clone() {
final AnnotationMap clone;
try {
clone = (AnnotationMap) super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError();
}
if (_annots != null) {
clone._annots = new LinkedHashMap>>(_annots);
for (Map.Entry>> me : clone._annots.entrySet()) {
final Map> anmap = new LinkedHashMap>(4);
AnnotationMap.addAllAns(anmap, me.getValue());
me.setValue(anmap); //replace with the new one
}
}
return clone;
}
public String toString() {
return "[annot:" + _annots + ']';
}
/** An implementation of {@link Annotation}.
*/
private static class AnnotImpl implements Annotation {
private final String _name;
private Map _attrs;
private Location _loc;
private AnnotImpl(String name, Location loc) {
_name = name;
_loc = loc;
}
private AnnotImpl(String name, Map attrs, Location loc) {
_name = name;
_loc = loc;
addAttributes(attrs);
}
//Extra//
/** Adds the specified attribute to the annotation.
*
* @param name the attribute name. "value" is assumed if name is null or empty.
* @param value the attribute value. If null, "" is assumed (not removal).
*/
private void addAttribute(String name, String[] value) {
if (name == null || name.length() == 0)
name = "value";
if (value == null)
value = new String[0];
if (_attrs == null) {
_attrs = new LinkedHashMap(4);
} else {
final String[] oldval = _attrs.get(name);
if (oldval != null)
value = ArraysX.concat(oldval, value);
}
_attrs.put(name, value);
}
/** Adds a map of attributes, (String name, String value), to the annotation.
*/
private void addAttributes(Map attrs) {
if (attrs != null)
for (Map.Entry me : attrs.entrySet())
addAttribute(me.getKey(), me.getValue());
}
/** Adds the attributes of the given annotation to this annotation.
*/
private void addAttributes(Annotation an) {
if (an != null)
addAttributes(((AnnotImpl) an)._attrs);
}
public String getName() {
return _name;
}
public Map getAttributes() {
if (_attrs != null)
return _attrs;
return Collections.emptyMap();
}
public String getAttribute(String name) {
if (_attrs != null) {
String[] val = _attrs.get(name);
return val != null && val.length > 0 ? val[0] : null;
}
return null;
}
public String[] getAttributeValues(String name) {
return _attrs != null ? _attrs.get(name) : null;
}
public Location getLocation() {
return _loc;
}
public String toString() {
final StringBuffer sb = new StringBuffer().append('@').append(_name).append('(');
if (_attrs != null) {
boolean first = true;
for (Map.Entry me : _attrs.entrySet()) {
if (first)
first = false;
else
sb.append(", ");
sb.append(me.getKey()).append('=').append(Objects.toString(me.getValue()));
}
}
return sb.append(')').toString();
}
}
}