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;
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     * Return 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 tags associated which describe this component library.
147     * Use just lowercase letters, numbers and dashes.
148     */
149    public List<String> getTags()
150    {
151        return tags;
152    }
153
154    /**
155     * Returns an URL decribing the dependency management information for this component library.
156     */
157    public String getDependencyManagementInfoUrl()
158    {
159        String url = null;
160        if (isDependencyManagementInfoPresent())
161        {
162            url = String.format(
163                    "http://search.maven.org/#artifactdetails|%s|%s|version=%s|jar",
164                    getGroupId(), getArtifactId(), getVersion());
165        }
166        return url;
167    }
168
169    public void setName(String name)
170    {
171        if (this.name != null) throwExceptionIfAlreadySet("name", name);
172        this.name = name;
173    }
174    
175    public void setDescription(String description)
176    {
177        if (this.description != null) throwExceptionIfAlreadySet("description", description);
178        this.description = description;
179    }
180
181    public void setHomepageUrl(String homepageUrl)
182    {
183        if (this.homepageUrl != null) throwExceptionIfAlreadySet("homepageUrl", homepageUrl);
184        this.homepageUrl = homepageUrl;
185    }
186
187    public void setDocumentationUrl(String documentationUrl)
188    {
189        if (this.documentationUrl != null) throwExceptionIfAlreadySet("documentationUrl", documentationUrl);
190        this.documentationUrl = documentationUrl;
191    }
192
193    public void setSourceBrowseUrl(String sourceBrowseUrl)
194    {
195        if (this.sourceBrowseUrl != null) throwExceptionIfAlreadySet("sourceBrowseUrl", sourceBrowseUrl);
196        this.sourceBrowseUrl = sourceBrowseUrl;
197    }
198
199    public void setSourceRootUrl(String sourceRootUrl)
200    {
201        if (this.sourceRootUrl != null) throwExceptionIfAlreadySet("sourceRootUrl", sourceRootUrl);
202        this.sourceRootUrl = sourceRootUrl;
203    }
204
205    public void setJavadocUrl(String javadocUrl)
206    {
207        if (this.javadocUrl != null) throwExceptionIfAlreadySet("javadocUrl", javadocUrl);
208        this.javadocUrl = javadocUrl;
209    }
210
211    public void setVersion(String version)
212    {
213        if (this.version != null) throwExceptionIfAlreadySet("version", version);
214        this.version = version;
215    }
216    
217    public void setGroupId(String groupId)
218    {
219        if (this.groupId != null) throwExceptionIfAlreadySet("groupId", artifactId);
220        this.groupId = groupId;
221    }
222    
223    public void setArtifactId(String artifactId)
224    {
225        if (this.artifactId != null) throwExceptionIfAlreadySet("artifactId", artifactId);
226        this.artifactId = artifactId;
227    }
228    
229    public void setIssueTrackerUrl(String issueTrackingUrl)
230    {
231        if (this.issueTrackerUrl != null) throwExceptionIfAlreadySet("issueTrackingUrl", issueTrackingUrl);
232        this.issueTrackerUrl = issueTrackingUrl;
233    }
234
235    public void setTags(List<String> tags)
236    {
237        if (this.tags != null) throwExceptionIfAlreadySet("tags", tags);
238        this.tags = tags;
239    }
240
241    public void setLibraryMapping(LibraryMapping libraryMapping)
242    {
243        if (this.libraryMapping != null) throwExceptionIfAlreadySet("libraryMapping", libraryMapping);
244        this.libraryMapping = libraryMapping;
245    }
246    
247    public void setSourceUrlResolver(SourceUrlResolver sourceUrlResolver)
248    {
249        if (this.sourceUrlResolver != null) throwExceptionIfAlreadySet("sourceUrlResolver", sourceUrlResolver);
250        this.sourceUrlResolver = sourceUrlResolver;
251        if (sourceUrlResolver != null)
252        {
253            sourceUrlResolver.setRootUrl(getSourceRootUrl());
254        }
255    }
256
257    /**
258     * Tells whether full dependency management info (group id, artifact id and version) are present.
259     */
260    public boolean isDependencyManagementInfoPresent()
261    {
262        return groupId != null && artifactId != null && version != null;
263    }
264    
265    /**
266     * Given a logical name, tells whether a given component, page or mixin is part of this
267     * component library.
268     */
269    public boolean isPart(String logicalName)
270    {
271        return logicalName.startsWith(libraryMapping.libraryName + "/") || 
272                (libraryMapping.libraryName.equals("") && logicalName.indexOf("/") < 0);
273    }
274    
275    /**
276     * Returns the JavaDoc URL for a given class or <code>null</code> if the root JavaDoc URL was 
277     * not provided. 
278     * @param className the fully qualified class name.
279     */
280    public String getJavadocUrl(String className)
281    {
282        String url = null;
283        String baseUrl = getJavadocUrl();
284        if (baseUrl != null)
285        {
286            if (!baseUrl.endsWith("/"))
287            {
288                baseUrl = baseUrl + "/";
289            }
290            url = baseUrl + className.replace('.', '/') + ".html";
291        }
292        return url;
293    }
294
295    /**
296     * Returns the URL where the source of this class can be found or <code>null</code> if 
297     * not available. This implementation delegates to {@link SourceUrlResolver} if set.
298     * @param className the fully qualified class name.
299     */
300    public String getSourceUrl(String className)
301    {
302        String url = null;
303        if (sourceRootUrl != null)
304        {
305            if (sourceUrlResolver == null)
306            {
307                sourceUrlResolver = new DefaultSourceUrlResolver();
308                sourceUrlResolver.setRootUrl(sourceRootUrl);
309            }
310            url = sourceUrlResolver.resolve(className);
311        }
312        return url;
313    }
314
315    private void throwExceptionIfAlreadySet(String propertyName, Object propertyValue)
316    {
317        if (propertyValue != null)
318        {
319            throw new RuntimeException(String.format("%s already has a value of \"%s\"", propertyName, propertyValue));
320        }
321    }
322    
323    /**
324     * Interface that provides the source URL for a given {@link ComponentLibraryInfo}.
325     */
326    public static interface SourceUrlResolver
327    {
328        /**
329         * Returns the source URL for a given class.
330         * @param className the fully qualified class name.
331         */
332        String resolve(String className);
333        
334        /**
335         * Sets the source root URL. This method will be invoked by {@link ComponentLibraryInfo#setSourceBrowseUrl(String)}.
336         */
337        void setRootUrl(String url);
338        
339    }
340    
341    /**
342     * Default {@link SourceUrlResolver} implementation.
343     */
344    public static class DefaultSourceUrlResolver implements SourceUrlResolver
345    {
346
347        private String sourceRootUrl;
348
349        @Override
350        public String resolve(String className)
351        {
352            return sourceRootUrl + className.replace('.', '/') + ".java";
353        }
354
355        @Override
356        public void setRootUrl(String url)
357        {
358            this.sourceRootUrl = url;
359            if (sourceRootUrl.startsWith("scm:"))
360            {
361                this.sourceRootUrl = this.sourceRootUrl.replaceFirst("[^:]+:[^:]+:", "");
362            }
363        }
364        
365    }
366    
367    public String toString() {
368        return String.format("ComponentLibraryInfo[%s]", libraryMapping);
369    }
370    
371}