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