001// Copyright 2007, 2008, 2011, 2012 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007// http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.tapestry5.javadoc;
016
017import java.io.IOException;
018import java.util.Locale;
019import java.util.Set;
020import java.util.regex.Matcher;
021import java.util.regex.Pattern;
022
023import org.apache.commons.lang.StringEscapeUtils;
024import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
025
026import com.sun.javadoc.FieldDoc;
027import com.sun.javadoc.SeeTag;
028import com.sun.javadoc.Tag;
029
030public class ParameterDescription
031{
032    public final FieldDoc field;
033
034    public final String name;
035
036    public final String type;
037
038    public final String defaultValue;
039
040    public final String defaultPrefix;
041
042    public final boolean required;
043
044    public final boolean allowNull;
045
046    public final boolean cache;
047
048    public final String since;
049
050    public final boolean deprecated;
051
052    private static final Pattern SPECIAL_CONTENT = Pattern.compile("(?:</?(\\p{Alpha}+)>)|(?:&\\p{Alpha}+;)");
053    private static final Set<String> PASS_THROUGH_TAGS = CollectionFactory.newSet("b", "em", "i", "code", "strong");
054
055
056    public ParameterDescription(final FieldDoc fieldDoc, final String name, final String type, final String defaultValue, final String defaultPrefix,
057            final boolean required, final boolean allowNull, final boolean cache, final String since, final boolean deprecated)
058    {
059        this.field = fieldDoc;
060        this.name = name;
061        this.type = type;
062        this.defaultValue = defaultValue;
063        this.defaultPrefix = defaultPrefix;
064        this.required = required;
065        this.allowNull = allowNull;
066        this.cache = cache;
067        this.since = since;
068        this.deprecated = deprecated;
069    }
070
071    /**
072     * Extracts the description, converting Text and @link nodes as needed into markup text.
073     *
074     * @return markup text, ready for writing
075     * @throws IOException
076     */
077    public String extractDescription() throws IOException
078    {
079        StringBuilder builder = new StringBuilder();
080
081        for (Tag tag : field.inlineTags())
082        {
083            if (tag.name().equals("Text"))
084            {
085                appendContentSafe(builder, tag.text());
086                continue;
087            }
088
089            if (tag.name().equals("@link"))
090            {
091                SeeTag seeTag = (SeeTag) tag;
092
093                String label = seeTag.label();
094                if (label != null && !label.equals(""))
095                {
096                    builder.append(StringEscapeUtils.escapeHtml(label));
097                    continue;
098                }
099
100                if (seeTag.referencedClassName() != null)
101                    builder.append(StringEscapeUtils.escapeHtml(seeTag.referencedClassName()));
102
103                if (seeTag.referencedMemberName() != null)
104                {
105                    builder.append('#');
106                    builder.append(StringEscapeUtils.escapeHtml(seeTag.referencedMemberName()));
107                }
108            }
109            else if (tag.name().equals("@code"))
110            {
111                builder.append("<code>");
112                builder.append(StringEscapeUtils.escapeHtml(tag.text()));
113                builder.append("</code>");
114            }
115        }
116
117        String text = builder.toString();
118
119        // Fix it up a little.
120
121        // Remove any simple open or close tags found in the text, as well as any XML entities.
122
123        return text.trim();
124    }
125
126    private static void appendContentSafe(final StringBuilder sb, final String string){
127        Matcher m = SPECIAL_CONTENT.matcher(string);
128        int index = 0;
129        while (index < string.length()){
130            boolean match = m.find(index);
131            if (match){
132                if (index != m.start()){
133                    sb.append(StringEscapeUtils.escapeHtml(string.substring(index, m.start())));
134                }
135                String tagName = m.group(1);
136                if (tagName!= null){
137                    if(PASS_THROUGH_TAGS.contains(tagName.toLowerCase(Locale.US))){
138                        sb.append(m.group());
139                    }
140                }else{
141                    sb.append(m.group());
142                }
143                index = m.end();
144            }else{
145                sb.append(StringEscapeUtils.escapeHtml(string.substring(index)));
146                break;
147            }
148        }
149    }
150}