bloggerapihandler.java

### File Source: bloggerapihandler.java

             /*
        P/P   *  Method: net.sourceforge.pebble.webservice.BloggerAPIHandler__static_init
              *
              *  Postconditions:
              *    init'ed(log)
              */
         1  /*
         2   * Copyright (c) 2003-2006, Simon Brown
         3   * All rights reserved.
         4   *
         5   * Redistribution and use in source and binary forms, with or without
         6   * modification, are permitted provided that the following conditions are met:
         7   *
         8   *   - Redistributions of source code must retain the above copyright
         9   *     notice, this list of conditions and the following disclaimer.
        10   *
        11   *   - Redistributions in binary form must reproduce the above copyright
        12   *     notice, this list of conditions and the following disclaimer in
        13   *     the documentation and/or other materials provided with the
        14   *     distribution.
        15   *
        16   *   - Neither the name of Pebble nor the names of its contributors may
        17   *     be used to endorse or promote products derived from this software
        18   *     without specific prior written permission.
        19   *
        20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
        21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
        24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
        25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
        26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
        27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
        28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
        29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
        30   * POSSIBILITY OF SUCH DAMAGE.
        31   */
        32  package net.sourceforge.pebble.webservice;
        33
        34  import net.sourceforge.pebble.domain.*;
        35  import org.apache.commons.logging.Log;
        36  import org.apache.commons.logging.LogFactory;
             /*
        P/P   *  Method: void net.sourceforge.pebble.webservice.BloggerAPIHandler()
              */
        37  import org.apache.xmlrpc.XmlRpcException;
        38
        39  import java.util.*;
        40
        41  /**
        42   * A handler for the Blogger API (accessed via XML-RPC).
        43   *
        44   * @author    Simon Brown
        45   */
        46  public class BloggerAPIHandler extends AbstractAPIHandler {
        47
        48    static final String URL = "url";
        49    static final String BLOG_ID = "blogid";
        50    static final String BLOG_NAME = "blogName";
        51    static final String DATE_CREATED = "dateCreated";
        52    static final String USER_ID = "userId";
        53    static final String POST_ID = "postid";
        54    static final String CONTENT = "content";
        55
        56    static final String TITLE_START_DELIMITER = "<title>";
        57    static final String TITLE_END_DELIMITER = "</title>";
        58    static final String CATEGORY_START_DELIMITER = "<category>";
        59    static final String CATEGORY_END_DELIMITER = "</category>";
        60    static final char BLOG_ID_SEPARATOR = '/';
        61
        62    /** the log used by this class */
        63    private static Log log = LogFactory.getLog(BloggerAPIHandler.class);
        64
        65    /**
        66     * Gets information about the user logging in.
        67     *
        68     * @param appkey    the client application key (ignored)
        69     * @param username  the username used for logging in via XML-RPC
        70     * @param password  the password used for logging in via XML-RPC
        71     * @return  a Hashtable containing user information
        72     * @throws XmlRpcException    if something goes wrong, including an authentication error
        73     */
               /*
        P/P     *  Method: Hashtable getUserInfo(String, String, String)
                *
                *  Preconditions:
                *    log != null
                *    this.authenticationManager != null
                *
                *  Postconditions:
                *    return_value == &new Hashtable(getUserInfo#2)
                *    new Hashtable(getUserInfo#2) num objects == 1
                */
        74    public Hashtable getUserInfo(String appkey, String username, String password) throws XmlRpcException {
        75      log.debug("BloggerAPI.getUserInfo(" +
        76          appkey + ", " +
        77          username + ", " +
        78          "********)");
        79
        80      authenticate((Blog)null, username, password);
        81      Hashtable ht = new Hashtable();
        82      ht.put("userid", username);
        83
        84      return ht;
        85    }
        86
        87    /**
        88     * Gets a list of the blogs that the specified user can edit. Pabble
        89     * only has the concept of a single blog.
        90     *
        91     * @param appkey    the client application key (ignored)
        92     * @param username  the username used for logging in via XML-RPC
        93     * @param password  the password used for logging in via XML-RPC
        94     * @return  a Vector of Hashtables (an array of structs) representing blogs
        95     * @throws XmlRpcException    if something goes wrong, including an authentication error
        96     */
               /*
        P/P     *  Method: Vector getUsersBlogs(String, String, String)
                *
                *  Preconditions:
                *    log != null
                *    (soft) this.authenticationManager != null
                *
                *  Presumptions:
                *    java.util.Iterator:next(...)@106 != null
                *    net.sourceforge.pebble.domain.BlogManager:getBlogs(...)@103 != null
                *    net.sourceforge.pebble.domain.BlogManager:getInstance(...)@103 != null
                *
                *  Postconditions:
                *    return_value == &new Vector(getUsersBlogs#2)
                *    new Vector(getUsersBlogs#2) num objects == 1
                *
                *  Test Vectors:
                *    java.util.Iterator:hasNext(...)@106: {1}, {0}
                */
        97    public Vector getUsersBlogs(String appkey, String username, String password) throws XmlRpcException {
        98      log.debug("BloggerAPI.getUsersBlogs(" +
        99          appkey + ", " +
       100          username + ", " +
       101          "********)");
       102
       103      Collection<Blog> blogs = BlogManager.getInstance().getBlogs();
       104      Vector usersBlogs = new Vector();
       105
       106      for (Blog blog : blogs) {
       107        try {
       108          authenticate(blog, username, password);
       109          Hashtable blogInfo = new Hashtable();
       110          blogInfo.put(URL, blog.getUrl());
       111          blogInfo.put(BLOG_ID, blog.getId());
       112          blogInfo.put(BLOG_NAME, blog.getName());
       113
       114          usersBlogs.add(blogInfo);
       115        } catch (XmlRpcAuthenticationException xmlrpcae) {
       116          // do nothing - means that they didn't authenticate against the blog
       117        }
       118      }
       119
       120      return usersBlogs;
       121    }
       122
       123    /**
       124     * Gets a list of the recent blog entries.
       125     *
       126     * @param appkey    the client application key (ignored)
       127     * @param blogid    the ID of the blog (ignored)
       128     * @param username  the username used for logging in via XML-RPC
       129     * @param password  the password used for logging in via XML-RPC
       130     * @param numberOfPosts   the number of posts to get
       131     * @return  a Vector of Hashtables (an array of structs) representing blog entries
       132     * @throws XmlRpcException    if something goes wrong, including an authentication error
       133     */
               /*
        P/P     *  Method: Vector getRecentPosts(String, String, String, String, int)
                *
                *  Preconditions:
                *    log != null
                *    this.authenticationManager != null
                *
                *  Presumptions:
                *    java.util.Iterator:next(...)@150 != null
                *    net.sourceforge.pebble.domain.Blog:getRecentBlogEntries(...)@145 != null
                *
                *  Postconditions:
                *    return_value == &new Vector(getRecentPosts#2)
                *    new Vector(getRecentPosts#2) num objects == 1
                *
                *  Test Vectors:
                *    java.util.Iterator:hasNext(...)@149: {1}, {0}
                */
       134    public Vector getRecentPosts(String appkey, String blogid, String username, String password, int numberOfPosts) throws XmlRpcException {
       135      log.debug("BloggerAPI.getRecentPosts(" +
       136          appkey + ", " +
       137          blogid + ", " +
       138          username + ", " +
       139          "********)");
       140
       141      Blog blog = getBlogWithBlogId(blogid);
       142      authenticate(blog, username, password);
       143
       144      Vector posts = new Vector();
       145      Collection coll = blog.getRecentBlogEntries(numberOfPosts);
       146
       147      Iterator it = coll.iterator();
       148      BlogEntry entry;
       149      while (it.hasNext()) {
       150        entry = (BlogEntry)it.next();
       151        posts.add(adaptBlogEntry(entry));
       152      }
       153
       154      return posts;
       155    }
       156
       157    /**
       158     * Gets an individual blog entry.
       159     *
       160     * @param appkey    the client application key (ignored)
       161     * @param postid    the ID of the blog (ignored)
       162     * @param username  the username used for logging in via XML-RPC
       163     * @param password  the password used for logging in via XML-RPC
       164     * @return  a Hashtable representing a blog entry
       165     * @throws XmlRpcException    if something goes wrong, including an authentication error
       166     */
               /*
        P/P     *  Method: Hashtable getPost(String, String, String, String)
                *
                *  Preconditions:
                *    log != null
                *    postid != null
                *    this.authenticationManager != null
                *
                *  Presumptions:
                *    net.sourceforge.pebble.domain.BlogService:getBlogEntry(...)@180 != null
                *
                *  Postconditions:
                *    return_value == &new Hashtable(adaptBlogEntry#1)
                *    new Hashtable(adaptBlogEntry#1) num objects == 1
                */
       167    public Hashtable getPost(String appkey, String postid, String username, String password) throws XmlRpcException {
       168      log.debug("BloggerAPI.getPost(" +
       169          appkey + ", " +
       170          postid + ", " +
       171          username + ", " +
       172          "********)");
       173
       174      Blog blog = getBlogWithPostId(postid);
       175      postid = getPostId(postid);
       176      authenticate(blog, username, password);
       177      BlogService service = new BlogService();
       178      BlogEntry entry = null;
       179      try {
       180        entry = service.getBlogEntry(blog, postid);
       181      } catch (BlogServiceException e) {
       182        throw new XmlRpcException(0, "Blog entry with ID of " + postid + " was not found.");
       183      }
       184
       185      if (entry != null) {
       186        return adaptBlogEntry(entry);
       187      } else {
       188        throw new XmlRpcException(0, "Blog entry with ID of " + postid + " was not found.");
       189      }
       190    }
       191
       192    /**
       193     * Creates a new blog entry.
       194     *
       195     * @param appkey    the client application key (ignored)
       196     * @param blogid    the ID of the blog (ignored)
       197     * @param username  the username used for logging in via XML-RPC
       198     * @param password  the password used for logging in via XML-RPC
       199     * @param content   the content of the new blog entry
       200     * @param publish   a flag to indicate whether the entry should be published
       201     * @return  a String representing the ID of the new blog entry
       202     * @throws XmlRpcException    if something goes wrong, including an authentication error
       203     */
               /*
        P/P     *  Method: String newPost(String, String, String, String, String, bool)
                *
                *  Preconditions:
                *    content != null
                *    log != null
                *    this.authenticationManager != null
                *
                *  Postconditions:
                *    return_value != null
                */
       204    public String newPost(String appkey, String blogid, String username, String password, String content, boolean publish) throws XmlRpcException {
       205      log.debug("BloggerAPI.newPost(" +
       206          appkey + ", " +
       207          blogid + ", " +
       208          username + ", " +
       209          "********, " +
       210          content + ", " +
       211          publish + ")");
       212
       213      try {
       214        Blog blog = getBlogWithBlogId(blogid);
       215        authenticate(blog, username, password);
       216
       217        BlogEntry blogEntry = new BlogEntry(blog);
       218        populateEntry(blogEntry, content, username);
       219        blogEntry.setPublished(publish);
       220
       221        BlogService service = new BlogService();
       222        service.putBlogEntry(blogEntry);
       223
       224        return formatPostId(blogid, blogEntry.getId());
       225      } catch (BlogServiceException be) {
       226        throw new XmlRpcException(0, be.getMessage());
       227      }
       228    }
       229
       230    /**
       231     * Edits an existing blog entry.
       232     *
       233     * @param appkey    the client application key (ignored)
       234     * @param postid    the ID of the blog entry to be edited
       235     * @param username  the username used for logging in via XML-RPC
       236     * @param password  the password used for logging in via XML-RPC
       237     * @param content   the new content of the new blog entry
       238     * @param publish   a flag to indicate whether the entry should be published
       239     *                  (this is ignored as all new entries are published)
       240     * @return  a boolean true value to signal success
       241     * @throws XmlRpcException    if something goes wrong, including an authentication error
       242     */
               /*
        P/P     *  Method: bool editPost(String, String, String, String, String, bool)
                *
                *  Preconditions:
                *    content != null
                *    log != null
                *    postid != null
                *    this.authenticationManager != null
                *
                *  Presumptions:
                *    net.sourceforge.pebble.domain.BlogService:getBlogEntry(...)@257 != null
                *
                *  Postconditions:
                *    return_value == 1
                */
       243    public boolean editPost(String appkey, String postid, String username, String password, String content, boolean publish) throws XmlRpcException {
       244      log.debug("BloggerAPI.editPost(" +
       245          appkey + ", " +
       246          postid + ", " +
       247          username + ", " +
       248          "********, " +
       249          content + ", " +
       250          publish + ")");
       251
       252      try {
       253        Blog blog = getBlogWithPostId(postid);
       254        postid = getPostId(postid);
       255        authenticate(blog, username, password);
       256        BlogService service = new BlogService();
       257        BlogEntry entry = service.getBlogEntry(blog, postid);
       258
       259        if (entry != null) {
       260          populateEntry(entry, content, username);
       261          entry.setPublished(publish);
       262          service.putBlogEntry(entry);
       263        } else {
       264          throw new XmlRpcException(0, "Blog entry with ID of " + postid + " was not found.");
       265        }
       266
       267        return true;
       268      } catch (BlogServiceException be) {
       269        throw new XmlRpcException(0, be.getMessage());
       270      }
       271    }
       272
       273    /**
       274     * Deletes an existing blog entry.
       275     *
       276     * @param appkey    the client application key (ignored)
       277     * @param postid    the ID of the blog entry to be edited
       278     * @param username  the username used for logging in via XML-RPC
       279     * @param password  the password used for logging in via XML-RPC
       280     * @param publish   a flag to indicate whether the entry should be published
       281     *                  (this is ignored)
       282     * @return  a boolean true value to signal success
       283     * @throws XmlRpcException    if something goes wrong, including an authentication error
       284     */
               /*
        P/P     *  Method: bool deletePost(String, String, String, String, bool)
                *
                *  Preconditions:
                *    log != null
                *    postid != null
                *    this.authenticationManager != null
                *
                *  Presumptions:
                *    net.sourceforge.pebble.domain.BlogService:getBlogEntry(...)@298 != null
                *
                *  Postconditions:
                *    return_value == 1
                */
       285    public boolean deletePost(String appkey, String postid, String username, String password, boolean publish) throws XmlRpcException {
       286      log.debug("BloggerAPI.deletePost(" +
       287          appkey + ", " +
       288          postid + ", " +
       289          username + ", " +
       290          "********, " +
       291          publish + ")");
       292
       293      try {
       294        Blog blog = getBlogWithPostId(postid);
       295        postid = getPostId(postid);
       296        authenticate(blog, username, password);
       297        BlogService service = new BlogService();
       298        BlogEntry blogEntry = service.getBlogEntry(blog, postid);
       299
       300        if (blogEntry != null) {
       301          service.removeBlogEntry(blogEntry);
       302          return true;
       303        } else {
       304          throw new XmlRpcException(0, "Blog entry with ID of " + postid + " was not found.");
       305        }
       306      } catch (BlogServiceException be) {
       307        throw new XmlRpcException(0, be.getMessage());
       308      }
       309    }
       310
       311    /**
       312     * Helper method to adapt a blog entry into an XML-RPC compatible struct.
       313     * Since the Blogger API doesn't support titles, the title is wrapped in
       314     * <title></title> tags.
       315     *
       316     * @param entry   the BlogEntry to adapt
       317     * @return  a Hashtable representing the major properties of the entry
       318     */
               /*
        P/P     *  Method: Hashtable adaptBlogEntry(BlogEntry)
                *
                *  Preconditions:
                *    entry != null
                *
                *  Presumptions:
                *    java.util.Iterator:next(...)@324 != null
                *    net.sourceforge.pebble.domain.BlogEntry:getBlog(...)@332 != null
                *    net.sourceforge.pebble.domain.BlogEntry:getCategories(...)@322 != null
                *
                *  Postconditions:
                *    return_value == &new Hashtable(adaptBlogEntry#1)
                *    new Hashtable(adaptBlogEntry#1) num objects == 1
                *
                *  Test Vectors:
                *    java.util.Iterator:hasNext(...)@323: {1}, {0}
                *    java.util.Iterator:hasNext(...)@326: {0}, {1}
                */
       319    private Hashtable adaptBlogEntry(BlogEntry entry) {
       320      Hashtable post = new Hashtable();
       321      String categories = "";
       322      Iterator it = entry.getCategories().iterator();
       323      while (it.hasNext()) {
       324        Category category = (Category)it.next();
       325        categories += category.getId();
       326        if (it.hasNext()) {
       327          categories += ",";
       328        }
       329      }
       330      post.put(DATE_CREATED, entry.getDate());
       331      post.put(USER_ID, entry.getAuthor());
       332      post.put(POST_ID, formatPostId(entry.getBlog().getId(), entry.getId()));
       333      post.put(CONTENT, TITLE_START_DELIMITER + entry.getTitle() + TITLE_END_DELIMITER
       334          + CATEGORY_START_DELIMITER + categories + CATEGORY_END_DELIMITER + entry.getBody());
       335
       336      return post;
       337    }
       338
       339    /**
       340     * Populates a given BlogEntry.
       341     *
       342     * @param entry     the BlogEntry to populate
       343     * @param content   the content (including title)
       344     * @param username  the author
       345     */
               /*
        P/P     *  Method: void populateEntry(BlogEntry, String, String)
                *
                *  Preconditions:
                *    content != null
                *    entry != null
                *
                *  Presumptions:
                *    categories.length@371 <= 232-1
                *    categories[i]@371 != null
                *    net.sourceforge.pebble.domain.BlogEntry:getBlog(...)@373 != null
                *
                *  Test Vectors:
                *    java.lang.String:equals(...)@370: {1}, {0}
                *    java.lang.String:indexOf(...)@350: {-231..-1}, {0..232-1}
                *    java.lang.String:indexOf(...)@358: {-231..-1}, {0..232-1}
                *    net.sourceforge.pebble.domain.Blog:getCategory(...)@373: Addr_Set{null}, Inverse{null}
                */
       346    private void populateEntry(BlogEntry entry, String content, String username) {
       347      String title = "";
       348      String category = "";
       349
       350      if (content.indexOf(TITLE_START_DELIMITER) > -1 && content.indexOf(TITLE_END_DELIMITER) > -1) {
       351        content = content.substring(TITLE_START_DELIMITER.length());
       352        int index = content.indexOf(TITLE_END_DELIMITER);
       353        title = content.substring(0, index);
       354        content = content.substring(index);
       355        content = content.substring(TITLE_END_DELIMITER.length());
       356      }
       357
       358      if (content.indexOf(CATEGORY_START_DELIMITER) > -1 && content.indexOf(CATEGORY_END_DELIMITER) > -1) {
       359        content = content.substring(CATEGORY_START_DELIMITER.length());
       360        int index = content.indexOf(CATEGORY_END_DELIMITER);
       361        category = content.substring(0, index);
       362        content = content.substring(index);
       363        content = content.substring(CATEGORY_END_DELIMITER.length());
       364      }
       365
       366      entry.setTitle(title);
       367      entry.setBody(content);
       368      entry.setAuthor(username);
       369
    +  370      if (category != null && !category.trim().equals("")) {
       371        String[] categories = category.split(",");
       372        for (int i = 0; i < categories.length; i++) {
       373          Category c = entry.getBlog().getCategory(categories[i].trim());
       374           if (c != null) {
       375             entry.addCategory(c);
       376           }
       377        }
       378      }
       379    }
       380
       381    /**
       382     * Gets the specified template type for a blog - not supported by Pebble.
       383     *
       384     * @param appkey    the client application key (ignored)
       385     * @param blogid    the ID of the blog
       386     * @param username  the username used for logging in via XML-RPC
       387     * @param password  the password used for logging in via XML-RPC
       388     * @param templateType  the type of template to retrieve
       389     * @return  the text of the specified template
       390     * @throws XmlRpcException
       391     */
               /*
        P/P     *  Method: String getTemplate(String, String, String, String, String)
                *    getTemplate fails for all possible inputs
                */
       392    public String getTemplate(String appkey, String blogid, String username, String password, String templateType) throws XmlRpcException {
    +  393      log.debug("BloggerAPI.getTemplate(" +
       394          appkey + ", " +
       395          blogid + ", " +
       396          username + ", " +
       397          "********, " +
       398          templateType + ")");
       399
       400      throw new XmlRpcException(0, "getTemplate is not supported by Pebble.");
       401    }
       402
       403    /**
       404     * Sets the specified template type for a blog - not supported by Pebble.
       405     *
       406     * @param appkey    the client application key (ignored)
       407     * @param blogid    the ID of the blog
       408     * @param username  the username used for logging in via XML-RPC
       409     * @param password  the password used for logging in via XML-RPC
       410     * @param template  the new text of the template
       411     * @param templateType  the type of template to retrieve
       412     * @return  true if setting the template was successful, false otherwise
       413     * @throws XmlRpcException
       414     */
               /*
        P/P     *  Method: bool setTemplate(String, String, String, String, String, String)
                *    setTemplate fails for all possible inputs
                */
       415    public boolean setTemplate(String appkey, String blogid, String username, String password, String template, String templateType) throws XmlRpcException {
    +  416      log.debug("BloggerAPI.setTemplate(" +
       417          appkey + ", " +
       418          blogid + ", " +
       419          username + ", " +
       420          "********, " +
       421          template + ", " +
       422          templateType + ")");
       423
       424      throw new XmlRpcException(0, "setTemplate is not supported by Pebble.");
       425    }
       426
       427    /**
       428     * Adds a category to a blog entry - this isn't a standard Blogger API method.
       429     *
       430     * @param appkey    the client application key (ignored)
       431     * @param postid    the ID of the blog entry to be edited
       432     * @param username  the username used for logging in via XML-RPC
       433     * @param password  the password used for logging in via XML-RPC
       434     * @param category  the category ID
       435     * @return  a boolean true value to signal success
       436     * @throws XmlRpcException    if something goes wrong, including an authentication error
       437     */
               /*
        P/P     *  Method: bool addCategory(String, String, String, String, String)
                *
                *  Preconditions:
                *    log != null
                *    postid != null
                *    this.authenticationManager != null
                *
                *  Presumptions:
                *    net.sourceforge.pebble.domain.BlogEntry:getBlog(...)@454 != null
                *    net.sourceforge.pebble.domain.BlogService:getBlogEntry(...)@451 != null
                *
                *  Postconditions:
                *    init'ed(return_value)
                *
                *  Test Vectors:
                *    net.sourceforge.pebble.domain.Blog:getCategory(...)@454: Addr_Set{null}, Inverse{null}
                */
       438    public boolean addCategory(String appkey, String postid, String username, String password, String category) throws XmlRpcException {
       439      log.debug("BloggerAPI.addCategory(" +
       440          appkey + ", " +
       441          postid + ", " +
       442          username + ", " +
       443          "********, " +
       444          category + ")");
       445
       446      try {
       447        Blog blog = getBlogWithPostId(postid);
       448        postid = getPostId(postid);
       449        authenticate(blog, username, password);
       450        BlogService service = new BlogService();
       451        BlogEntry entry = service.getBlogEntry(blog, postid);
       452
       453        if (entry != null) {
       454          Category c = entry.getBlog().getCategory(category);
       455          if (c != null) {
       456            entry.addCategory(c);
       457            service.putBlogEntry(entry);
       458
       459            return true;
       460          }
       461        } else {
       462          throw new XmlRpcException(0, "Blog entry with ID of " + postid + " was not found.");
       463        }
       464
       465        return false;
       466      } catch (BlogServiceException be) {
       467        throw new XmlRpcException(0, be.getMessage());
       468      }
       469    }
       470
       471  }

SofCheck Inspector Build Version : 2.22510

bloggerapihandler.java | 2010-Jun-25 19:40:32
bloggerapihandler.class | 2010-Jul-19 20:23:38