You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@abdera.apache.org by jm...@apache.org on 2007/08/21 02:51:29 UTC

svn commit: r567888 [2/4] - in /incubator/abdera/java/trunk: ./ build/ extensions/ extensions/gdata/ extensions/gdata/src/ extensions/gdata/src/main/ extensions/gdata/src/main/java/ extensions/gdata/src/main/java/org/ extensions/gdata/src/main/java/org...

Added: incubator/abdera/java/trunk/extensions/geo/src/main/java/org/apache/abdera/ext/geo/Polygon.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/extensions/geo/src/main/java/org/apache/abdera/ext/geo/Polygon.java?rev=567888&view=auto
==============================================================================
--- incubator/abdera/java/trunk/extensions/geo/src/main/java/org/apache/abdera/ext/geo/Polygon.java (added)
+++ incubator/abdera/java/trunk/extensions/geo/src/main/java/org/apache/abdera/ext/geo/Polygon.java Mon Aug 20 17:51:20 2007
@@ -0,0 +1,78 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.ext.geo;
+
+public class Polygon 
+  extends Multiple {
+
+  private static final long serialVersionUID = 5387230171535985909L;
+
+  public Polygon() {
+    super();
+  }
+
+  public Polygon(Multiple multiple) {
+    super(multiple);
+    verify();
+  }
+  
+  public Polygon(Point point) {
+    super(point);
+    verify();
+  }
+  
+  public Polygon(Coordinate... coordinates) {
+    super(coordinates);
+    verify();
+  }
+
+  public Polygon(Coordinates coordinates) {
+    super(coordinates);
+    verify();
+  }
+
+  public Polygon(String value) {
+    super(value);
+    verify();
+  }
+
+  public Polygon(Multiple... multiples) {
+    super(multiples);
+    verify();
+  }
+
+  public Polygon(Point... points) {
+    super(points);
+    verify();
+  }
+
+  public Polygon(double... values) {
+    super(values);
+    verify();
+  }
+  
+  @Override
+  public void setCoordinates(Coordinates coordinates) {
+    super.setCoordinates(coordinates);
+    verify();
+  }
+
+  public void verify() {
+    super.verify179Rule();
+  }
+}

Added: incubator/abdera/java/trunk/extensions/geo/src/main/java/org/apache/abdera/ext/geo/Position.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/extensions/geo/src/main/java/org/apache/abdera/ext/geo/Position.java?rev=567888&view=auto
==============================================================================
--- incubator/abdera/java/trunk/extensions/geo/src/main/java/org/apache/abdera/ext/geo/Position.java (added)
+++ incubator/abdera/java/trunk/extensions/geo/src/main/java/org/apache/abdera/ext/geo/Position.java Mon Aug 20 17:51:20 2007
@@ -0,0 +1,132 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.ext.geo;
+
+import java.io.Serializable;
+
+public abstract class Position 
+  implements Serializable,
+             Cloneable,
+             Comparable<Position> {
+
+  public static final String DEFAULT_FEATURE_TYPE_TAG = "location";
+  public static final String DEFAULT_RELATIONSHIP_TAG = "is-located-at";
+  
+  protected String featureTypeTag;
+  protected String relationshipTag;
+  protected Double elevation;
+  protected Double floor;
+  protected Double radius;
+  
+  public Double getElevation() {
+    return elevation;
+  }
+  public void setElevation(Double elevation) {
+    this.elevation = elevation;
+  }
+  public String getFeatureTypeTag() {
+    return featureTypeTag;
+  }
+  public void setFeatureTypeTag(String featureTypeTag) {
+    this.featureTypeTag = featureTypeTag;
+  }
+  public Double getFloor() {
+    return floor;
+  }
+  public void setFloor(Double floor) {
+    this.floor = floor;
+  }
+  public Double getRadius() {
+    return radius;
+  }
+  public void setRadius(Double radius) {
+    this.radius = radius;
+  }
+  public String getRelationshipTag() {
+    return relationshipTag;
+  }
+  public void setRelationshipTag(String relationshipTag) {
+    this.relationshipTag = relationshipTag;
+  }
+  
+  @Override
+  public int hashCode() {
+    final int PRIME = 31;
+    //int result = super.hashCode();
+    int result = this.getClass().hashCode();
+    result = PRIME * result + ((elevation == null) ? 0 : elevation.hashCode());
+    result = PRIME * result + ((featureTypeTag == null) ? DEFAULT_FEATURE_TYPE_TAG.hashCode() : featureTypeTag.hashCode());
+    result = PRIME * result + ((floor == null) ? 0 : floor.hashCode());
+    result = PRIME * result + ((radius == null) ? 0 : radius.hashCode());
+    result = PRIME * result + ((relationshipTag == null) ? DEFAULT_RELATIONSHIP_TAG.hashCode() : relationshipTag.hashCode());
+    return result;
+  }
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj)
+      return true;
+    if (getClass() != obj.getClass())
+      return false;
+    final Position other = (Position) obj;
+    if (elevation == null) {
+      if (other.elevation != null)
+        return false;
+    } else if (!elevation.equals(other.elevation))
+      return false;
+    if (featureTypeTag == null) {
+      if (other.featureTypeTag != null && 
+          !other.featureTypeTag.equalsIgnoreCase(DEFAULT_FEATURE_TYPE_TAG))
+        return false;
+    } else {
+      String s = other.featureTypeTag != null ? 
+        other.featureTypeTag : DEFAULT_FEATURE_TYPE_TAG;
+      if (!featureTypeTag.equalsIgnoreCase(s))
+        return false;
+    }
+    if (floor == null) {
+      if (other.floor != null)
+        return false;
+    } else if (!floor.equals(other.floor))
+      return false;
+    if (radius == null) {
+      if (other.radius != null)
+        return false;
+    } else if (!radius.equals(other.radius))
+      return false;
+    if (relationshipTag == null) {
+      if (other.relationshipTag != null && 
+          !other.relationshipTag.equalsIgnoreCase(DEFAULT_RELATIONSHIP_TAG))
+        return false;
+    } else {
+      String s = other.relationshipTag != null ? 
+          other.relationshipTag : DEFAULT_RELATIONSHIP_TAG;
+      if (!relationshipTag.equalsIgnoreCase(s))
+        return false;
+    }
+    return true;
+  }
+  
+  @SuppressWarnings("unchecked")
+  public <T extends Position>T clone() {
+    try {
+      return (T)super.clone();
+    } catch (CloneNotSupportedException e) {
+      return null; // should never happen
+    }
+  }
+}

Added: incubator/abdera/java/trunk/extensions/json/pom.xml
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/extensions/json/pom.xml?rev=567888&view=auto
==============================================================================
--- incubator/abdera/java/trunk/extensions/json/pom.xml (added)
+++ incubator/abdera/java/trunk/extensions/json/pom.xml Mon Aug 20 17:51:20 2007
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  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.  For additional information regarding
+  copyright in this work, please see the NOTICE file in the top level
+  directory of this distribution. -->
+<project 
+  xmlns="http://maven.apache.org/POM/4.0.0" 
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.apache.abdera</groupId>
+    <artifactId>abdera</artifactId>
+    <version>0.3.0-incubating-SNAPSHOT</version>
+  </parent>  
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>abdera-extensions-json</artifactId>
+  <packaging>jar</packaging>
+  <name>Abdera Extensions - JSON</name>
+  <version>0.3.0-incubating-SNAPSHOT</version>
+  <description>Atom Specification Extensions - JSON</description>
+  <inceptionYear>2006</inceptionYear>
+  <url>http://incubator.apache.org/abdera</url>
+  <scm>
+    <connection>scm:svn:http://svn.apache.org/repos/asf/incubator/abdera/java/trunk/extensions/json</connection>
+    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/incubator/abdera/java/trunk/extensions/json</developerConnection>
+    <url>http://svn.apache.org/repos/asf/incubator/abdera/java/trunk/extensions/json</url>
+  </scm>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.abdera</groupId>
+      <artifactId>abdera-core</artifactId>
+      <version>0.3.0-incubating-SNAPSHOT</version>
+      <scope>compile</scope>
+    </dependency>    
+    <dependency>
+      <groupId>org.apache.abdera</groupId>
+      <artifactId>abdera-parser</artifactId>
+      <version>0.3.0-incubating-SNAPSHOT</version>
+      <scope>compile</scope>
+    </dependency>   
+    <dependency>
+      <groupId>org.apache.abdera</groupId>
+      <artifactId>abdera-protocol</artifactId>
+      <version>0.3.0-incubating-SNAPSHOT</version>
+      <scope>compile</scope>
+    </dependency>  
+    <dependency>
+      <groupId>org.apache.abdera</groupId>
+      <artifactId>abdera-client</artifactId>
+      <version>0.3.0-incubating-SNAPSHOT</version>
+      <scope>compile</scope>
+    </dependency>  
+    <dependency>
+      <groupId>org.apache.abdera</groupId>
+      <artifactId>json</artifactId>
+      <version>1.0</version>
+      <scope>compile</scope>
+    </dependency>   
+    <dependency>
+      <groupId>org.apache.ws.commons.axiom</groupId>
+      <artifactId>axiom-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.ws.commons.axiom</groupId>
+      <artifactId>axiom-impl</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>stax</groupId>
+      <artifactId>stax-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.woodstox</groupId>
+      <artifactId>wstx-asl</artifactId>
+    </dependency> 
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+    </dependency>
+  </dependencies>
+</project>

