| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| /* |
| * BloglinesClient.groovy - an example of the Bloglines Web Services |
| * |
| * Written by Marc Hedlund <marc@precipice.org>, September 2004. |
| * |
| * Mangled by John Wilson September 2004 |
| * |
| * Small adaptions to JSR Version by Dierk Koenig, June 2005 |
| * |
| * Used in Marc's article at: |
| * http://www.oreillynet.com/pub/a/network/2004/09/28/bloglines.html |
| * |
| * Requirements: |
| * - install Groovy as detailed at <http://groovy.codehaus.org/>. |
| * - put commons-httpclient-3.0-rc3.jar into GROOVY_HOME/lib |
| * see <http://jakarta.apache.org/commons/httpclient/>. |
| * note: this is currently designed for HttpClient2.x and not HttpClient3.x |
| * |
| * To Launch: |
| * groovy BloglinesClient.groovy |
| * |
| * This work is licensed under the Creative Commons Attribution |
| * License. To view a copy of this license, visit |
| * <http://creativecommons.org/licenses/by/2.0/> or send a letter to |
| * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. |
| */ |
| package swing |
| |
| import groovy.swing.SwingBuilder |
| import java.awt.BorderLayout |
| import javax.swing.JOptionPane |
| import javax.swing.JSplitPane |
| import javax.swing.JTree |
| import javax.swing.ListSelectionModel |
| import javax.swing.WindowConstants |
| import javax.swing.tree.DefaultMutableTreeNode |
| import javax.swing.tree.TreeSelectionModel |
| import org.apache.commons.httpclient.HttpClient |
| import org.apache.commons.httpclient.UsernamePasswordCredentials |
| import org.apache.commons.httpclient.methods.GetMethod |
| |
| //Set up global variables and data types |
| server = 'rpc.bloglines.com' |
| |
| class Feed { |
| def name; |
| def id; |
| def unread; |
| |
| String toString() { (unread == "0" ? name : "${name} (${unread})") } |
| } |
| |
| class Item { |
| def title; |
| def description; |
| |
| String toString() { title } |
| } |
| |
| // Ask the user for account information (using simple dialogs) |
| email = JOptionPane.showInputDialog(null, "Email address:", "Log in to Bloglines", |
| JOptionPane.QUESTION_MESSAGE) |
| password = JOptionPane.showInputDialog(null, "Password:", "Log in to Bloglines", |
| JOptionPane.QUESTION_MESSAGE) |
| |
| //Use HTTPClient for web requests since the server requires authentication |
| client = new HttpClient() |
| credentials = new UsernamePasswordCredentials(email, password) |
| client.state.setCredentials("Bloglines RPC", server, credentials) |
| |
| abstractCallBloglines = { method, parameters -> |
| url = "http://${server}/${method}${parameters}" |
| try { |
| get = new GetMethod(url) |
| get.doAuthentication = true |
| client.executeMethod(get) |
| return get.responseBodyAsStream |
| } catch (Exception e) { |
| println "Error retrieving <${url}>: ${e}" |
| } |
| } |
| |
| callBloglinesListsub = abstractCallBloglines.curry('listsubs', '') |
| callBloglinesGetItems = abstractCallBloglines.curry('getitems') |
| |
| //Get the list of subscriptions and parse it into a GPath structure |
| opml = new XmlSlurper().parse(callBloglinesListsub()) |
| |
| //Descend into the subscription outline, adding to the feed tree as we go |
| treeTop = new DefaultMutableTreeNode("My Feeds") |
| parseOutline(opml.body.outline.outline, treeTop) |
| |
| def parseOutline(parsedXml, treeLevel) { |
| parsedXml.each { outline -> |
| if (outline['@xmlUrl'] != null) { // this is an individual feed |
| feed = new Feed(name: outline['@title'], id: outline['@BloglinesSubId'], |
| unread: outline['@BloglinesUnread']) |
| treeLevel.add(new DefaultMutableTreeNode(feed)) |
| } else { // this is a folder of feeds |
| folder = new DefaultMutableTreeNode(outline['@title']) |
| parseOutline(outline.outline, folder) |
| treeLevel.add(folder) |
| } |
| } |
| } |
| |
| //Build the base user interface objects and configure them |
| swing = new SwingBuilder() |
| feedTree = new JTree(treeTop) |
| itemList = swing.list() |
| itemText = swing.textPane(contentType: 'text/html', editable: false) |
| model = feedTree.selectionModel |
| model.selectionMode = TreeSelectionModel.SINGLE_TREE_SELECTION |
| itemList.selectionMode = ListSelectionModel.SINGLE_SELECTION |
| |
| //Set up the action closures that will react to user selections |
| listItems = { feed -> |
| rssStream = callBloglinesGetItems("?s=${feed.id}&n=0") |
| if (rssStream != null) { |
| try { |
| rss = new XmlSlurper().parse(rssStream) |
| itemList.listData = rss.channel.item.collect(new Vector()) { |
| new Item(title: it.title, description: it.description) |
| } |
| feed.unread = "0" // update the unread item count in the feed list |
| } catch (Exception e) { |
| println "Error during <${feed.name}> RSS parse: ${e}" |
| } |
| } |
| } |
| |
| feedTree.valueChanged = { event -> |
| itemText.text = "" // clear any old item text |
| node = (DefaultMutableTreeNode) feedTree.getLastSelectedPathComponent() |
| if (node != null) { |
| feed = node.userObject |
| if (feed instanceof Feed && feed.unread != "0") { |
| listItems(feed) |
| } |
| } |
| } |
| |
| itemList.valueChanged = { event -> |
| item = event.source.selectedValue |
| if (item instanceof Item && item?.description != null) { |
| itemText.text = "<html><body>${item.description}</body></html>" |
| } |
| } |
| |
| //Put the user interface together and display it |
| gui = swing.frame(title: 'Bloglines Client', location: [100, 100], size: [800, 600], |
| defaultCloseOperation: WindowConstants.EXIT_ON_CLOSE) { |
| panel(layout: new BorderLayout()) { |
| splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT, dividerLocation: 200) { |
| scrollPane { |
| widget(feedTree) |
| } |
| splitPane(orientation: JSplitPane.VERTICAL_SPLIT, dividerLocation: 150) { |
| scrollPane(constraints: BorderLayout.CENTER) { |
| widget(itemList) |
| } |
| scrollPane(constraints: BorderLayout.CENTER) { |
| widget(itemText) |
| } |
| } |
| } |
| } |
| } |
| |
| gui.show() |