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}