Added: incubator/abdera/java/trunk/extensions/json/src/main/java/org/apache/abdera/ext/json/JSONWriter.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/extensions/json/src/main/java/org/apache/abdera/ext/json/JSONWriter.java?rev=567888&view=auto
==============================================================================
--- incubator/abdera/java/trunk/extensions/json/src/main/java/org/apache/abdera/ext/json/JSONWriter.java (added)
+++ incubator/abdera/java/trunk/extensions/json/src/main/java/org/apache/abdera/ext/json/JSONWriter.java Mon Aug 20 17:51:20 2007
@@ -0,0 +1,298 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  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.  For additional information regarding
+ * copyright in this work, please see the NOTICE file in the top level
+ * directory of this distribution.
+ */
+
+package org.apache.abdera.ext.json;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+import org.apache.abdera.model.Base;
+import org.apache.abdera.model.Category;
+import org.apache.abdera.model.Collection;
+import org.apache.abdera.model.Content;
+import org.apache.abdera.model.Document;
+import org.apache.abdera.model.Entry;
+import org.apache.abdera.model.Feed;
+import org.apache.abdera.model.Generator;
+import org.apache.abdera.model.Link;
+import org.apache.abdera.model.Person;
+import org.apache.abdera.model.Service;
+import org.apache.abdera.model.Workspace;
+import org.apache.abdera.model.Content.Type;
+import org.apache.abdera.util.AbstractNamedWriter;
+import org.apache.abdera.util.AbstractWriterOptions;
+import org.apache.abdera.writer.NamedWriter;
+import org.apache.abdera.writer.WriterOptions;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+public class JSONWriter
+  extends AbstractNamedWriter
+  implements NamedWriter {
+
+  public static final String NAME = "json";
+
+  public static final String[] FORMATS = {
+    "application/json",
+    "application/javascript",
+    "application/ecmascript",
+    "text/javascript",
+    "text/ecmascript"
+  };
+  
+  public JSONWriter() {
+    super(NAME,FORMATS);
+  }
+  
+  public String getName() {
+    return NAME;
+  }
+
+  public Object write(Base base, WriterOptions options) throws IOException {
+    try {
+      return toJSON(base).toString();
+    } catch (Exception e) {
+      throw new IOException(e.getMessage());
+    }
+  }
+
+  public void writeTo(Base base, OutputStream out, WriterOptions options) throws IOException {
+    try {
+      Object result = toJSON(base);
+      out.write(result.toString().getBytes());
+      if (options.getAutoClose()) out.close();
+    } catch (Exception e) {
+      throw new IOException(e.getMessage());
+    }
+  }
+
+  public void writeTo(Base base, java.io.Writer out, WriterOptions options) throws IOException {
+    try {
+      Object result = toJSON(base);
+      out.write(result.toString());
+      if (options.getAutoClose()) out.close();
+    } catch (Exception e) {
+      throw new IOException(e.getMessage());
+    }
+  }
+
+  public static Object toJSON(Object object) throws Exception {
+    if (object instanceof Feed) {
+      return toJSON((Feed) object);
+    } else if (object instanceof Entry) {
+      return toJSON((Entry) object);
+    } else if (object instanceof Service) {
+      return toJSON((Service) object);
+    } else if (object instanceof Document) {
+      return toJSON(((Document) object).getRoot());
+    }
+    return new IllegalArgumentException("Element is not supported by JSONWriter.");
+  }
+
+  public static JSONObject toJSON(Entry entry) throws Exception {
+    JSONObject jsentry = new JSONObject();
+    if (entry.getTitle() != null)
+      jsentry.put("title", entry.getTitle());
+
+    if (entry.getSummary() != null)
+      jsentry.put("summary", entry.getSummary());
+
+    if (entry.getId() != null)
+      jsentry.put("id", entry.getId().toString());
+
+    if (entry.getUpdated() != null)
+      jsentry.put("updated", entry.getUpdated().toString());
+
+    if (entry.getPublished() != null)
+      jsentry.put("published", entry.getPublished().toString());
+
+    jsentry.put("authors", personsToJSON(entry.getAuthors()));
+
+    jsentry.put("contributors", personsToJSON(entry.getContributors()));
+
+    jsentry.put("categories", categoriesToJSON(entry.getCategories()));
+
+    jsentry.put("links", linksToJSON(entry.getLinks()));
+
+    JSONObject jscontent = new JSONObject();
+    if (entry.getContentElement() != null) {
+
+      Content content = entry.getContentElement();
+      Type type = entry.getContentType();
+      if (type.equals(Content.Type.HTML) || type.equals(Content.Type.XHTML) || type.equals(Content.Type.TEXT)) {
+        jscontent.put("type", type.toString().toLowerCase());
+      } else {
+        jscontent.put("type", content.getMimeType().toString());
+      }
+      jscontent.put("value", content.getValue());;
+      jsentry.put("content", jscontent);
+    }
+
+    return jsentry;
+  }
+
+  public static JSONObject toJSON(Feed feed) throws Exception {
+    JSONObject jsfeed = new JSONObject();
+
+    if (feed.getGenerator() != null) {
+      Generator gen = feed.getGenerator();
+      JSONObject jsgen = new JSONObject();
+      jsgen.put("uri", gen.getUri().toString());
+      jsgen.put("value", gen.getText());
+    }
+
+    if (feed.getTitle() != null) {
+      jsfeed.put("title", feed.getTitle());
+    }
+
+    if (feed.getSubtitle() != null) {
+      jsfeed.put("subtitle", feed.getSubtitle());
+    }
+
+    if (feed.getId() != null) {
+      jsfeed.put("id", feed.getId().toString());
+    }
+
+    if (feed.getRights() != null) {
+      jsfeed.put("rights", feed.getRights());
+    }
+
+    if (feed.getLogo() != null) {
+      jsfeed.put("logo", feed.getLogo().toString());
+    }
+
+    if (feed.getIcon() != null) {
+      jsfeed.put("icon", feed.getIcon().toString());
+    }
+
+    if (feed.getUpdatedString() != null) {
+      jsfeed.put("updated", feed.getUpdatedString());
+    }
+
+    jsfeed.put("authors", personsToJSON(feed.getAuthors()));
+
+    jsfeed.put("contributors", personsToJSON(feed.getContributors()));
+
+    jsfeed.put("categories", categoriesToJSON(feed.getCategories()));
+
+    jsfeed.put("links", linksToJSON(feed.getLinks()));
+
+    JSONArray jsentries = new JSONArray();
+    List<Entry> entries = feed.getEntries();
+    for (Entry entry : entries) {
+      jsentries.put(toJSON(entry));
+    }
+
+    jsfeed.put("entries", jsentries);
+
+    return jsfeed;
+  }
+
+  public static JSONObject toJSON(Service service) throws Exception {
+    JSONObject jssvc = new JSONObject();
+    JSONArray jsworkspaces = new JSONArray();
+    List<Workspace> workspaces = service.getWorkspaces();
+    for (Workspace workspace : workspaces) {
+      JSONObject jsworkspace = new JSONObject();
+      JSONArray jscollections = new JSONArray();
+      jsworkspace.put("title", workspace.getTitle());
+      List<Collection> collections = workspace.getCollections();
+      for (Collection collection : collections) {
+        JSONObject jscollection = new JSONObject();
+        JSONArray jsaccepts = new JSONArray();
+        String[] accepts = collection.getAccept();
+        for (String accept : accepts) {
+          jsaccepts.put(accept);
+        }
+        jscollection.put("href", collection.getHref().toString());
+        jscollection.put("accept", jsaccepts);
+        jscollections.put(jscollection);
+      }
+      jsworkspace.put("collections", jscollections);
+      jsworkspaces.put(jsworkspace);
+    }
+    jssvc.put("workspaces", jsworkspaces);
+
+    return jssvc;
+  }
+
+  private static JSONArray categoriesToJSON(List<Category> categories) throws JSONException {
+    JSONArray jscategories = new JSONArray();
+    for (Category category : categories) {
+      if (category.getScheme() != null || category.getLabel() != null || category.getTerm() != null) {
+        JSONObject jscategory = new JSONObject();
+        if (category.getScheme() != null)
+          jscategory.put("scheme", category.getScheme().toString());
+
+        if (category.getTerm() != null)
+          jscategory.put("term", category.getTerm());
+
+        if (category.getLabel() != null)
+          jscategory.put("label", category.getLabel());
+        jscategories.put(jscategory);
+      }
+    }
+    return jscategories;
+  }
+
+  private static JSONArray personsToJSON(List<Person> persons) throws JSONException {
+    JSONArray jspersons = new JSONArray();
+    for (Person p : persons) {
+      if (p.getName() != null || p.getUri() != null || p.getEmail() != null) {
+        JSONObject jsperson = new JSONObject();
+        if (p.getName() != null)
+          jsperson.put("name", p.getName());
+        if (p.getUri() != null)
+          jsperson.put("uri", p.getUri().toString());
+        if (p.getEmail() != null)
+          jsperson.put("email", p.getEmail());
+        jspersons.put(jsperson);
+      }
+    }
+    return jspersons;
+  }
+
+  private static JSONArray linksToJSON(List<Link> links) throws JSONException {
+    JSONArray jslinks = new JSONArray();
+    for (Link link : links) {
+      JSONObject jslink = new JSONObject();
+      if (link.getHref() != null) {
+        jslink.put("href", link.getHref().toString());
+
+        if (link.getRel() != null)
+          jslink.put("rel", link.getRel());
+
+        if (link.getMimeType() != null)
+          jslink.put("type", link.getMimeType().getBaseType());
+
+        if (link.getHrefLang() != null)
+          jslink.put("hreflang", link.getHrefLang());
+      }
+      jslinks.put(jslink);
+    }
+    return jslinks;
+  }
+
+  @Override
+  protected WriterOptions initDefaultWriterOptions() {
+    return new AbstractWriterOptions() {};
+  }
+
+}

