| // Copyright 2007, 2009, 2011 The Apache Software Foundation |
| // |
| // Licensed 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.tapestry5.javadoc; |
| |
| import java.util.Map; |
| |
| import org.apache.tapestry5.BindingConstants; |
| import org.apache.tapestry5.annotations.Component; |
| import org.apache.tapestry5.annotations.Events; |
| import org.apache.tapestry5.annotations.Parameter; |
| import org.apache.tapestry5.ioc.internal.util.CollectionFactory; |
| import org.apache.tapestry5.ioc.internal.util.InternalUtils; |
| |
| import com.sun.javadoc.AnnotationDesc; |
| import com.sun.javadoc.AnnotationDesc.ElementValuePair; |
| import com.sun.javadoc.AnnotationValue; |
| import com.sun.javadoc.ClassDoc; |
| import com.sun.javadoc.Doc; |
| import com.sun.javadoc.FieldDoc; |
| import com.sun.javadoc.ProgramElementDoc; |
| import com.sun.javadoc.Tag; |
| |
| public class ClassDescription |
| { |
| public final ClassDoc classDoc; |
| |
| public final Map<String, ParameterDescription> parameters = CollectionFactory.newCaseInsensitiveMap(); |
| |
| /** |
| * Case insensitive map, keyed on event name, value is optional description (often blank). |
| */ |
| public final Map<String, String> events = CollectionFactory.newCaseInsensitiveMap(); |
| |
| public ClassDescription() |
| { |
| this.classDoc = null; |
| } |
| |
| public ClassDescription(ClassDoc classDoc, ClassDescriptionSource source) |
| { |
| this.classDoc = classDoc; |
| |
| loadEvents(); |
| loadParameters(source); |
| |
| ClassDoc parentDoc = classDoc.superclass(); |
| |
| if (parentDoc != null) |
| { |
| ClassDescription parentDescription = source.getDescription(classDoc.superclass().qualifiedName()); |
| |
| mergeInto(events, parentDescription.events); |
| mergeInto(parameters, parentDescription.parameters); |
| } |
| } |
| |
| private void loadEvents() |
| { |
| AnnotationDesc eventsAnnotation = getAnnotation(classDoc, Events.class); |
| |
| if (eventsAnnotation == null) |
| return; |
| |
| // Events has only a single attribute: value(), so we know its the first element |
| // in the array. |
| |
| ElementValuePair pair = eventsAnnotation.elementValues()[0]; |
| |
| AnnotationValue annotationValue = pair.value(); |
| AnnotationValue[] values = (AnnotationValue[]) annotationValue.value(); |
| |
| for (AnnotationValue eventValue : values) |
| { |
| String event = (String) eventValue.value(); |
| int ws = event.indexOf(' '); |
| |
| String name = ws < 0 ? event : event.substring(0, ws); |
| String description = ws < 0 ? "" : event.substring(ws + 1).trim(); |
| |
| events.put(name, description); |
| } |
| } |
| |
| private static <K, V> void mergeInto(Map<K, V> target, Map<K, V> source) |
| { |
| for (K key : source.keySet()) |
| { |
| if (!target.containsKey(key)) |
| { |
| V value = source.get(key); |
| target.put(key, value); |
| } |
| } |
| } |
| |
| private void loadParameters(ClassDescriptionSource source) |
| { |
| for (FieldDoc fd : classDoc.fields(false)) |
| { |
| if (fd.isStatic()) |
| continue; |
| |
| if (!fd.isPrivate()) |
| continue; |
| |
| Map<String, String> values = getAnnotationValues(fd, Parameter.class); |
| |
| if (values != null) |
| { |
| String name = values.get("name"); |
| |
| if (name == null) |
| name = fd.name().replaceAll("^[$_]*", ""); |
| |
| ParameterDescription pd = new ParameterDescription(fd, name, fd.type().qualifiedTypeName(), get(values, |
| "value", ""), get(values, "defaultPrefix", BindingConstants.PROP), getBoolean(values, |
| "required", false), getBoolean(values, "allowNull", true), getBoolean(values, "cache", true), |
| getSinceTagValue(fd), isDeprecated(fd)); |
| |
| parameters.put(name, pd); |
| |
| continue; |
| } |
| |
| values = getAnnotationValues(fd, Component.class); |
| |
| if (values != null) |
| { |
| String names = get(values, "publishParameters", ""); |
| |
| if (InternalUtils.isBlank(names)) |
| continue; |
| |
| for (String name : names.split("\\s*,\\s*")) |
| { |
| ParameterDescription pd = getPublishedParameterDescription(source, fd, name); |
| parameters.put(name, pd); |
| } |
| |
| } |
| |
| } |
| } |
| |
| private ParameterDescription getPublishedParameterDescription(ClassDescriptionSource source, FieldDoc fd, |
| String name) |
| { |
| String currentClassName = fd.type().qualifiedTypeName(); |
| |
| while (true) |
| { |
| ClassDescription componentCD = source.getDescription(currentClassName); |
| |
| if (componentCD.classDoc == null) |
| throw new IllegalArgumentException(String.format("Published parameter '%s' from %s not found.", name, |
| fd.qualifiedName())); |
| |
| if (componentCD.parameters.containsKey(name)) { return componentCD.parameters.get(name); } |
| |
| currentClassName = componentCD.classDoc.superclass().typeName(); |
| } |
| } |
| |
| private static boolean isDeprecated(ProgramElementDoc doc) |
| { |
| return (getAnnotation(doc, Deprecated.class) != null) || (doc.tags("deprecated").length != 0); |
| } |
| |
| private static String getSinceTagValue(Doc doc) |
| { |
| return getTagValue(doc, "since"); |
| } |
| |
| private static String getTagValue(Doc doc, String tagName) |
| { |
| Tag[] tags = doc.tags(tagName); |
| |
| return 0 < tags.length ? tags[0].text() : ""; |
| } |
| |
| private static boolean getBoolean(Map<String, String> map, String key, boolean defaultValue) |
| { |
| if (map.containsKey(key)) |
| return Boolean.parseBoolean(map.get(key)); |
| |
| return defaultValue; |
| } |
| |
| private static String get(Map<String, String> map, String key, String defaultValue) |
| { |
| if (map.containsKey(key)) |
| return map.get(key); |
| |
| return defaultValue; |
| } |
| |
| private static AnnotationDesc getAnnotation(ProgramElementDoc source, Class annotationType) |
| { |
| String name = annotationType.getName(); |
| |
| for (AnnotationDesc ad : source.annotations()) |
| { |
| if (ad.annotationType().qualifiedTypeName().equals(name)) { return ad; } |
| } |
| |
| return null; |
| } |
| |
| private static Map<String, String> getAnnotationValues(ProgramElementDoc source, Class annotationType) |
| { |
| AnnotationDesc annotation = getAnnotation(source, annotationType); |
| |
| if (annotation == null) |
| return null; |
| |
| Map<String, String> result = CollectionFactory.newMap(); |
| |
| for (ElementValuePair pair : annotation.elementValues()) |
| { |
| result.put(pair.element().name(), pair.value().value().toString()); |
| } |
| |
| return result; |
| } |
| } |