View Javadoc

1   /* -*- mode: JDE; c-basic-offset: 2; indent-tabs-mode: nil -*-
2    *
3    * $Id: SourceForge.java,v 1.4 2004/07/27 20:20:36 ljnelson Exp $
4    *
5    * Copyright (c) 2003 Laird Jarrett Nelson.
6    *
7    * Permission is hereby granted, free of charge, to any person obtaining a copy
8    * of this software and associated documentation files (the "Software"), to deal
9    * in the Software without restriction, including without limitation the rights
10   * to use, copy, modify, merge, publish, distribute, sublicense and/or sell 
11   * copies of the Software, and to permit persons to whom the Software is 
12   * furnished to do so, subject to the following conditions:
13   * 
14   * The above copyright notice and this permission notice shall be included in 
15   * all copies or substantial portions of the Software.
16   * 
17   * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
19   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
20   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
21   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23   * SOFTWARE.
24   *
25   * The original copy of this license is available at
26   * http://www.opensource.org/license/mit-license.html.
27   */
28  package sfutils;
29  
30  import java.io.IOException;
31  
32  import java.util.Collections;
33  import java.util.Map;
34  import java.util.WeakHashMap;
35  
36  import com.meterware.httpunit.GetMethodWebRequest;
37  import com.meterware.httpunit.HttpUnitOptions;
38  import com.meterware.httpunit.WebConversation;
39  import com.meterware.httpunit.WebLink;
40  import com.meterware.httpunit.WebResponse;
41  
42  import org.xml.sax.SAXException;
43  
44  /***
45   * A utility class that provides access to small, core bits of information from
46   * the <a href="http://sourceforge.net/">SourceForge</a> website.
47   *
48   * @author     <a href="mailto:ljnelson94@alumni.amherst.edu">Laird Nelson</a>
49   * @version    $Revision: 1.4 $ $Date: 2004/07/27 20:20:36 $
50   * @since      July 12, 2003
51   */
52  public final class SourceForge {
53  
54    /***
55     * The leading portion of the URL required to access a <a
56     * href="http://sourceforge.net/">SourceForge</a> project.
57     */
58    private static final String PROJECT_PREFIX = 
59      "http://sourceforge.net/projects/";
60  
61    /***
62     * A {@link Map} of project IDs indexed by project short names.  This field is
63     * thread-safe.
64     */
65    private static final Map PROJECT_ID_MAP = 
66      Collections.synchronizedMap(new WeakHashMap());
67  
68    /***
69     * Static initializer; ensures that the {@link
70     * HttpUnitOptions#setExceptionsThrownOnScriptError(boolean)} method is called
71     * with <code>false</code> as its parameter to work around the fact that <a
72     * href="http://sourceforge.net/">SourceForge</a> pages have JavaScript errors
73     * in them.
74     */
75    static {
76      HttpUnitOptions.setExceptionsThrownOnScriptError(false);
77      HttpUnitOptions.setScriptingEnabled(false);
78    }
79  
80    /***
81     * Throws an {@link UnsupportedOperationException} when invoked.
82     *
83     * @exception  UnsupportedOperationException
84     *               when invoked
85     */
86    private SourceForge() throws UnsupportedOperationException {
87      super();
88      throw new UnsupportedOperationException();
89    }
90  
91    /***
92     * Returns the <a href="http://sourceforge.net/">SourceForge</a> project
93     * identifier given a project short name.  This method may return
94     * <code>null</code>.
95     *
96     * @param      projectShortName
97     *               the name of the project whose identifier should be returned;
98     *               may be <code>null</code> in which case <code>null</code> will
99     *               be returned
100    * @return     the identifier corresponding to the <a
101    *               href="http://sourceforge.net/">SourceForge</a> project with
102    *               the supplied short name, or <code>null</code>
103    * @exception  SourceForgeException
104    *               if an error occurs
105    */
106   public static final String getProjectID(final String projectShortName) 
107     throws SourceForgeException {
108     if (projectShortName == null) {
109       return null;
110     }
111     String projectID = (String)PROJECT_ID_MAP.get(projectShortName);
112     if (projectID != null) {
113       return projectID;
114     }
115     try {
116       final WebConversation webConversation = new WebConversation();
117       final WebResponse summaryPage = 
118         webConversation.getResponse(new GetMethodWebRequest(PROJECT_PREFIX +
119                                                             projectShortName));
120       assert summaryPage != null;
121       final WebLink viewMembersLink = summaryPage.getLinkWith("[View Members]");
122       if (viewMembersLink == null) {
123         // Maybe it was an invalid project?
124         final String text = summaryPage.getText();
125         assert text != null;
126         if (text.indexOf("Invalid Project") >= 0) {
127           throw new NoSuchProjectException(projectShortName);
128         }
129         // OK, uh, UI change?
130         throw new SourceForgeUIChangeException();
131       }
132       final String linkText = viewMembersLink.getURLString();
133       assert linkText != null;
134       final int groupIDIndex = linkText.indexOf("?group_id=");
135       if (groupIDIndex < 0) {
136         throw new SourceForgeUIChangeException();
137       }
138       projectID = linkText.substring(groupIDIndex + "?group_id=".length());
139       PROJECT_ID_MAP.put(projectShortName, projectID);
140       return projectID;
141     } catch (final IOException kaboom) {
142       throw new SourceForgeException(kaboom);
143     } catch (final SAXException kaboom) {
144       throw new SourceForgeException(kaboom);
145     }
146   }
147 
148 }