Added: incubator/abdera/java/trunk/extensions/json/src/main/resources/META-INF/services/org.apache.abdera.writer.NamedWriter
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/extensions/json/src/main/resources/META-INF/services/org.apache.abdera.writer.NamedWriter?rev=567888&view=auto
==============================================================================
--- incubator/abdera/java/trunk/extensions/json/src/main/resources/META-INF/services/org.apache.abdera.writer.NamedWriter (added)
+++ incubator/abdera/java/trunk/extensions/json/src/main/resources/META-INF/services/org.apache.abdera.writer.NamedWriter Mon Aug 20 17:51:20 2007
@@ -0,0 +1 @@
+org.apache.abdera.ext.json.JSONWriter
\ No newline at end of file

Added: incubator/abdera/java/trunk/extensions/main/pom.xml
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/extensions/main/pom.xml?rev=567888&view=auto
==============================================================================
--- incubator/abdera/java/trunk/extensions/main/pom.xml (added)
+++ incubator/abdera/java/trunk/extensions/main/pom.xml Mon Aug 20 17:51:20 2007
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  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.  For additional information regarding
+  copyright in this work, please see the NOTICE file in the top level
+  directory of this distribution. -->
+<project 
+  xmlns="http://maven.apache.org/POM/4.0.0" 
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+  <parent>
+    <groupId>org.apache.abdera</groupId>
+    <artifactId>abdera</artifactId>
+    <version>0.3.0-incubating-SNAPSHOT</version>
+  </parent>  
+  <modelVersion>4.0.0</modelVersion>
+  <artifactId>abdera-extensions-main</artifactId>
+  <packaging>jar</packaging>
+  <name>Abdera Extensions - Main</name>
+  <version>0.3.0-incubating-SNAPSHOT</version>
+  <description>Atom Specification Extensions - Main</description>
+  <inceptionYear>2006</inceptionYear>
+  <url>http://incubator.apache.org/abdera</url>
+  <scm>
+    <connection>scm:svn:http://svn.apache.org/repos/asf/incubator/abdera/java/trunk/extensions/main</connection>
+    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/incubator/abdera/java/trunk/extensions/main</developerConnection>
+    <url>http://svn.apache.org/repos/asf/incubator/abdera/java/trunk/extensions/main</url>
+  </scm>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.abdera</groupId>
+      <artifactId>abdera-core</artifactId>
+      <version>0.3.0-incubating-SNAPSHOT</version>
+      <scope>compile</scope>
+    </dependency>    
+    <dependency>
+      <groupId>org.apache.abdera</groupId>
+      <artifactId>abdera-parser</artifactId>
+      <version>0.3.0-incubating-SNAPSHOT</version>
+      <scope>compile</scope>
+    </dependency>   
+    <dependency>
+      <groupId>org.apache.abdera</groupId>
+      <artifactId>abdera-protocol</artifactId>
+      <version>0.3.0-incubating-SNAPSHOT</version>
+      <scope>compile</scope>
+    </dependency>  
+    <dependency>
+      <groupId>org.apache.abdera</groupId>
+      <artifactId>abdera-client</artifactId>
+      <version>0.3.0-incubating-SNAPSHOT</version>
+      <scope>compile</scope>
+    </dependency>  
+    <dependency>
+      <groupId>org.apache.abdera</groupId>
+      <artifactId>json</artifactId>
+      <version>1.0</version>
+      <scope>compile</scope>
+    </dependency>   
+    <dependency>
+      <groupId>org.apache.ws.commons.axiom</groupId>
+      <artifactId>axiom-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.ws.commons.axiom</groupId>
+      <artifactId>axiom-impl</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>stax</groupId>
+      <artifactId>stax-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.woodstox</groupId>
+      <artifactId>wstx-asl</artifactId>
+    </dependency> 
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+    </dependency>
+  </dependencies>
+</project>

