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}