    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 com.sun.source.doctree.*;
    018import org.apache.commons.lang.StringEscapeUtils;
    019import org.apache.commons.lang.StringUtils;
    020import org.apache.tapestry5.commons.util.CollectionFactory;
    021
    022import javax.lang.model.element.VariableElement;
    023import java.io.IOException;
    024import java.util.Locale;
    025import java.util.Set;
    026import java.util.regex.Matcher;
    027import java.util.regex.Pattern;
    028
    029public class ParameterDescription
    030{
    031    public final VariableElement field;
    032
    033    public final String name;
    034
    035    public final String type;
    036
    037    public final String defaultValue;
    038
    039    public final String defaultPrefix;
    040
    041    public final boolean required;
    042
    043    public final boolean allowNull;
    044
    045    public final boolean cache;
    046
    047    public final String since;
    048
    049    public final boolean deprecated;
    050
    051    private final DocCommentTreeProvider docCommentTreeProvider;
    052
    053    private static final Pattern SPECIAL_CONTENT = Pattern.compile("(?:</?(\\p{Alpha}+)>)|(?:&\\p{Alpha}+;)");
    054    private static final Set<String> PASS_THROUGH_TAGS = CollectionFactory.newSet("b", "em", "i", "code", "strong");
    055
    056
    057    public ParameterDescription(final VariableElement fieldDoc, final String name, final String type, final String defaultValue, final String defaultPrefix,
    058                                final boolean required, final boolean allowNull, final boolean cache, final String since, final boolean deprecated,
    059                                final DocCommentTreeProvider docCommentTreeProvider)
    060    {
    061        this.field = fieldDoc;
    062        this.name = name;
    063        this.type = type;
    064        this.defaultValue = defaultValue;
    065        this.defaultPrefix = defaultPrefix;
    066        this.required = required;
    067        this.allowNull = allowNull;
    068        this.cache = cache;
    069        this.since = since;
    070        this.deprecated = deprecated;
    071        this.docCommentTreeProvider = docCommentTreeProvider;
    072    }
    073
    074    /**
    075     * Extracts the description, converting Text and @link nodes as needed into markup text.
    076     *
    077     * @return markup text, ready for writing
    078     * @throws IOException if some error occurs.
    079     */
    080    public String extractDescription() throws IOException
    081    {
    082        final DocCommentTree tree = docCommentTreeProvider.getDocCommentTree(field);
    083
    084        if (tree == null)
    085        {
    086            return "";
    087        }
    088
    089        StringBuilder builder = new StringBuilder();
    090
    091        for (com.sun.source.doctree.DocTree tag : tree.getFullBody())
    092        {
    093            if (tag.getKind() == DocTree.Kind.TEXT)
    094            {
    095                TextTree textTree = (TextTree) tag;
    096                appendContentSafe(builder, textTree.getBody());
    097                continue;
    098            }
    099
    100            if (tag.getKind() == DocTree.Kind.LINK)
    101            {
    102                LinkTree seeTag = (LinkTree) tag;
    103                String label = seeTag.getLabel().toString();
    104                if (StringUtils.isNotEmpty(label))
    105                {
    106                    builder.append(StringEscapeUtils.escapeHtml(label));
    107                    continue;
    108                }
    109
    110                if (seeTag.getReference() != null)
    111                    builder.append(StringEscapeUtils.escapeHtml(seeTag.getReference().getSignature()));
    112            }
    113            else if (tag.getKind() == DocTree.Kind.CODE)
    114            {
    115                LiteralTree codeTag = (LiteralTree) tag;
    116                builder.append("<code>");
    117                builder.append(StringEscapeUtils.escapeHtml(codeTag.getBody().getBody()));
    118                builder.append("</code>");
    119            }
    120        }
    121
    122        String text = builder.toString();
    123
    124        // Fix it up a little.
    125
    126        // Remove any simple open or close tags found in the text, as well as any XML entities.
    127
    128        return text.trim();
    129    }
    130
    131    private static void appendContentSafe(final StringBuilder sb, final String string){
    132        Matcher m = SPECIAL_CONTENT.matcher(string);
    133        int index = 0;
    134        while (index < string.length()){
    135            boolean match = m.find(index);
    136            if (match){
    137                if (index != m.start()){
    138                    sb.append(StringEscapeUtils.escapeHtml(string.substring(index, m.start())));
    139                }
    140                String tagName = m.group(1);
    141                if (tagName!= null){
    142                    if(PASS_THROUGH_TAGS.contains(tagName.toLowerCase(Locale.US))){
    143                        sb.append(m.group());
    144                    }
    145                }else{
    146                    sb.append(m.group());
    147                }
    148                index = m.end();
    149            }else{
    150                sb.append(StringEscapeUtils.escapeHtml(string.substring(index)));
    151                break;
    152            }
    153        }
    154    }
    155}