Added: incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/bidi/BidiHelper.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/bidi/BidiHelper.java?rev=567888&view=auto
==============================================================================
--- incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/bidi/BidiHelper.java (added)
+++ incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/bidi/BidiHelper.java Mon Aug 20 17:51:20 2007
@@ -0,0 +1,308 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.ext.bidi;
+
+import java.text.AttributedString;
+import java.text.Bidi;
+import java.util.Locale;
+
+import javax.xml.namespace.QName;
+
+import org.apache.abdera.model.Base;
+import org.apache.abdera.model.Element;
+import org.apache.abdera.i18n.io.CharUtils;
+import org.apache.abdera.i18n.lang.Lang;
+
+/**
+ * <p>This is (hopefully) temporary.  Ideally, this would be wrapped into the 
+ * core model API so that the bidi stuff is handled seamlessly.  There are 
+ * still details being worked out on the Atom WG list and it's likely that
+ * at least one other impl (mozilla) will do something slightly different.</p>
+ * 
+ * <p>Based on http://www.ietf.org/internet-drafts/draft-snell-atompub-bidi-04.txt</p>
+ * 
+ * <p>Example:</p>
+ * <pre>
+ *   &lt;feed xmlns="http://www.w3.org/2005/Atom" dir="rtl">
+ *     ...
+ *   &lt;/feed>
+ * </pre>
+ * 
+ * <p>The getBidi___ elements use the in-scope direction to wrap the text with 
+ * the appropriate Unicode control characters. e.g. if dir="rtl", the text is
+ * wrapped with the RLE and PDF controls.  If the text already contains the 
+ * control chars, the dir attribute is ignored.</p>
+ * 
+ * <pre>
+ *    org.apache.abdera.Abdera abdera = new org.apache.abdera.Abdera();
+ *    org.apache.abdera.model.Feed feed = abdera.getFactory().newFeed();
+ *    feed.setAttributeValue("dir", "rtl");
+ *    feed.setTitle("Testing");
+ *    feed.addCategory("foo");
+ *    
+ *    System.out.println(
+ *      BidiHelper.getBidiElementText(
+ *        feed.getTitleElement()));
+ *    System.out.println(
+ *      BidiHelper.getBidiAttributeValue(
+ *        feed.getCategories().get(0),"term"));
+ * </pre>
+ * 
+ */
+public final class BidiHelper {
+
+  private static final QName DIR = new QName("dir");
+  
+  BidiHelper() {}
+  
+  public enum Direction { UNSPECIFIED, LTR, RTL};
+  
+  /**
+   * Set the value of dir attribute
+   */
+  public static <T extends Element>void setDirection(
+    Direction direction, 
+    T element) {
+      if (direction != Direction.UNSPECIFIED)
+        element.setAttributeValue(
+          DIR, 
+          direction.toString().toLowerCase());
+      else if (direction == Direction.UNSPECIFIED)
+        element.setAttributeValue(DIR,"");
+      else if (direction == null)
+        element.removeAttribute(DIR);
+  }
+  
+  /**
+   * Get the in-scope direction for an element.
+   */
+  public static <T extends Element>Direction getDirection(T element) {
+    Direction direction = Direction.UNSPECIFIED;
+    String dir = element.getAttributeValue("dir");
+    if (dir != null && dir.length() > 0)
+      direction = Direction.valueOf(dir.toUpperCase());
+    else if (dir == null) {
+      // if the direction is unspecified on this element, 
+      // let's see if we've inherited it
+      Base parent = element.getParentElement(); 
+      if (parent != null && 
+          parent instanceof Element)
+        direction = getDirection((Element)parent);
+    }
+    return direction;
+  }
+  
+  /**
+   * Return the specified text with appropriate Unicode Control Characters given
+   * the specified Direction.
+   * @param direction The Directionality of the text
+   * @param text The text to wrap within Unicode Control Characters
+   * @return The directionally-wrapped text  
+   */
+  public static String getBidiText(Direction direction, String text) {
+    switch (direction) {
+      case LTR: return CharUtils.bidiLRE(text);
+      case RTL: return CharUtils.bidiRLE(text);
+      default:  return text;
+    }
+  }
+  
+  /**
+   * Return the textual content of a child element using the in-scope directionality
+   * @param element The parent element
+   * @param child The XML QName of the child element
+   * @return The directionally-wrapped text of the child element
+   */
+  public static <T extends Element>String getBidiChildText(T element, QName child) {
+    Element el = element.getFirstChild(child);
+    return (el != null) ? getBidiText(getDirection(el),el.getText()) : null;
+  }
+  
+  /**
+   * Return the textual content of the specified element
+   * @param element An element containing directionally-sensitive text
+   * @return The directionally-wrapped text of the element
+   */
+  public static <T extends Element>String getBidiElementText(T element) {
+    return getBidiText(getDirection(element),element.getText());
+  }
+  
+  /**
+   * Return the text content of the specified attribute using the in-scope directionality
+   * @param element The parent element
+   * @param name the name of the attribute
+   * @return The directionally-wrapped text of the attribute
+   */
+  public static <T extends Element>String getBidiAttributeValue(T element, String name) {
+    return getBidiText(getDirection(element),element.getAttributeValue(name));
+  }
+  
+  /**
+   * Return the text content of the specified attribute using the in-scope directionality
+   * @param element The parent element
+   * @param name the name of the attribute
+   * @return The directionally-wrapped text of the attribute
+   */
+  public static <T extends Element>String getBidiAttributeValue(T element, QName name) {
+    return getBidiText(getDirection(element),element.getAttributeValue(name));
+  }
+  
+  
+  /**
+   * Attempt to guess the base direction using the in-scope language.  
+   * Implements the method used by Internet Explorer 7's feed view
+   * documented here: http://blogs.msdn.com/rssteam/archive/2007/05/17/reading-feeds-in-right-to-left-order.aspx.
+   * 
+   * This algorithm differs slightly from the method documented in that the 
+   * primary language tag is case insensitive.
+   * 
+   * If the language tag is not specified, then the default Locale is used to 
+   * determine the direction.
+   * 
+   * If the dir attribute is specified, the direction will be determine using it's value 
+   * instead of the language
+   */
+  public static <T extends Element>Direction guessDirectionFromLanguage(T element) {
+    return guessDirectionFromLanguage(element, false);
+  }
+  
+  /**
+   * Attempt to guess the base direction using the in-scope language.  
+   * Implements the method used by Internet Explorer 7's feed view
+   * documented here: http://blogs.msdn.com/rssteam/archive/2007/05/17/reading-feeds-in-right-to-left-order.aspx.
+   * 
+   * This algorithm differs slightly from the method documented in that the 
+   * primary language tag is case insensitive.
+   * 
+   * If the language tag is not specified, then the default Locale is used to 
+   * determine the direction.
+   * 
+   * According to the Atom Bidi spec, if the dir attribute is set explicitly, we 
+   * should not do language guessing.  This restriction can be bypassed by setting
+   * ignoredir to true.
+   */
+  public static <T extends Element>Direction guessDirectionFromLanguage(T element, boolean ignoredir) {
+    if (!ignoredir && hasDirection(element)) return getDirection(element);
+    Lang lang = element.getLanguageTag();
+    if (lang == null) {
+      Locale l = Locale.getDefault();
+      lang = new Lang(l.getLanguage());
+    }
+    String primary = lang.getPrimary();
+    return (primary.equalsIgnoreCase("ar") ||
+            primary.equalsIgnoreCase("fa") ||
+            primary.equalsIgnoreCase("ur") ||
+            primary.equalsIgnoreCase("ps") ||
+            primary.equalsIgnoreCase("syr") ||
+            primary.equalsIgnoreCase("dv") ||
+            primary.equalsIgnoreCase("he") ||
+            primary.equalsIgnoreCase("yi")) ? Direction.RTL : Direction.LTR; 
+  }
+
+  /**
+   * Attempt to guess the base direction of an element using an analysis of
+   * the directional properties of the characters used.  This is a brute-force
+   * style approach that can achieve fairly reasonable results when the element
+   * text consists primarily of characters with the same bidi properties.  This
+   * approach is implemented by the Snarfer feed reader as is documented at
+   * http://www.xn--8ws00zhy3a.com/blog/2006/12/right-to-left-rss   
+   * 
+   * If the dir attribute is specified, the direction will be determine using it's value 
+   * instead of the characteristics of the text
+   */
+  public static <T extends Element>Direction guessDirectionFromTextProperties(T element) {
+    return guessDirectionFromTextProperties(element, false);
+  }
+  
+  /**
+   * Attempt to guess the base direction of an element using an analysis of
+   * the directional properties of the characters used.  This is a brute-force
+   * style approach that can achieve fairly reasonable results when the element
+   * text consists primarily of characters with the same bidi properties.  This
+   * approach is implemented by the Snarfer feed reader as is documented at
+   * http://www.xn--8ws00zhy3a.com/blog/2006/12/right-to-left-rss   
+   * 
+   * According to the Atom Bidi spec, if the dir attribute is set explicitly, we 
+   * should not do language guessing.  This restriction can be bypassed by setting
+   * ignoredir to true.
+   */
+  public static <T extends Element>Direction guessDirectionFromTextProperties(T element, boolean ignoredir) {
+    Direction dir = Direction.UNSPECIFIED;
+    if (!ignoredir && hasDirection(element)) return getDirection(element);
+    String text = element.getText();
+    if (text != null) {
+      int c = 0;
+      for (int n = 0; n < text.length(); n++) {
+        char ch = text.charAt(n);
+        if (Bidi.requiresBidi(new char[] {ch}, 0, 1)) c++;
+        else c--;
+      }
+      dir = (c > 0) ? Direction.RTL : Direction.LTR;
+    }
+    return dir;
+  }
+
+  /**
+   * Use Java's built in support for bidi text to determine the base directionality
+   * of the element's text.  The response to this only indicates the *base* directionality,
+   * it does not indicate whether or not there are any RTL characters in the text.
+   * 
+   * If the dir attribute is specified, the direction will be determine using it's value 
+   * instead of the characteristics of the text
+   */
+  public static <T extends Element>Direction guessDirectionFromJavaBidi(T element) {
+    return guessDirectionFromJavaBidi(element, false);
+  }
+  
+  /**
+   * Use Java's built in support for bidi text to determine the base directionality
+   * of the element's text.  The response to this only indicates the *base* directionality,
+   * it does not indicate whether or not there are any RTL characters in the text.
+   * 
+   * According to the Atom Bidi spec, if the dir attribute is set explicitly, we 
+   * should not do language guessing.  This restriction can be bypassed by setting
+   * ignoredir to true.
+   */
+  public static <T extends Element>Direction guessDirectionFromJavaBidi(T element, boolean ignoredir) {
+    Direction dir = Direction.UNSPECIFIED;
+    if (!ignoredir && hasDirection(element)) return getDirection(element);
+    String text = element.getText();
+    if (text != null) {
+      AttributedString s = new AttributedString(text);
+      Bidi bidi = new Bidi(s.getIterator());
+      dir = (bidi.baseIsLeftToRight()) ? Direction.LTR : Direction.RTL;
+    }
+    return dir;
+  }
+  
+  private static <T extends Element>boolean hasDirection(T element) {
+    boolean answer = false;
+    String dir = element.getAttributeValue("dir");
+    if (dir != null && dir.length() > 0)
+      answer = true;
+    else if (dir == null) {
+      // if the direction is unspecified on this element, 
+      // let's see if we've inherited it
+      Base parent = element.getParentElement(); 
+      if (parent != null && 
+          parent instanceof Element)
+        answer = hasDirection((Element)parent);
+    }
+    return answer;
+  }
+}

Added: incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/AbstractSelector.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/AbstractSelector.java?rev=567888&view=auto
==============================================================================
--- incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/AbstractSelector.java (added)
+++ incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/AbstractSelector.java Mon Aug 20 17:51:20 2007
@@ -0,0 +1,36 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.ext.features;
+
+public abstract class AbstractSelector
+    implements Selector {
+
+  public Selector clone() {
+    try {
+      return (Selector) super.clone();
+    } catch (CloneNotSupportedException e) {
+      return copy();
+    }
+  }
+
+  protected Selector copy() {
+    throw new RuntimeException(
+      new CloneNotSupportedException());
+  }
+  
+}

