001: package org.kohsuke.javanet.approval;
002:
003: import com.meterware.httpunit.WebConversation;
004: import com.meterware.httpunit.WebForm;
005: import com.meterware.httpunit.WebResponse;
006: import org.cyberneko.html.parsers.SAXParser;
007: import org.dom4j.Document;
008: import org.dom4j.DocumentException;
009: import org.dom4j.Element;
010: import org.dom4j.Node;
011: import org.dom4j.io.SAXReader;
012: import org.kohsuke.jnt.JNProject;
013: import org.kohsuke.jnt.JavaNet;
014: import org.kohsuke.jnt.ProcessingException;
015: import org.xml.sax.SAXException;
016:
017: import java.io.IOException;
018: import java.io.Serializable;
019: import java.io.StringReader;
020: import java.text.DateFormat;
021: import java.text.SimpleDateFormat;
022: import java.util.Date;
023: import java.util.HashMap;
024: import java.util.Map;
025:
026: /**
027: * Represents the project database entry in O'Reilly side of the java.net.
028: * @author Kohsuke Kawaguchi
029: */
030: public class OReillyProject implements Serializable {
031:
032: /**
033: * URL to the edit page like
034: * http://community.java.net/cs/sun/edit/p/5611
035: */
036: private final String editURL;
037: private transient String community;
038:
039: private transient boolean parsed;
040:
041: // parsed information is transient, so that we'll reparse them
042: // everytime we suspend
043:
044: public OReillyProject(JNProject project) throws IOException,
045: SAXException, DocumentException {
046: WebResponse r = project.getConnection().getConversation()
047: .getResponse("http://today.java.net/pub/q/42");
048: Document dom = toDOM(r);
049:
050: Node row = dom
051: .selectSingleNode("/HTML/BODY/TABLE//TR[TD/A[translate(@href,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz')='https://"
052: + project.getName().toLowerCase()
053: + ".dev.java.net']]");
054: if (row == null)
055: throw new IllegalArgumentException(
056: "Unable to find the project link");
057:
058: Element editLink = (Element) row.selectSingleNode("TD[1]/A");
059: if (editLink == null)
060: throw new IllegalArgumentException(
061: "Unable to find the project link");
062:
063: editURL = editLink.attributeValue("href");
064: if (editURL == null)
065: throw new IllegalArgumentException(
066: "Unable to find the project link");
067: }
068:
069: private Document toDOM(WebResponse r) throws DocumentException,
070: IOException {
071: return new SAXReader(new SAXParser()).read(new StringReader(r
072: .getText()));
073: }
074:
075: public void parse(JavaNet jn) throws DocumentException,
076: IOException, SAXException, ProcessingException {
077: if (parsed)
078: return;
079:
080: new Task() {
081: protected boolean scrape(WebForm form) {
082: community = getOptionValueFor(form, "id_c", form
083: .getParameterValue("id_c"));
084: parsed = true;
085: return false;
086: }
087: }.run(jn);
088: }
089:
090: /**
091: * Gets the community to which this project falls under.
092: *
093: * (If this project is not approved, it is the community to which the project
094: * wants to join.)
095: *
096: * @return
097: * can be null.
098: */
099: public JNProject getCommunity(JavaNet jn)
100: throws ProcessingException, DocumentException, IOException,
101: SAXException {
102: parse(jn);
103: if (community == null)
104: return null;
105: String c = o2c.get(community);
106: if (c == null)
107: return null;
108: return jn.getProject(c);
109: }
110:
111: /**
112: * Update the O'Reilly database by saying that this project is approved into the given community.
113: */
114: public void approve(final JNProject community)
115: throws ProcessingException, IOException, SAXException {
116: final String oName = c2o.get(community.getName());
117: if (oName == null)
118: throw new IllegalArgumentException(community
119: + " is not a community");
120:
121: new Task() {
122: protected boolean scrape(WebForm form) {
123: {// set the community
124: boolean found = false;
125: String[] options = form.getOptions("id_c");
126: for (int i = 0; i < options.length; i++)
127: if (options[i].equals(oName)) {
128: form.setParameter("id_c", form
129: .getOptionValues("id_c")[i]);
130: found = true;
131: break;
132: }
133:
134: if (!found)
135: throw new IllegalArgumentException(community
136: + " was not found on the form");
137: }
138:
139: // change the state
140: form.setParameter("status", "approved");
141:
142: addNote(form, "Approved");
143:
144: return true;
145: }
146: }.run(community.getConnection());
147: }
148:
149: /**
150: * Marks the project simply as "disapproved".
151: */
152: public void disapprove(JavaNet jn) throws ProcessingException,
153: IOException, SAXException {
154: new Task() {
155: protected boolean scrape(WebForm form) {
156: // change the state
157: form.setParameter("status", "disapproved");
158:
159: addNote(form, "Disapproved");
160:
161: return true;
162: }
163: }.run(jn);
164: }
165:
166: /**
167: * Updates the note by adding the given text.
168: */
169: public void update(JavaNet jn, final String note)
170: throws ProcessingException, IOException, SAXException {
171: new Task() {
172: protected boolean scrape(WebForm form) {
173: addNote(form, note);
174: return true;
175: }
176: }.run(jn);
177: }
178:
179: /**
180: * Adds a new line to the note.
181: */
182: private void addNote(WebForm form, String text) {
183: String value = form.getParameterValue("notes");
184: value = value.trim();
185: if (value.length() > 0)
186: value += '\n';
187: value += text.trim() + ' ' + FORMATTER.format(new Date())
188: + " daemon";
189: form.setParameter("notes", value);
190: }
191:
192: /**
193: * Oreilly name to collab.net name.
194: */
195: private static final Map<String, String> o2c = new HashMap<String, String>();
196: private static final Map<String, String> c2o;
197:
198: static {
199: o2c.put("Embedded Java", "embeddedjava");
200: o2c.put("Global Education and Learning Community", "edu-gelc");
201: o2c.put("Java Communications", "communications");
202: o2c.put("Java Distributed Data Acquisition and Control",
203: "jddac");
204: o2c.put("Java Enterprise", "java-enterprise");
205: o2c.put("Java Games", "games");
206: o2c.put("Java Patterns", "patterns");
207: o2c.put("Java Specification Requests", "jsr");
208: o2c.put("Java Tools", "javatools");
209: o2c.put("Java User Groups", "jugs");
210: o2c.put("Java Web Services and XML", "java-ws-xml");
211: o2c.put("JavaDesktop", "javadesktop");
212: o2c.put("JDK", "jdk");
213: // data.put("Jini","");
214: o2c.put("JXTA", "jxta");
215: o2c.put("Linux", "linux");
216: o2c.put("Mac Java Community", "mac");
217: // data.put("NetBeans","");
218: o2c.put("Portlet", "portlet");
219: o2c.put("Robotics", "robotics");
220: // data.put("Sun Grid Developer Community","");
221:
222: c2o = Util.reverseUniqueMap(o2c);
223: }
224:
225: /**
226: * Gets the display name for the option value.
227: */
228: static String getOptionValueFor(WebForm form, String parameter,
229: String value) {
230: String[] options = form.getOptionValues(parameter);
231: for (int i = 0; i < options.length; i++) {
232: if (options[i].equals(value)) {
233: return form.getOptions(parameter)[i];
234: }
235: }
236: return null;
237: }
238:
239: public static void main(String[] args) throws Exception {
240: JavaNet jn = JavaNet.connect();
241: OReillyProject op = new OReillyProject(jn
242: .getProject("kohsuke-test-4"));
243: // System.out.println(op.getCommunity(jn));
244: // op.approve(jn.getProject("javatools"));
245: op.update(jn, "New note.");
246: op.disapprove(jn);
247: }
248:
249: private static final DateFormat FORMATTER = new SimpleDateFormat(
250: "MM/dd/yy");
251:
252: /**
253: * Wrapper for the HTML scraper, to make sure that we don't leave a lock behind.
254: */
255: private abstract class Task {
256: final void run(JavaNet jn) throws SAXException, IOException {
257: WebConversation wc = jn.getConversation();
258: WebResponse r = wc.getResponse(editURL);
259: WebForm form = r.getFormWithName("p_form");
260:
261: boolean success = false;
262: try {
263: if (scrape(form))
264: submit(form);
265: else
266: cancel(form);
267: success = true;
268: } finally {
269: if (!success) {
270: cancel(form);
271: }
272: }
273: }
274:
275: private void cancel(WebForm form) throws IOException,
276: SAXException {
277: form.getSubmitButton("x-a", "Cancel").click();
278: }
279:
280: private void submit(WebForm form) throws IOException,
281: SAXException {
282: form.getSubmitButton("x-a", "Submit").click();
283: }
284:
285: /**
286: * Returns true to commit. False to cancel.
287: */
288: protected abstract boolean scrape(WebForm form)
289: throws IOException, SAXException;
290: }
291: }
|