001/* 002 * Copyright (c) 2003, 2022, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. Oracle designates this 008 * particular file as subject to the "Classpath" exception as provided 009 * by Oracle in the LICENSE file that accompanied this code. 010 * 011 * This code is distributed in the hope that it will be useful, but WITHOUT 012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 014 * version 2 for more details (a copy is included in the LICENSE file that 015 * accompanied this code). 016 * 017 * You should have received a copy of the GNU General Public License version 018 * 2 along with this work; if not, write to the Free Software Foundation, 019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 020 * 021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 022 * or visit www.oracle.com if you need additional information or have any 023 * questions. 024 */ 025 026package org.jdrupes.mdoclet.internal.doclets.toolkit.builders; 027 028import static org.jdrupes.mdoclet.internal.doclets.toolkit.util.VisibleMemberTable.Kind.PROPERTIES; 029 030import java.util.ArrayList; 031import java.util.List; 032import java.util.Objects; 033import java.util.stream.Collectors; 034import javax.lang.model.element.Element; 035import javax.lang.model.element.ExecutableElement; 036import javax.lang.model.element.TypeElement; 037 038import org.jdrupes.mdoclet.internal.doclets.toolkit.BaseOptions; 039import org.jdrupes.mdoclet.internal.doclets.toolkit.CommentUtils; 040import org.jdrupes.mdoclet.internal.doclets.toolkit.Content; 041import org.jdrupes.mdoclet.internal.doclets.toolkit.DocletException; 042import org.jdrupes.mdoclet.internal.doclets.toolkit.PropertyWriter; 043 044import com.sun.source.doctree.DocCommentTree; 045import com.sun.source.doctree.DocTree; 046 047/** 048 * Builds documentation for a property. 049 */ 050public class PropertyBuilder extends AbstractMemberBuilder { 051 052 /** 053 * The writer to output the property documentation. 054 */ 055 private final PropertyWriter writer; 056 057 /** 058 * The list of properties being documented. 059 */ 060 private final List<? extends Element> properties; 061 062 /** 063 * The index of the current property that is being documented at this point 064 * in time. 065 */ 066 private ExecutableElement currentProperty; 067 068 /** 069 * Construct a new PropertyBuilder. 070 * 071 * @param context the build context. 072 * @param typeElement the class whose members are being documented. 073 * @param writer the doclet specific writer. 074 */ 075 private PropertyBuilder(Context context, 076 TypeElement typeElement, 077 PropertyWriter writer) { 078 super(context, typeElement); 079 this.writer = Objects.requireNonNull(writer); 080 properties = getVisibleMembers(PROPERTIES); 081 } 082 083 /** 084 * Construct a new PropertyBuilder. 085 * 086 * @param context the build context. 087 * @param typeElement the class whose members are being documented. 088 * @param writer the doclet specific writer. 089 * @return the new PropertyBuilder 090 */ 091 public static PropertyBuilder getInstance(Context context, 092 TypeElement typeElement, 093 PropertyWriter writer) { 094 return new PropertyBuilder(context, typeElement, writer); 095 } 096 097 /** 098 * Returns whether or not there are members to document. 099 * 100 * @return whether or not there are members to document 101 */ 102 @Override 103 public boolean hasMembersToDocument() { 104 return !properties.isEmpty(); 105 } 106 107 @Override 108 public void build(Content target) throws DocletException { 109 buildPropertyDoc(target); 110 } 111 112 /** 113 * Build the property documentation. 114 * 115 * @param detailsList the content to which the documentation will be added 116 * @throws DocletException if there is a problem while building the documentation 117 */ 118 protected void buildPropertyDoc(Content detailsList) 119 throws DocletException { 120 if (hasMembersToDocument()) { 121 Content propertyDetailsHeader 122 = writer.getPropertyDetailsHeader(detailsList); 123 Content memberList = writer.getMemberList(); 124 125 for (Element property : properties) { 126 currentProperty = (ExecutableElement) property; 127 Content propertyContent 128 = writer.getPropertyHeaderContent(currentProperty); 129 130 buildSignature(propertyContent); 131 buildPropertyComments(propertyContent); 132 buildTagInfo(propertyContent); 133 134 memberList.add(writer.getMemberListItem(propertyContent)); 135 } 136 Content propertyDetails 137 = writer.getPropertyDetails(propertyDetailsHeader, memberList); 138 detailsList.add(propertyDetails); 139 } 140 } 141 142 /** 143 * Build the signature. 144 * 145 * @param propertyContent the content to which the documentation will be added 146 */ 147 protected void buildSignature(Content propertyContent) { 148 propertyContent.add(writer.getSignature(currentProperty)); 149 } 150 151 /** 152 * Build the deprecation information. 153 * 154 * @param propertyContent the content to which the documentation will be added 155 */ 156 protected void buildDeprecationInfo(Content propertyContent) { 157 writer.addDeprecated(currentProperty, propertyContent); 158 } 159 160 /** 161 * Build the preview information. 162 * 163 * @param propertyContent the content to which the documentation will be added 164 */ 165 protected void buildPreviewInfo(Content propertyContent) { 166 writer.addPreview(currentProperty, propertyContent); 167 } 168 169 /** 170 * Build the comments for the property. Do nothing if 171 * {@link BaseOptions#noComment()} is set to true. 172 * 173 * @param propertyContent the content to which the documentation will be added 174 */ 175 protected void buildPropertyComments(Content propertyContent) { 176 if (!options.noComment()) { 177 writer.addComments(currentProperty, propertyContent); 178 } 179 } 180 181 /** 182 * Build the tag information. 183 * 184 * @param propertyContent the content to which the documentation will be added 185 */ 186 protected void buildTagInfo(Content propertyContent) { 187 CommentUtils cmtUtils = configuration.cmtUtils; 188 DocCommentTree dct = utils.getDocCommentTree(currentProperty); 189 var fullBody = dct.getFullBody(); 190 ArrayList<DocTree> blockTags = dct.getBlockTags().stream() 191 .filter(t -> t.getKind() != DocTree.Kind.RETURN) 192 .collect(Collectors.toCollection(ArrayList::new)); 193 String sig = "#" + currentProperty.getSimpleName() + "()"; 194 blockTags.add(cmtUtils.makeSeeTree(sig, currentProperty)); 195 // The property method is used as a proxy for the property 196 // (which does not have an explicit element of its own.) 197 // Temporarily override the doc comment for the property method 198 // by removing the `@return` tag, which should not be displayed for 199 // the property. 200 CommentUtils.DocCommentInfo prev 201 = cmtUtils.setDocCommentTree(currentProperty, fullBody, blockTags); 202 try { 203 writer.addTags(currentProperty, propertyContent); 204 } finally { 205 cmtUtils.setDocCommentInfo(currentProperty, prev); 206 } 207 } 208 209 /** 210 * Return the property writer for this builder. 211 * 212 * @return the property writer for this builder. 213 */ 214 public PropertyWriter getWriter() { 215 return writer; 216 } 217}