Added: incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/AcceptSelector.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/AcceptSelector.java?rev=567888&view=auto
==============================================================================
--- incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/AcceptSelector.java (added)
+++ incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/AcceptSelector.java Mon Aug 20 17:51:20 2007
@@ -0,0 +1,43 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.ext.features;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.abdera.model.Collection;
+
+public class AcceptSelector
+    extends AbstractSelector
+    implements Selector {
+
+  private static final long serialVersionUID = -1289924342084041384L;
+  private final List<String> accepts = new ArrayList<String>();
+  
+  public AcceptSelector(String... accepts) {
+    for (String accept : accepts) this.accepts.add(accept);
+  }
+  
+  public boolean select(Collection collection) {
+    for (String accept : accepts) {
+      if (collection.accepts(accept)) return true;
+    }
+    return false;
+  }
+
+}

Added: incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/Feature.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/Feature.java?rev=567888&view=auto
==============================================================================
--- incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/Feature.java (added)
+++ incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/Feature.java Mon Aug 20 17:51:20 2007
@@ -0,0 +1,96 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.ext.features;
+
+import javax.xml.namespace.QName;
+
+import org.apache.abdera.factory.Factory;
+import org.apache.abdera.i18n.iri.IRI;
+import org.apache.abdera.model.Element;
+import org.apache.abdera.model.ExtensibleElementWrapper;
+
+public class Feature 
+  extends ExtensibleElementWrapper {
+  
+  public enum Status {
+    UNSUPPORTED,
+    UNSPECIFIED,
+    SUPPORTED, 
+    REQUIRED,  
+  }
+  
+  public Feature(Element internal) {
+    super(internal);
+  }
+  
+  public Feature(Factory factory) {
+    super(factory, FeaturesHelper.FEATURE);
+  }
+  
+  public IRI getRef()  {
+      String ref = getAttributeValue("ref");
+      return (ref != null) ? new IRI(ref) : null;
+  }
+  
+  public Status getStatus() {
+    String status = getAttributeValue("status");
+    return status != null ? Status.valueOf(status.toUpperCase()) : Status.SUPPORTED;
+  }
+  
+  public void setStatus(Status status) {
+    if (status != null && status != Status.SUPPORTED) {
+      if (status != Status.UNSPECIFIED) {
+        setAttributeValue("status",status.name().toLowerCase());
+      } else {
+        throw new IllegalArgumentException(
+          "Cannot set the status to unspecified");
+      }
+    } else {
+      removeAttribute(new QName("status"));
+    }
+  }
+  
+  public IRI getHref()  {
+      String href = getAttributeValue("href");
+      return (href != null) ? new IRI(href) : null;
+  }
+  
+  public String getLabel() {
+    return getAttributeValue("label");
+  }
+  
+  public void setRef(String ref) {
+      if (ref == null) throw new IllegalArgumentException();
+      setAttributeValue("ref", (new IRI(ref)).toString());
+  }
+  
+  public void setHref(String href)  {
+      if (href != null)
+        setAttributeValue("href", (new IRI(href)).toString());
+      else 
+        removeAttribute(new QName("href"));
+  }
+  
+  public void setLabel(String label) {
+    if (label != null)
+      setAttributeValue("label", label);
+    else 
+      removeAttribute(new QName("label"));
+  }
+  
+}

Added: incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/FeatureSelector.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/FeatureSelector.java?rev=567888&view=auto
==============================================================================
--- incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/FeatureSelector.java (added)
+++ incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/FeatureSelector.java Mon Aug 20 17:51:20 2007
@@ -0,0 +1,84 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.ext.features;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.abdera.ext.features.Feature.Status;
+import org.apache.abdera.model.Collection;
+
+public class FeatureSelector
+  extends AbstractSelector
+  implements Selector {
+
+  private static final long serialVersionUID = -8943638085557912175L;
+  private final Status minimumStatus;
+  private final List<String> features = new ArrayList<String>();
+  
+  public FeatureSelector(String... features) {
+    this(Status.SUPPORTED,features);
+  }
+  
+  public FeatureSelector(Status minimumStatus, String... features) {
+    this.minimumStatus = minimumStatus;
+    for (String feature : features) this.features.add(feature);
+  }
+  
+  public boolean select(Collection collection) {
+    for (String feature : features) {
+      Status status = FeaturesHelper.getFeatureStatus(collection, feature);
+      if (status != null && status.ordinal() >= minimumStatus.ordinal()) 
+        return true;
+    }
+    return false;
+  }
+
+  public String[] getFeatures() {
+    return features.toArray(new String[features.size()]);
+  }
+
+  public Status getMinimumStatus() {
+    return minimumStatus;
+  }
+
+  @Override
+  public int hashCode() {
+    final int PRIME = 31;
+    int result = 1;
+    result = PRIME * result + ((features == null) ? 0 : features.hashCode());
+    result = PRIME * result + ((minimumStatus == null) ? 0 : minimumStatus.hashCode());
+    return result;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) return true;
+    if (obj == null) return false;
+    if (getClass() != obj.getClass()) return false;
+    final FeatureSelector other = (FeatureSelector) obj;
+    if (features == null) {
+      if (other.features != null) return false;
+    } else if (!features.equals(other.features)) return false;
+    if (minimumStatus == null) {
+      if (other.minimumStatus != null) return false;
+    } else if (!minimumStatus.equals(other.minimumStatus)) return false;
+    return true;
+  }
+
+}

Added: incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/FeaturesExtensionFactory.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/FeaturesExtensionFactory.java?rev=567888&view=auto
==============================================================================
--- incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/FeaturesExtensionFactory.java (added)
+++ incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/FeaturesExtensionFactory.java Mon Aug 20 17:51:20 2007
@@ -0,0 +1,30 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.ext.features;
+
+import org.apache.abdera.util.AbstractExtensionFactory;
+
+public final class FeaturesExtensionFactory 
+  extends AbstractExtensionFactory {
+
+  public FeaturesExtensionFactory() {
+    super(FeaturesHelper.FNS);
+    addImpl(FeaturesHelper.FEATURE,Feature.class);
+  }
+  
+}

