    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}