    001// Copyright 2014 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.
    014package org.apache.tapestry5.services;
    015
    016import java.io.Serializable;
    017import java.util.ArrayList;
    018import java.util.List;
    019
    020/**
    021 * Class that encapsulates information about a component library, going beyond what a library mapping
    022 * provides.
    023 *
    024 * @see LibraryMapping
    025 * @see SourceUrlResolver
    026 * @since 5.4
    027 */
    028public final class ComponentLibraryInfo implements Serializable
    029{
    030    private static final long serialVersionUID = 1L;
    031
    032    private LibraryMapping libraryMapping;
    033
    034    private SourceUrlResolver sourceUrlResolver;
    035
    036    private String name, description, homepageUrl, documentationUrl, sourceBrowseUrl, issueTrackerUrl, sourceRootUrl,
    037                   javadocUrl, groupId, artifactId, version, tapestryVersion;
    038
    039    private List<String> tags = new ArrayList<String>();
    040
    041    /**
    042     * Returns the actual name of the component library (not the identifier).
    043     * For example, "Tapestry 5 Core Library".
    044     */
    045    public String getName()
    046    {
    047        return name;
    048    }
    049
    050    /**
    051     * Returns a description of the component library.
    052     * For example, "The set of components, pages and mixins provided by Tapestry out-of-the-box.".
    053     */
    054    public String getDescription()
    055    {
    056        return description;
    057    }
    058
    059    /**
    060     * Returns the URL of the homepage of the component library.
    061     * For example, "http://tapestry.apache.org".
    062     */
    063    public String getHomepageUrl()
    064    {
    065        return homepageUrl;
    066    }
    067
    068    /**
    069     * Returns the URL of the component library's documentation.
    070     * For example, "http://tapestry.apache.org/documentation.html".
    071     */
    072    public String getDocumentationUrl()
    073    {
    074        return documentationUrl;
    075    }
    076
    077    /**
    078     * Returns the URL where the component library's source can be browsed.
    079     * For example, "https://git-wip-us.apache.org/repos/asf?p=tapestry-5.git;a=summary".
    080     */
    081    public String getSourceBrowseUrl()
    082    {
    083        return sourceBrowseUrl;
    084    }
    085
    086    /**
    087     * Returns the URL where the root folder of component library's source can be found.
    088     * For example, "https://git-wip-us.apache.org/repos/asf?p=tapestry-5.git;a=tree;f=tapestry-core/src/main/java/".
    089     */
    090    public String getSourceRootUrl()
    091    {
    092        return sourceRootUrl;
    093    }
    094
    095    /**
    096     * Returns the URL of the component's library issue tracker.
    097     * For example, "https://issues.apache.org/jira/browse/TAP5".
    098     */
    099    public String getIssueTrackerUrl()
    100    {
    101        return issueTrackerUrl;
    102    }
    103
    104    /**
    105     * Returns the URL of the component library's JavaDoc URL.
    106     * For example, "http://tapestry.apache.org/current/apidocs/"
    107     */
    108    public String getJavadocUrl()
    109    {
    110        return javadocUrl;
    111    }
    112
    113    /**
    114     * Returns the component library's group id for dependency management tools like Maven and Gradle.
    115     * For example, "org.apache.tapestry".
    116     * @see #artifactId
    117     * @see #version
    118     */
    119    public String getGroupId()
    120    {
    121        return groupId;
    122    }
    123
    124    /**
    125     * Returns the component library's group id for dependency management tools like Maven and Gradle.
    126     * For example, "tapestry-core".
    127     * @see #groupId
    128     * @see #version
    129     */
    130    public String getArtifactId()
    131    {
    132        return artifactId;
    133    }
    134
    135    /**
    136     * Returns the component library version. For example, "5.4.0".
    137     * @see #artifactId
    138     * @see #groupId
    139     */
    140    public String getVersion()
    141    {
    142        return version;
    143    }
    144
    145    /**
    146     * Returns the Tapestry version used by this component library. For example, "5.4.0".
    147     */
    148    public String getTapestryVersion()
    149    {
    150        return tapestryVersion;
    151    }
    152
    153    /**
    154     * Returns the tags associated which describe this component library.
    155     * Use just lowercase letters, numbers and dashes.
    156     */
    157    public List<String> getTags()
    158    {
    159        return tags;
    160    }
    161
    162    /**
    163     * Returns an URL decribing the dependency management information for this component library.
    164     */
    165    public String getDependencyManagementInfoUrl()
    166    {
    167        String url = null;
    168        if (isDependencyManagementInfoPresent())
    169        {
    170            url = String.format(
    171                    "http://search.maven.org/#artifactdetails|%s|%s|version=%s|jar",
    172                    getGroupId(), getArtifactId(), getVersion());
    173        }
    174        return url;
    175    }
    176
    177    public void setName(String name)
    178    {
    179        if (this.name != null) throwExceptionIfAlreadySet("name", name);
    180        this.name = name;
    181    }
    182
    183    public void setDescription(String description)
    184    {
    185        if (this.description != null) throwExceptionIfAlreadySet("description", description);
    186        this.description = description;
    187    }
    188
    189    public void setHomepageUrl(String homepageUrl)
    190    {
    191        if (this.homepageUrl != null) throwExceptionIfAlreadySet("homepageUrl", homepageUrl);
    192        this.homepageUrl = homepageUrl;
    193    }
    194
    195    public void setDocumentationUrl(String documentationUrl)
    196    {
    197        if (this.documentationUrl != null) throwExceptionIfAlreadySet("documentationUrl", documentationUrl);
    198        this.documentationUrl = documentationUrl;
    199    }
    200
    201    public void setSourceBrowseUrl(String sourceBrowseUrl)
    202    {
    203        if (this.sourceBrowseUrl != null) throwExceptionIfAlreadySet("sourceBrowseUrl", sourceBrowseUrl);
    204        this.sourceBrowseUrl = sourceBrowseUrl;
    205    }
    206
    207    public void setSourceRootUrl(String sourceRootUrl)
    208    {
    209        if (this.sourceRootUrl != null) throwExceptionIfAlreadySet("sourceRootUrl", sourceRootUrl);
    210        this.sourceRootUrl = sourceRootUrl;
    211    }
    212
    213    public void setJavadocUrl(String javadocUrl)
    214    {
    215        if (this.javadocUrl != null) throwExceptionIfAlreadySet("javadocUrl", javadocUrl);
    216        this.javadocUrl = javadocUrl;
    217    }
    218
    219    public void setVersion(String version)
    220    {
    221        if (this.version != null) throwExceptionIfAlreadySet("version", version);
    222        this.version = version;
    223    }
    224
    225    public void setTapestryVersion(String tapestryVersion)
    226    {
    227        if (this.tapestryVersion != null) throwExceptionIfAlreadySet("tapestryVersion", version);
    228        this.tapestryVersion = tapestryVersion;
    229    }
    230
    231    public void setGroupId(String groupId)
    232    {
    233        if (this.groupId != null) throwExceptionIfAlreadySet("groupId", artifactId);
    234        this.groupId = groupId;
    235    }
    236
    237    public void setArtifactId(String artifactId)
    238    {
    239        if (this.artifactId != null) throwExceptionIfAlreadySet("artifactId", artifactId);
    240        this.artifactId = artifactId;
    241    }
    242
    243    public void setIssueTrackerUrl(String issueTrackingUrl)
    244    {
    245        if (this.issueTrackerUrl != null) throwExceptionIfAlreadySet("issueTrackingUrl", issueTrackingUrl);
    246        this.issueTrackerUrl = issueTrackingUrl;
    247    }
    248
    249    public void setTags(List<String> tags)
    250    {
    251        if (this.tags != null) throwExceptionIfAlreadySet("tags", tags);
    252        this.tags = tags;
    253    }
    254
    255    public void setLibraryMapping(LibraryMapping libraryMapping)
    256    {
    257        if (this.libraryMapping != null) throwExceptionIfAlreadySet("libraryMapping", libraryMapping);
    258        this.libraryMapping = libraryMapping;
    259    }
    260
    261    public void setSourceUrlResolver(SourceUrlResolver sourceUrlResolver)
    262    {
    263        if (this.sourceUrlResolver != null) throwExceptionIfAlreadySet("sourceUrlResolver", sourceUrlResolver);
    264        this.sourceUrlResolver = sourceUrlResolver;
    265        if (sourceUrlResolver != null)
    266        {
    267            sourceUrlResolver.setRootUrl(getSourceRootUrl());
    268        }
    269    }
    270
    271    /**
    272     * Tells whether full dependency management info (group id, artifact id and version) are present.
    273     */
    274    public boolean isDependencyManagementInfoPresent()
    275    {
    276        return groupId != null && artifactId != null && version != null;
    277    }
    278
    279    /**
    280     * Given a logical name, tells whether a given component, page or mixin is part of this
    281     * component library.
    282     */
    283    public boolean isPart(String logicalName)
    284    {
    285        return logicalName.startsWith(libraryMapping.libraryName + "/") ||
    286                (libraryMapping.libraryName.equals("") && logicalName.indexOf("/") < 0);
    287    }
    288
    289    /**
    290     * Returns the JavaDoc URL for a given class or <code>null</code> if the root JavaDoc URL was
    291     * not provided.
    292     * @param className the fully qualified class name.
    293     */
    294    public String getJavadocUrl(String className)
    295    {
    296        String url = null;
    297        String baseUrl = getJavadocUrl();
    298        if (baseUrl != null)
    299        {
    300            if (!baseUrl.endsWith("/"))
    301            {
    302                baseUrl = baseUrl + "/";
    303            }
    304            url = baseUrl + className.replace('.', '/') + ".html";
    305        }
    306        return url;
    307    }
    308
    309    /**
    310     * Returns the URL where the source of this class can be found or <code>null</code> if
    311     * not available. This implementation delegates to {@link SourceUrlResolver} if set.
    312     * @param className the fully qualified class name.
    313     */
    314    public String getSourceUrl(String className)
    315    {
    316        String url = null;
    317        if (sourceRootUrl != null)
    318        {
    319            if (sourceUrlResolver == null)
    320            {
    321                sourceUrlResolver = new DefaultSourceUrlResolver();
    322                sourceUrlResolver.setRootUrl(sourceRootUrl);
    323            }
    324            url = sourceUrlResolver.resolve(className);
    325        }
    326        return url;
    327    }
    328
    329    private void throwExceptionIfAlreadySet(String propertyName, Object propertyValue)
    330    {
    331        if (propertyValue != null)
    332        {
    333            throw new RuntimeException(String.format("%s already has a value of \"%s\"", propertyName, propertyValue));
    334        }
    335    }
    336
    337    /**
    338     * Interface that provides the source URL for a given {@link ComponentLibraryInfo}.
    339     */
    340    public static interface SourceUrlResolver
    341    {
    342        /**
    343         * Returns the source URL for a given class.
    344         * @param className the fully qualified class name.
    345         */
    346        String resolve(String className);
    347
    348        /**
    349         * Sets the source root URL. This method will be invoked by {@link ComponentLibraryInfo#setSourceBrowseUrl(String)}.
    350         */
    351        void setRootUrl(String url);
    352
    353    }
    354
    355    /**
    356     * Default {@link SourceUrlResolver} implementation.
    357     */
    358    public static class DefaultSourceUrlResolver implements SourceUrlResolver
    359    {
    360
    361        private String sourceRootUrl;
    362
    363        @Override
    364        public String resolve(String className)
    365        {
    366            return sourceRootUrl + className.replace('.', '/') + ".java";
    367        }
    368
    369        @Override
    370        public void setRootUrl(String url)
    371        {
    372            this.sourceRootUrl = url;
    373            if (sourceRootUrl.startsWith("scm:"))
    374            {
    375                this.sourceRootUrl = this.sourceRootUrl.replaceFirst("[^:]+:[^:]+:", "");
    376            }
    377        }
    378
    379    }
    380
    381    public String toString() {
    382        return String.format("ComponentLibraryInfo[%s]", libraryMapping);
    383    }
    384
    385}