Added: incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/FeaturesHelper.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/FeaturesHelper.java?rev=567888&view=auto
==============================================================================
--- incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/FeaturesHelper.java (added)
+++ incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/FeaturesHelper.java Mon Aug 20 17:51:20 2007
@@ -0,0 +1,352 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.ext.features;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.activation.MimeType;
+import javax.activation.MimeTypeParseException;
+import javax.xml.namespace.QName;
+
+import org.apache.abdera.ext.features.Feature.Status;
+import org.apache.abdera.ext.thread.ThreadConstants;
+import org.apache.abdera.factory.Factory;
+import org.apache.abdera.i18n.iri.IRI;
+import org.apache.abdera.model.Collection;
+import org.apache.abdera.model.Element;
+import org.apache.abdera.model.Service;
+import org.apache.abdera.model.Workspace;
+import org.apache.abdera.util.MimeTypeHelper;
+
+/**
+ * Implementation of the current APP Features Draft
+ * (http://www.ietf.org/internet-drafts/draft-snell-atompub-feature-05.txt)
+ */
+public final class FeaturesHelper {
+
+  public static final String FNS = "http://purl.org/atompub/features/1.0";
+  public static final QName FEATURE = new QName(FNS, "feature","f");
+  public static final QName TYPE = new QName(FNS, "type", "f");
+  
+  private static final String FEATURE_BASE                 = "http://www.w3.org/2007/app/";
+  public static final String FEATURE_DRAFTS                = FEATURE_BASE + "drafts";
+  public static final String FEATURE_XHTML_CONTENT         = FEATURE_BASE + "xhtml-content";
+  public static final String FEATURE_HTML_CONTENT          = FEATURE_BASE + "html-content";
+  public static final String FEATURE_TEXT_CONTENT          = FEATURE_BASE + "text-content";
+  public static final String FEATURE_XML_CONTENT           = FEATURE_BASE + "xml-content";
+  public static final String FEATURE_BINARY_CONTENT        = FEATURE_BASE + "binary-content";
+  public static final String FEATURE_REF_CONTENT           = FEATURE_BASE + "ref-content";
+  public static final String FEATURE_XHTML_TITLE           = FEATURE_BASE + "xhtml-title";
+  public static final String FEATURE_HTML_TITLE            = FEATURE_BASE + "html-title";
+  public static final String FEATURE_TEXT_TITLE            = FEATURE_BASE + "text-title";
+  public static final String FEATURE_XHTML_SUMMARY         = FEATURE_BASE + "xhtml-summary";
+  public static final String FEATURE_HTML_SUMMARY          = FEATURE_BASE + "html-summary";
+  public static final String FEATURE_TEXT_SUMMARY          = FEATURE_BASE + "text-summary";
+  public static final String FEATURE_AUTO_SUMMARY          = FEATURE_BASE + "auto-summary";
+  public static final String FEATURE_XHTML_RIGHTS          = FEATURE_BASE + "xhtml-rights";
+  public static final String FEATURE_HTML_RIGHTS           = FEATURE_BASE + "html-rights";
+  public static final String FEATURE_TEXT_RIGHTS           = FEATURE_BASE + "text-rights";
+  public static final String FEATURE_AUTH_AUTHOR           = FEATURE_BASE + "auth-author";
+  public static final String FEATURE_SLUG                  = FEATURE_BASE + "slug";
+  public static final String FEATURE_MULTIPLE_CATEGORIES   = FEATURE_BASE + "multiple-categories";
+  public static final String FEATURE_MULTIPLE_AUTHORS      = FEATURE_BASE + "multiple-authors";
+  public static final String FEATURE_MULTIPLE_CONTRIBUTORS = FEATURE_BASE + "multiple-contributors";
+  public static final String FEATURE_PRESERVE_INFOSET      = FEATURE_BASE + "preserve-infoset";
+  public static final String FEATURE_PRESERVE_ID           = FEATURE_BASE + "preserve-id";
+  public static final String FEATURE_PRESERVE_DATES        = FEATURE_BASE + "preserve-dates";
+  public static final String FEATURE_PRESERVE_EXTENSIONS   = FEATURE_BASE + "preserve-extensions";
+  public static final String FEATURE_PRESERVE_LINKS        = FEATURE_BASE + "preserve-links";
+  public static final String FEATURE_PRESERVE_RIGHTS       = FEATURE_BASE + "preserve-rights";
+  public static final String FEATURE_SCHEDULED_PUBLISHING  = FEATURE_BASE + "scheduled-publishing";
+  public static final String FEATURE_THREADING             = ThreadConstants.THR_NS;
+
+  
+  private static final String ABDERA_FEATURE_BASE = "http://incubator.apache.org/abdera/features/";
+  
+  /**
+   * Indicates that the collection will accept digitally signed entries
+   * If marked as "required", the collection will only accept digitally signed entries
+   */
+  public static final String ABDERA_FEATURE_SIGNATURE = ABDERA_FEATURE_BASE + "signature";
+  
+  /**
+   * Indicates that the collection will preserve XML digital signatures contained
+   * in member resources 
+   */
+  public static final String ABDERA_FEATURE_PRESERVE_SIGNATURE = ABDERA_FEATURE_BASE + "preserve-signature";
+  
+  /**
+   * Indicates that the collection supports the use of the Atom Bidi Attribute.
+   * If marked as "required", the collection will only accept entries that contain the bidi attribute
+   */
+  public static final String ABDERA_FEATURE_BIDI = ABDERA_FEATURE_BASE + "bidi";
+  
+  /**
+   * Indicates that the collection supports the use of Diffie-Hellman key exchange
+   * for XML encrypted requests
+   */
+  public static final String ABDERA_FEATURE_DHENCREQUEST = ABDERA_FEATURE_BASE + "dhenc-request";
+  
+  /**
+   * Indicates that the collection supports the use of Diffie-Hellman key exchange
+   * for XML encrypted responses
+   */
+  public static final String ABDERA_FEATURE_DHENCRESPONSE = ABDERA_FEATURE_BASE + "dhenc-response";
+  
+  /**
+   * Indicates that the collection will add it's own digital signature to the 
+   * collection feed and member resources
+   */
+  public static final String ABDERA_FEATURE_SIGNED_RESPONSE = ABDERA_FEATURE_BASE + "response-signature";
+  
+  /**
+   * Indicates that the collection supports the use of Geo extensions (see the
+   * org.apache.abdera.ext.geo Package)
+   */
+  public static final String ABDERA_FEATURE_GEO = ABDERA_FEATURE_BASE + "geo";
+  
+  /**
+   * Indicates that the collection supports the use of the Feed paging standard.
+   * (ftp://ftp.rfc-editor.org/in-notes/internet-drafts/draft-nottingham-atompub-feed-history-11.txt)
+   * See the org.apache.abdera.ext.history Package)
+   */
+  public static final String ABDERA_FEATURE_PAGING = ABDERA_FEATURE_BASE + "paging";
+  
+  /**
+   * Indicates that the collection supports the use of the Simple Sharing Extensions
+   * (see the org.apache.abdera.ext.sharing Package)
+   */
+  public static final String ABDERA_FEATURE_SHARING = ABDERA_FEATURE_BASE + "sharing";
+  
+  /**
+   * Indicates that the collection supports the GoogleLogin auth scheme
+   * (see the org.apache.abdera.ext.gdata Package)
+   */
+  public static final String ABDERA_FEATURE_GOOGLELOGIN = ABDERA_FEATURE_BASE + "googlelogin";
+  
+  /**
+   * Indicates that the collection supports the WSSE auth scheme
+   * (see the org.apache.abdera.ext.wsse Package)
+   */
+  public static final String ABDERA_FEATURE_WSSE = ABDERA_FEATURE_BASE + "wsse";
+  
+  
+  
+  
+  private FeaturesHelper() {}
+  
+  /**
+   * Returns the specified feature element or null
+   */
+  public static Feature getFeature(
+    Collection collection,
+    String feature) {
+      List<Element> list = collection.getExtensions(FEATURE);
+      for (Element el : list) {
+        if (el.getAttributeValue("ref").equals(feature))
+          return (Feature) el;
+      }
+      return null;
+  }
+  
+  public static Status getFeatureStatus(Collection collection, String feature) {
+    Feature f = getFeature(collection,feature);
+    return f != null ? f.getStatus() : Status.UNSPECIFIED;
+  }
+  
+  public static Feature[] getSupportedFeatures(Collection collection) {
+    return getFeatures(collection, Status.SUPPORTED);
+  }
+  
+  public static Feature[] getUnsupportedFeatures(Collection collection) {
+    return getFeatures(collection, Status.UNSUPPORTED);
+  }
+  
+  public static Feature[] getRequiredFeatures(Collection collection) {
+    return getFeatures(collection, Status.REQUIRED);
+  }
+  
+  public static Feature[] getFeatures(Collection collection, Status status) {
+    if (status == null) status = Status.SUPPORTED;
+    List<Feature> list = new ArrayList<Feature>();
+    List<Feature> features = collection.getExtensions(FEATURE);
+    for (Feature feature : features) {
+      if (status == feature.getStatus()) {
+        list.add(feature);
+      }
+    }
+    return list.toArray(new Feature[list.size()]);
+  }
+  
+  /**
+   * Add the specified features to the collection
+   */
+  public static Feature[] addFeatures(
+    Collection collection, 
+    String... features) {
+      List<Feature> list = new ArrayList<Feature>();
+      for (String feature : features)
+        list.add(addFeature(collection,feature));
+      return list.toArray(new Feature[list.size()]);
+  }
+  
+  /**
+   * Add the specified features to the collection
+   */
+  public static Feature[] addFeatures(
+    Collection collection,
+    Status status,
+    String... features) {
+      List<Feature> list = new ArrayList<Feature>();
+      for (String feature : features)
+        list.add(addFeature(collection,feature, status));
+      return list.toArray(new Feature[list.size()]);
+  }
+  
+  /**
+   * Add the specified feature to the collection
+   * @param collection The collection
+   * @param feature The IRI of the feature to add 
+   */
+  public static Feature addFeature(
+    Collection collection, 
+    String feature) {
+      return addFeature(
+        collection, 
+        feature, 
+        null, null, null);
+  }
+  
+  /**
+   * Add the specified feature to the collection
+   * @param collection The collection
+   * @param feature The IRI of the feature to add 
+   */
+  public static Feature addFeature(
+    Collection collection, 
+    String feature, 
+    Status status) {
+      return addFeature(
+        collection, 
+        feature, 
+        status, 
+        null, null);
+  }
+  
+  /**
+   * Add the specified feature to the collection
+   * @param collection The collection
+   * @param feature The IRI of the feature to add 
+   * @param required True if the feature is required
+   * @param href An IRI pointing to a human readable resource describing the feature
+   * @param label A human readable label for the feature
+   */
+  public static Feature addFeature(
+    Collection collection, 
+    String feature,
+    Status status,
+    String href,
+    String label) {
+    if (getFeature(collection, feature) != null) 
+      throw new IllegalArgumentException("Feature already specified");
+    Factory factory = collection.getFactory();
+    Feature el = 
+      (Feature)factory.newExtensionElement(
+        FeaturesHelper.FEATURE, collection);
+    collection.declareNS(FNS, "f");
+    el.setRef(new IRI(feature).toString());
+    el.setStatus(status);
+    if (href != null) el.setHref(new IRI(href).toString());
+    if (label != null) el.setLabel(label);
+    return el;
+  }
+  
+  /**
+   * Select a Collection from the service document
+   */
+  public static Collection[] select(Service service, Selector selector) {
+    return select(service, new Selector[] {selector});
+  }
+  
+  /**
+   * Select a Collection from the service document
+   */
+  public static Collection[] select(Service service, Selector... selectors) {
+    List<Collection> list = new ArrayList<Collection>();
+    for (Workspace workspace : service.getWorkspaces()) {
+      Collection[] collections = select(workspace, selectors);
+      for (Collection collection : collections)
+        list.add(collection);
+    }
+    return list.toArray(new Collection[list.size()]);
+  }
+  
+  /**
+   * Select a Collection from the Workspace
+   */
+  public static Collection[] select(Workspace workspace, Selector selector) {
+    return select(workspace, new Selector[] {selector});
+  }
+  
+  /**
+   * Select a Collection from the Workspace
+   */
+  public static Collection[] select(Workspace workspace, Selector... selectors) {
+    List<Collection> list = new ArrayList<Collection>();
+    for (Collection collection : workspace.getCollections()) {
+      boolean accept = true;
+      for (Selector selector : selectors) {
+        if (!selector.select(collection)) {
+          accept = false;
+          break;
+        }
+      }
+      if (accept) list.add(collection);
+    }
+    return list.toArray(new Collection[list.size()]);
+  }
+  
+  public static void addType(Feature feature, String mediaRange) {
+    addType(feature, new String[] {mediaRange});
+  }
+  
+  public static void addType(Feature feature, String... mediaRanges) {
+    mediaRanges = MimeTypeHelper.condense(mediaRanges);
+    for (String mediaRange : mediaRanges) {
+      try {
+        feature.addSimpleExtension(TYPE, new MimeType(mediaRange).toString());
+      } catch (MimeTypeParseException e) {}
+    }
+  }
+  
+  public static String[] getTypes(Feature feature) {
+    List<String> list = new ArrayList<String>();
+    for (Element type : feature.getExtensions(TYPE)) {
+      String value = type.getText();
+      if (value != null) {
+        value = value.trim();
+        try {
+          list.add(new MimeType(value).toString());
+        } catch (MimeTypeParseException e) {}
+      }
+    }
+    return list.toArray(new String[list.size()]);
+  }
+  
+}

