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}