Added: incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/Selector.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/Selector.java?rev=567888&view=auto
==============================================================================
--- incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/Selector.java (added)
+++ incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/Selector.java Mon Aug 20 17:51:20 2007
@@ -0,0 +1,31 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.ext.features;
+
+import java.io.Serializable;
+
+import org.apache.abdera.model.Collection;
+
+public interface Selector
+  extends Cloneable, Serializable {
+
+  boolean select(Collection collection);
+  
+  Selector clone(); 
+  
+}

Added: incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/XPathSelector.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/XPathSelector.java?rev=567888&view=auto
==============================================================================
--- incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/XPathSelector.java (added)
+++ incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/features/XPathSelector.java Mon Aug 20 17:51:20 2007
@@ -0,0 +1,89 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.ext.features;
+
+import java.util.Map;
+
+import org.apache.abdera.Abdera;
+import org.apache.abdera.model.Collection;
+import org.apache.abdera.xpath.XPath;
+
+/**
+ * Selects a collection based on a boolean XPath expression
+ */
+public class XPathSelector
+    extends AbstractSelector
+    implements Selector {
+
+  private static final long serialVersionUID = 7751803876821166591L;
+  
+  private final XPath xpath;
+  private final Map<String,String> namespaces;
+  private final String path;
+  
+  public XPathSelector(
+    String path) {
+      this(path,(new Abdera()).getXPath());
+  }
+  
+  public XPathSelector(
+    String path, 
+    XPath xpath) {
+      this(path,xpath,xpath.getDefaultNamespaces());
+  }
+  
+  public XPathSelector(
+    String path, 
+    XPath xpath, 
+    Map<String,String> namespaces) {
+      this.path = path;
+      this.xpath = xpath;
+      this.namespaces = namespaces;
+      if (!this.namespaces.containsValue(FeaturesHelper.FNS)) {
+        int c = 0;
+        String p = "f";
+        if (!this.namespaces.containsKey(p)) {
+          this.namespaces.put(p, FeaturesHelper.FNS);
+        } else {
+          String s = p + c;
+          while (this.namespaces.containsKey(s)) {
+            c++; s = p + c;
+          }
+          this.namespaces.put(s, FeaturesHelper.FNS);
+        }
+      }
+  }
+  
+  public String getFeaturesPrefix() {
+    for (Map.Entry<String,String> entry : namespaces.entrySet()) {
+      if (entry.getValue().equals(FeaturesHelper.FNS)) return entry.getKey();
+    }
+    return null;
+  }
+  
+  public boolean select(Collection collection) {
+    if (xpath.booleanValueOf(path, collection, namespaces)) {
+      return true;
+    }
+    return false;
+  }
+
+  public void addNamespace(String prefix, String uri) {
+    namespaces.put(prefix, uri);
+  }
+}

Added: incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/history/FeedPagingHelper.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/history/FeedPagingHelper.java?rev=567888&view=auto
==============================================================================
--- incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/history/FeedPagingHelper.java (added)
+++ incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/history/FeedPagingHelper.java Mon Aug 20 17:51:20 2007
@@ -0,0 +1,289 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.ext.history;
+
+import javax.xml.namespace.QName;
+
+import org.apache.abdera.model.Element;
+import org.apache.abdera.model.Feed;
+import org.apache.abdera.model.Link;
+import org.apache.abdera.i18n.iri.IRI;
+
+/**
+ * Initial support for Mark Nottingham's Feed Paging and Archiving draft 
+ * (http://ietfreport.isoc.org/all-ids/draft-nottingham-atompub-feed-history-11.txt)
+ */
+public final class FeedPagingHelper {
+
+  public static final String FH_PREFIX = "fh";
+  public static final String FHNS = "http://purl.org/syndication/history/1.0";
+  public static final QName COMPLETE = new QName(FHNS, "complete", FH_PREFIX);
+  public static final QName ARCHIVE = new QName(FHNS, "archive", FH_PREFIX);
+  
+  FeedPagingHelper() {}
+  
+  /**
+   * Returns true if the feed is "complete". According to the Feed Paging
+   * and Archiving specification, in a complete feed, "any entry not actually 
+   * in the feed document SHOULD NOT be considered to be part of that feed."
+   * @param feed The feed to check
+   */
+  public static boolean isComplete(Feed feed) {
+    return (feed.getExtension(COMPLETE) != null);
+  }
+  
+  /**
+   * Flag the feed as being complete.  According to the Feed Paging
+   * and Archiving specification, in a complete feed, "any entry not actually 
+   * in the feed document SHOULD NOT be considered to be part of that feed."
+   * @param feed The Feed to mark as complete
+   * @param complete True if the feed is complete
+   */
+  public static void setComplete(Feed feed, boolean complete) {
+    if (complete) {
+      if (!isComplete(feed)) feed.addExtension(COMPLETE);
+    } else {
+      if (isComplete(feed)) {
+        Element ext = feed.getExtension(COMPLETE);
+        ext.discard(); 
+      }
+    }
+  }
+  
+  /**
+   * Flag the feed as being an archive.
+   * @param feed The Feed to mark as an archive
+   * @param archive True if the feed is an archive
+   */
+  public static void setArchive(Feed feed, boolean archive) {
+    if (archive) {
+      if (!isArchive(feed)) feed.addExtension(ARCHIVE);
+    } else {
+      if (isArchive(feed)) {
+        Element ext = feed.getExtension(ARCHIVE);
+        ext.discard();
+      }
+    }
+  }
+
+  /**
+   * Return true if the feed has been marked as an archive
+   * @param feed The feed to check
+   */
+  public static boolean isArchive(Feed feed) {
+    return feed.getExtension(ARCHIVE) != null;
+  }
+  
+  /**
+   * Return true if the feed contains any next, previous, first or last 
+   * paging link relations
+   * @param feed The feed to check
+   */
+  public static boolean isPaged(Feed feed) {
+    return feed.getLink("next") != null ||
+           feed.getLink("previous") != null ||
+           feed.getLink("first") != null ||
+           feed.getLink("last") != null;
+  }
+  
+  /**
+   * Adds a next link relation to the feed
+   * @param feed The feed 
+   * @param iri The IRI of the next feed document
+   * @return The newly created Link
+   */
+  public static Link setNext(Feed feed, String iri) {
+    Link link = feed.getLink("next");
+    if (link != null) {
+      link.setHref(iri);
+    } else {
+      link = feed.addLink(iri, "next");
+    }
+    return link;
+  }
+  
+  /**
+   * Adds a previous link relation to the feed
+   * @param feed The feed 
+   * @param iri The IRI of the previous feed document
+   * @return The newly created Link
+   */
+  public static Link setPrevious(Feed feed, String iri) {
+    Link link = feed.getLink("previous");
+    if (link != null) {
+      link.setHref(iri);
+    } else {
+      link = feed.addLink(iri, "previous");
+    }
+    return link;
+  }
+  
+  /**
+   * Adds a first link relation to the feed
+   * @param feed The feed 
+   * @param iri The IRI of the first feed document
+   * @return The newly created Link
+   */
+  public static Link setFirst(Feed feed, String iri) {
+    Link link = feed.getLink("first");
+    if (link != null) {
+      link.setHref(iri);
+    } else {
+      link = feed.addLink(iri, "first");
+    }
+    return link;
+  }
+  
+  /**
+   * Adds a last link relation to the feed
+   * @param feed The feed 
+   * @param iri The IRI of the last feed document
+   * @return The newly created Link
+   */
+  public static Link setLast(Feed feed, String iri) {
+    Link link = feed.getLink("last");
+    if (link != null) {
+      link.setHref(iri);
+    } else {
+      link = feed.addLink(iri, "last");
+    }
+    return link;
+  }
+  
+  /**
+   * Adds a next-archive link relation to the feed
+   * @param feed The feed 
+   * @param iri The IRI of the next archive feed document
+   * @return The newly created Link
+   */
+  public static Link setNextArchive(Feed feed, String iri) {
+    Link link = feed.getLink("next-archive");
+    if (link == null) { // try the full IANA URI version
+      link = feed.getLink(Link.IANA_BASE + "next-archive");
+    }
+    if (link != null) {
+      link.setHref(iri);
+    } else {
+      link = feed.addLink(iri, "next-archive");
+    }
+    return link;
+  }
+  
+  /**
+   * Adds a prev-archive link relation to the feed
+   * @param feed The feed 
+   * @param iri The IRI of the previous archive feed document
+   * @return The newly created Link
+   */
+  public static Link setPreviousArchive(Feed feed, String iri) {
+    Link link = feed.getLink("prev-archive");
+    if (link == null) { // try the full IANA URI version
+      link = feed.getLink(Link.IANA_BASE + "prev-archive");
+    }
+    if (link != null) {
+      link.setHref(iri);
+    } else {
+      link = feed.addLink(iri, "prev-archive");
+    }
+    return link;
+  }
+  
+  /**
+   * Adds a current link relation to the feed
+   * @param feed The feed 
+   * @param iri The IRI of the current feed document
+   * @return The newly created Link
+   */
+  public static Link setCurrent(Feed feed, String iri) {
+    Link link = feed.getLink("current");
+    if (link == null) { // try the full IANA URI version
+      link = feed.getLink(Link.IANA_BASE + "current");
+    }
+    if (link != null) {
+      link.setHref(iri);
+    } else {
+      link = feed.addLink(iri, "current");
+    }
+    return link;
+  }
+  
+  /**
+   * Returns the IRI of the next link relation
+   */
+  public static IRI getNext(Feed feed) {
+    Link link = feed.getLink("next");
+    return (link != null) ? link.getResolvedHref() : null;
+  }
+  
+  /**
+   * Returns the IRI of the previous link relation
+   */
+  public static IRI getPrevious(Feed feed) {
+    Link link = feed.getLink("previous");
+    return (link != null) ? link.getResolvedHref() : null;
+  }
+  
+  /**
+   * Returns the IRI of the first link relation
+   */
+  public static IRI getFirst(Feed feed) {
+    Link link = feed.getLink("first");
+    return (link != null) ? link.getResolvedHref() : null;
+  }
+  
+  /**
+   * Returns the IRI of the last link relation
+   */
+  public static IRI getLast(Feed feed) {
+    Link link = feed.getLink("last");
+    return (link != null) ? link.getResolvedHref() : null;
+  }
+  
+  /**
+   * Returns the IRI of the prev-archive link relation
+   */
+  public static IRI getPreviousArchive(Feed feed) {
+    Link link = feed.getLink("prev-archive");
+    if (link == null) { // try the full IANA URI version
+      link = feed.getLink(Link.IANA_BASE + "prev-archive");
+    }
+    return (link != null) ? link.getResolvedHref() : null;
+  }
+  
+  /**
+   * Returns the IRI of the next-archive link relation
+   */
+  public static IRI getNextArchive(Feed feed) {
+    Link link = feed.getLink("next-archive");
+    if (link == null) { // try the full IANA URI version
+      link = feed.getLink(Link.IANA_BASE + "next-archive");
+    }
+    return (link != null) ? link.getResolvedHref() : null;
+  }
+  
+  /**
+   * Returns the IRI of the current link relation
+   */
+  public static IRI getCurrent(Feed feed) {
+    Link link = feed.getLink("current");
+    if (link == null) { // try the full IANA URI version
+      link = feed.getLink(Link.IANA_BASE + "current");
+    }
+    return (link != null) ? link.getResolvedHref() : null;
+  }
+}

Added: incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/license/LicenseHelper.java
URL: http://svn.apache.org/viewvc/incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/license/LicenseHelper.java?rev=567888&view=auto
==============================================================================
--- incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/license/LicenseHelper.java (added)
+++ incubator/abdera/java/trunk/extensions/main/src/main/java/org/apache/abdera/ext/license/LicenseHelper.java Mon Aug 20 17:51:20 2007
@@ -0,0 +1,150 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  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.  For additional information regarding
+* copyright in this work, please see the NOTICE file in the top level
+* directory of this distribution.
+*/
+package org.apache.abdera.ext.license;
+
+import java.util.List;
+
+import org.apache.abdera.i18n.iri.IRI;
+import org.apache.abdera.model.Base;
+import org.apache.abdera.model.Entry;
+import org.apache.abdera.model.Link;
+import org.apache.abdera.model.Source;
+
+/**
+ * Implementation of the Atom License Extension, RFC 4946
+ */
+public final class LicenseHelper {
+
+  public static final String UNSPECIFIED_LICENSE = "http://purl.org/atompub/license#unspecified";
+  
+  LicenseHelper() {}
+  
+  public static List<Link> getLicense(
+    Base base, 
+    boolean inherited) {
+      List<Link> links = null;
+      if (base instanceof Source) {
+        links = ((Source)base).getLinks(Link.REL_LICENSE);
+      } else if (base instanceof Entry) {
+        Entry entry = (Entry)base;
+        Source source = entry.getSource();
+        Base parent = entry.getParentElement();
+        links = entry.getLinks(Link.REL_LICENSE);
+        if (inherited && (links == null || links.size() == 0) && source != null) {
+          links = getLicense(source, false);
+        }
+        if (inherited && (links == null || links.size() == 0) && parent != null) {
+          links = getLicense(parent, false);
+        }
+      }
+      return links;
+  }  
+  
+  public static List<Link> getLicense(
+    Base base) {
+      return getLicense(base, true);
+  }
+  
+  public static boolean hasUnspecifiedLicense(
+    Base base, 
+    boolean inherited) {
+      return hasLicense(base, UNSPECIFIED_LICENSE, inherited);
+  }
+  
+  public static boolean hasUnspecifiedLicense(
+    Base base) {
+      return hasUnspecifiedLicense(base, true);
+  }
+  
+  public static boolean hasLicense(
+    Base base, 
+    String iri, 
+    boolean inherited) {
+    List<Link> links = getLicense(base, inherited);
+    IRI check = new IRI(iri);
+    boolean answer = false;
+    if (links != null) {
+      for (Link link : links) {
+        if (link.getResolvedHref().equals(check)) {
+          answer = true;
+          break;
+        }
+      }
+    }
+    return answer;
+  }
+  
+  public static boolean hasLicense(
+    Base base, 
+    String iri) {
+    return hasLicense(base, iri, true);
+  }
+  
+  public static boolean hasLicense(
+    Base base, 
+    boolean inherited) {
+      List<Link> links = getLicense(base, inherited);
+      return (links != null && links.size() > 0);
+  }
+  
+  public static boolean hasLicense(
+    Base base) {
+      return hasLicense(base, true);
+  }
+  
+  public static Link addUnspecifiedLicense(
+    Base base) {
+      if (hasUnspecifiedLicense(base,false)) 
+        throw new IllegalStateException("Unspecified license already added");
+      if (hasLicense(base,false)) 
+        throw new IllegalStateException("Other licenses are already added.");
+      return addLicense(base, UNSPECIFIED_LICENSE);
+  }
+  
+  public static Link addLicense(
+    Base base, 
+    String iri) {
+      return addLicense(base, iri, null, null, null);
+  }
+  
+  public static Link addLicense(
+    Base base,
+    String iri,
+    String title) {
+      return addLicense(base, iri, null, title, null);
+  }
+  
+  public static Link addLicense(
+    Base base,
+    String iri,
+    String type,
+    String title,
+    String hreflang) {
+    if (hasLicense(base, iri,false))
+      throw new IllegalStateException("License '" + iri + "' has already been added");
+    if (hasUnspecifiedLicense(base,false))
+      throw new IllegalStateException("Unspecified license already added");
+    if (base instanceof Source) {
+      return ((Source)base).addLink((new IRI(iri)).toString(), Link.REL_LICENSE, type, title, hreflang, -1);
+    } else if (base instanceof Entry) {
+      return ((Entry)base).addLink((new IRI(iri)).toString(), Link.REL_LICENSE, type, title, hreflang, -1);
+    }
+    return null;
+  }
+  
+}