abdera-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jmsn...@apache.org
Subject svn commit: r1173209 [10/49] - in /abdera/abdera2: ./ .settings/ activities/ activities/src/ activities/src/main/ activities/src/main/java/ activities/src/main/java/org/ activities/src/main/java/org/apache/ activities/src/main/java/org/apache/abdera2/ ...
Date Tue, 20 Sep 2011 15:57:20 GMT
Added: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/io/PeekAheadInputStream.java
URL: http://svn.apache.org/viewvc/abdera/abdera2/common/src/main/java/org/apache/abdera2/common/io/PeekAheadInputStream.java?rev=1173209&view=auto
==============================================================================
--- abdera/abdera2/common/src/main/java/org/apache/abdera2/common/io/PeekAheadInputStream.java (added)
+++ abdera/abdera2/common/src/main/java/org/apache/abdera2/common/io/PeekAheadInputStream.java Tue Sep 20 15:56:46 2011
@@ -0,0 +1,65 @@
+package org.apache.abdera2.common.io;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A version of RewindableInputStream that provides methods for peeking ahead in the stream (equivalent to read()
+ * followed by an appropriate unread()
+ */
+public class PeekAheadInputStream extends RewindableInputStream {
+
+    public PeekAheadInputStream(InputStream in) {
+        super(in);
+    }
+
+    public PeekAheadInputStream(InputStream in, int initialSize) {
+        super(in, initialSize);
+    }
+
+    /**
+     * Peek the next byte in the stream
+     */
+    public int peek() throws IOException {
+        int m = read();
+        unread(m);
+        return m;
+    }
+
+    /**
+     * Peek the next bytes in the stream. Returns the number of bytes peeked. Will return -1 if the end of the stream is
+     * reached
+     */
+    public int peek(byte[] buf) throws IOException {
+        return peek(buf, 0, buf.length);
+    }
+
+    /**
+     * Peek the next bytes in the stream. Returns the number of bytes peeked. Will return -1 if the end of the stream is
+     * reached
+     */
+    public int peek(byte[] buf, int off, int len) throws IOException {
+        int r = read(buf, off, len);
+        if (r > -1) unread(buf, off, r);
+        return r;
+    }
+
+}

Propchange: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/io/PeekAheadInputStream.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/io/RewindableInputStream.java
URL: http://svn.apache.org/viewvc/abdera/abdera2/common/src/main/java/org/apache/abdera2/common/io/RewindableInputStream.java?rev=1173209&view=auto
==============================================================================
--- abdera/abdera2/common/src/main/java/org/apache/abdera2/common/io/RewindableInputStream.java (added)
+++ abdera/abdera2/common/src/main/java/org/apache/abdera2/common/io/RewindableInputStream.java Tue Sep 20 15:56:46 2011
@@ -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.abdera2.common.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * RewindableInputStream is a specialization of the PushbackInputStream that maintains an internal buffer of read bytes
+ * that a user can rewind (unread) back into the stream without having to do their own buffer management. The rewind
+ * buffer grows dynamically
+ */
+public class RewindableInputStream extends DynamicPushbackInputStream {
+
+    private static final int INITIAL_CAPACITY = 32;
+    private static final float DEFAULT_LOAD_FACTOR = 0.75f;
+    private byte[] buffer;
+    private int position;
+    private final int scale;
+    private final float lf;
+
+    public RewindableInputStream(InputStream in) {
+        this(in, INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
+    }
+
+    public RewindableInputStream(InputStream in, int capacity) {
+      this(in, capacity, DEFAULT_LOAD_FACTOR);
+    }
+    
+    public RewindableInputStream(InputStream in, int capacity, float lf) {
+        super(in);
+        grow(capacity);
+        this.scale = (int)(capacity * lf);
+        this.lf = Math.max(0.5f,Math.min(1.0f, lf));
+    }
+
+    public int position() {
+        return position;
+    }
+
+    private void grow(int capacity) {
+        if (buffer == null) {
+            buffer = new byte[capacity];
+            return;
+        } else {
+            byte[] buf = new byte[buffer.length + capacity];
+            System.arraycopy(buffer, 0, buf, 0, buffer.length);
+            buffer = buf;
+        }
+    }
+
+    private void shrink(int len) {
+        if (buffer == null)
+            return;
+        byte[] buf = new byte[buffer.length - len];
+        System.arraycopy(buffer, 0, buf, 0, buf.length);
+        position = buffer.length - len;
+        buffer = buf;
+    }
+
+    public void rewind() throws IOException {
+        if (buffer.length == 0)
+            return;
+        unread(buffer, 0, position);
+        shrink(buffer.length);
+        if (shouldGrow(position,0))
+          grow(scale);
+    }
+
+    public void rewind(int offset, int len) throws IOException {
+        if (buffer.length == 0)
+            return;
+        if (offset > buffer.length)
+            throw new ArrayIndexOutOfBoundsException(offset);
+        unread(buffer, offset, len);
+        shrink(len);
+        if (shouldGrow(position,0))
+          grow(scale);
+    }
+
+    public void rewind(int len) throws IOException {
+        if (buffer.length == 0)
+            return;
+        rewind(buffer.length - len, len);
+    }
+
+    public int read() throws IOException {
+        int i = super.read();
+        if (i != -1) {
+            if (shouldGrow(position,0))
+                grow(scale);
+            buffer[position++] = (byte)i;
+        }
+        return i;
+    }
+
+    public int read(byte[] b, int off, int len) throws IOException {
+        int r = super.read(b, off, len);
+        if (r != -1) {
+            if (shouldGrow(position,r))
+                grow(Math.max(position + r, scale));
+            System.arraycopy(b, off, buffer, position, r);
+            position = position + r;
+        }
+        return r;
+    }
+    
+    private boolean shouldGrow(int position, int r) {
+      int t = (int)(buffer.length * lf);
+      return position + r >= t;
+    }
+
+    public long skip(long n) throws IOException {
+        return super.skip(n);
+    }
+
+}

Propchange: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/io/RewindableInputStream.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/AbstractScheme.java
URL: http://svn.apache.org/viewvc/abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/AbstractScheme.java?rev=1173209&view=auto
==============================================================================
--- abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/AbstractScheme.java (added)
+++ abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/AbstractScheme.java Tue Sep 20 15:56:46 2011
@@ -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.abdera2.common.iri;
+
+/**
+ * Base implementation for IRI scheme providers
+ */
+public abstract class AbstractScheme implements Scheme {
+
+  private static final long serialVersionUID = 1458641384259566421L;
+    protected final String name;
+    protected final int port;
+
+    protected AbstractScheme(String name, int port) {
+        if (name == null)
+          throw new IllegalArgumentException();
+        this.name = name.toLowerCase();
+        this.port = port;
+    }
+
+    public int port() {
+        return port;
+    }
+
+    public String name() {
+        return name;
+    }
+
+    /**
+     * Default return unmodified
+     */
+    public IRI normalize(IRI iri) {
+        return iri;
+    }
+
+    /**
+     * Default return unmodified
+     */
+    public String normalizePath(String path) {
+        return path;
+    }
+
+    @Override
+    public int hashCode() {
+      final int prime = 31;
+      int result = 1;
+      result = prime * result + ((name == null) ? 0 : name.hashCode());
+      result = prime * result + port;
+      return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj)
+        return true;
+      if (obj == null)
+        return false;
+      if (getClass() != obj.getClass())
+        return false;
+      AbstractScheme other = (AbstractScheme) obj;
+      if (name == null) {
+        if (other.name != null)
+          return false;
+      } else if (!name.equals(other.name))
+        return false;
+      if (port != other.port)
+        return false;
+      return true;
+    }
+    
+    public String toString() {
+      return name();
+    }
+}

Propchange: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/AbstractScheme.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/FtpScheme.java
URL: http://svn.apache.org/viewvc/abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/FtpScheme.java?rev=1173209&view=auto
==============================================================================
--- abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/FtpScheme.java (added)
+++ abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/FtpScheme.java Tue Sep 20 15:56:46 2011
@@ -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.abdera2.common.iri;
+
+class FtpScheme extends HttpScheme {
+
+  private static final long serialVersionUID = 2615855819173389741L;
+    static final String NAME = "ftp";
+    static final int DEFAULT_PORT = 21;
+
+    public FtpScheme() {
+        super(NAME, DEFAULT_PORT);
+    }
+
+}

Propchange: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/FtpScheme.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/HttpScheme.java
URL: http://svn.apache.org/viewvc/abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/HttpScheme.java?rev=1173209&view=auto
==============================================================================
--- abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/HttpScheme.java (added)
+++ abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/HttpScheme.java Tue Sep 20 15:56:46 2011
@@ -0,0 +1,72 @@
+/*
+ * 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.abdera2.common.iri;
+
+import org.apache.abdera2.common.text.UrlEncoding;
+import org.apache.abdera2.common.text.CharUtils.Profile;
+
+class HttpScheme extends AbstractScheme {
+
+  private static final long serialVersionUID = -1245261239186820368L;
+    static final String HTTP = "http";
+    static final String HTTPS = "https";
+    static final int DEFAULT_PORT = 80;
+    static final int HTTPS_DEFAULT_PORT = 443;
+
+    public HttpScheme() {
+        super(HTTP, DEFAULT_PORT);
+    }
+
+    protected HttpScheme(String name, int port) {
+        super(name, port);
+    }
+
+    @Override
+    public IRI normalize(IRI iri) {
+        StringBuilder buf = new StringBuilder();
+        int port = (iri.getPort() == port()) ? -1 : iri.getPort();
+        String host = iri.getHost();
+        if (host != null)
+            host = host.toLowerCase();
+        String ui = iri.getUserInfo();
+        iri.buildAuthority(buf, ui, host, port);
+        String authority = buf.toString();
+        return new IRI(
+            iri._scheme, 
+            authority, 
+            ui, 
+            host, 
+            port, 
+            IRI.normalize(
+                iri.getPath()),
+                UrlEncoding.encode(
+                    UrlEncoding.decode(
+                        iri.getQuery()), 
+                    Profile.IQUERY), 
+                UrlEncoding.encode(
+                    UrlEncoding.decode(
+                        iri.getFragment()), 
+                    Profile.IFRAGMENT));
+    }
+
+    // use the path normalization coded into the IRI class
+    public String normalizePath(String path) {
+        return null;
+    }
+
+}

Propchange: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/HttpScheme.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/IRI.java
URL: http://svn.apache.org/viewvc/abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/IRI.java?rev=1173209&view=auto
==============================================================================
--- abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/IRI.java (added)
+++ abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/IRI.java Tue Sep 20 15:56:46 2011
@@ -0,0 +1,600 @@
+/*
+ * 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.abdera2.common.iri;
+
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.abdera2.common.text.CharUtils;
+import org.apache.abdera2.common.text.InvalidCharacterException;
+import org.apache.abdera2.common.text.NormalizationForm;
+import org.apache.abdera2.common.text.UrlEncoding;
+import org.apache.abdera2.common.text.CharUtils.Profile;
+
+import com.ibm.icu.text.IDNA;
+
+public final class IRI implements Serializable, Cloneable {
+
+    private static final long serialVersionUID = -4530530782760282284L;
+    protected Scheme _scheme;
+    private String authority;
+    private String userinfo;
+    private String host;
+    private int port = -1;
+    private String path;
+    private String query;
+    private String fragment;
+
+    private String a_host;
+    private String a_fragment;
+    private String a_path;
+    private String a_query;
+    private String a_userinfo;
+    private String a_authority;
+
+    public IRI(java.net.URL url) {
+        this(url.toString());
+    }
+
+    public IRI(java.net.URI uri) {
+        this(uri.toString());
+    }
+
+    public IRI(String iri) {
+        parse(iri);
+        init();
+    }
+
+    public IRI(String iri, NormalizationForm nf) throws IOException {
+        this(nf.normalize(iri));
+    }
+
+    public IRI(String scheme, String userinfo, String host, int port, String path, String query, String fragment) {
+        this._scheme = SchemeRegistry.get(scheme);
+        this.userinfo = userinfo;
+        this.host = host;
+        this.port = port;
+        this.path = path;
+        this.query = query;
+        this.fragment = fragment;
+        StringBuilder buf = new StringBuilder();
+        buildAuthority(buf, userinfo, host, port);
+        this.authority = (buf.length() != 0) ? buf.toString() : null;
+        init();
+    }
+
+    public IRI(String scheme, String authority, String path, String query, String fragment) {
+        this._scheme = SchemeRegistry.get(scheme);
+        this.authority = authority;
+        this.path = path;
+        this.query = query;
+        this.fragment = fragment;
+        parseAuthority();
+        init();
+    }
+
+    public IRI(String scheme, String host, String path, String fragment) {
+        this(scheme, null, host, -1, path, null, fragment);
+    }
+
+    IRI(Scheme _scheme,
+        String authority,
+        String userinfo,
+        String host,
+        int port,
+        String path,
+        String query,
+        String fragment) {
+        this._scheme = _scheme;
+        this.authority = authority;
+        this.userinfo = userinfo;
+        this.host = host;
+        this.port = port;
+        this.path = path;
+        this.query = query;
+        this.fragment = fragment;
+        init();
+    }
+
+    private void init() {
+        if (host != null && host.startsWith("[")) {
+            a_host = host;
+        } else {
+            try {
+                if (getHost() != null) {
+                  a_host = IDNA.convertIDNToASCII(host, IDNA.USE_STD3_RULES).toString();
+                }
+            } catch (Throwable t) {
+                throw new IRISyntaxException("Invalid Internationalized Domain Name");
+            }
+        }
+        a_fragment = UrlEncoding.encode(fragment, Profile.FRAGMENT);
+        a_path = UrlEncoding.encode(path, Profile.PATH);
+        a_query = UrlEncoding.encode(query, Profile.QUERY, Profile.PATH);
+        a_userinfo = UrlEncoding.encode(userinfo, Profile.USERINFO);
+        a_authority = buildASCIIAuthority();
+    }
+
+    @Override
+    public int hashCode() {
+        final int PRIME = 31;
+        int result = 1;
+        result = PRIME * result + ((authority == null) ? 0 : authority.hashCode());
+        result = PRIME * result + ((fragment == null) ? 0 : fragment.hashCode());
+        result = PRIME * result + ((host == null) ? 0 : host.hashCode());
+        result = PRIME * result + ((path == null) ? 0 : path.hashCode());
+        result = PRIME * result + port;
+        result = PRIME * result + ((query == null) ? 0 : query.hashCode());
+        result = PRIME * result + ((_scheme == null) ? 0 : _scheme.hashCode());
+        result = PRIME * result + ((userinfo == null) ? 0 : userinfo.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 IRI other = (IRI)obj;
+        if (authority == null) {
+            if (other.authority != null)
+                return false;
+        } else if (!authority.equals(other.authority))
+            return false;
+        if (fragment == null) {
+            if (other.fragment != null)
+                return false;
+        } else if (!fragment.equals(other.fragment))
+            return false;
+        if (host == null) {
+            if (other.host != null)
+                return false;
+        } else if (!host.equals(other.host))
+            return false;
+        if (path == null) {
+            if (other.path != null)
+                return false;
+        } else if (!path.equals(other.path))
+            return false;
+        if (port != other.port)
+            return false;
+        if (query == null) {
+            if (other.query != null)
+                return false;
+        } else if (!query.equals(other.query))
+            return false;
+        if (_scheme == null) {
+            if (other._scheme != null)
+                return false;
+        } else if (!_scheme.equals(other._scheme))
+            return false;
+        if (userinfo == null) {
+            if (other.userinfo != null)
+                return false;
+        } else if (!userinfo.equals(other.userinfo))
+            return false;
+        return true;
+    }
+
+    public String getAuthority() {
+        return (authority != null && authority.length() > 0) ? authority : null;
+    }
+
+    public String getFragment() {
+        return fragment;
+    }
+
+    public String getHost() {
+        return (host != null && host.length() > 0) ? host : null;
+    }
+
+    public String getASCIIHost() {
+        return (a_host != null && a_host.length() > 0) ? a_host : null;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public String getQuery() {
+        return query;
+    }
+
+    public String getScheme() {
+      return _scheme != null ? _scheme.name() : null;
+    }
+
+    public String getSchemeSpecificPart() {
+        return buildSchemeSpecificPart(authority, path, query, fragment);
+    }
+
+    public String getUserInfo() {
+        return userinfo;
+    }
+
+    void buildAuthority(StringBuilder buf, String aui, String ah, int port) {
+        if (aui != null && aui.length() != 0)
+            buf.append(aui)
+               .append('@');
+        if (ah != null && ah.length() != 0)
+            buf.append(ah);
+        if (port != -1)
+            buf.append(':')
+               .append(port);
+    }
+
+    private String buildASCIIAuthority() {
+        if (_scheme instanceof HttpScheme) {
+            StringBuilder buf = new StringBuilder();
+            buildAuthority(buf, getASCIIUserInfo(), getASCIIHost(), getPort());
+            return buf.toString();
+        } else {
+            return UrlEncoding.encode(authority, Profile.AUTHORITY);
+        }
+    }
+
+    public String getASCIIAuthority() {
+        return (a_authority != null && a_authority.length() > 0) ? a_authority : null;
+    }
+
+    public String getASCIIFragment() {
+        return a_fragment;
+    }
+
+    public String getASCIIPath() {
+        return a_path;
+    }
+
+    public String getASCIIQuery() {
+        return a_query;
+    }
+
+    public String getASCIIUserInfo() {
+        return a_userinfo;
+    }
+
+    public String getASCIISchemeSpecificPart() {
+        return buildSchemeSpecificPart(a_authority, a_path, a_query, a_fragment);
+    }
+
+    private String buildSchemeSpecificPart(String authority, String path, String query, String fragment) {
+        StringBuilder buf = new StringBuilder();
+        if (authority != null)
+            buf.append("//")
+               .append(authority);
+        if (path != null && path.length() != 0)
+            buf.append(path);
+        if (query != null)
+            buf.append('?')
+               .append(query);
+        if (fragment != null)
+            buf.append('#')
+               .append(fragment);
+        return buf.toString();
+    }
+
+    public Object clone() {
+        try {
+            return super.clone();
+        } catch (CloneNotSupportedException e) {
+            return new IRI(toString()); // not going to happen, but we have to
+                                        // catch it just in case
+        }
+    }
+
+    public boolean isAbsolute() {
+        return _scheme != null;
+    }
+
+    public boolean isOpaque() {
+        return path == null;
+    }
+
+    public static IRI relativize(IRI b, IRI c) {
+        if (c.isOpaque() || b.isOpaque())
+            return c;
+        if ((b._scheme == null && c._scheme != null) || 
+            (b._scheme != null && c._scheme == null) || 
+            (b._scheme != null && c._scheme != null && 
+             !b._scheme.equals(c._scheme)))
+            return c;
+        String bpath = normalize(b.getPath());
+        String cpath = normalize(c.getPath());
+        bpath = (bpath != null) ? bpath : "/";
+        cpath = (cpath != null) ? cpath : "/";
+        if (!bpath.equals(cpath)) {
+            if (bpath.charAt(bpath.length() - 1) != '/')
+                bpath += "/";
+            if (!cpath.startsWith(bpath))
+                return c;
+        }
+        IRI iri =
+            new IRI(null, null, null, null, -1, normalize(cpath.substring(bpath.length())), c.getQuery(), c
+                .getFragment());
+        return iri;
+    }
+
+    public IRI relativize(IRI iri) {
+        return relativize(this, iri);
+    }
+
+    public boolean isPathAbsolute() {
+        String path = getPath();
+        return (path != null) && path.length() > 0 && path.charAt(0) == '/';
+    }
+
+    public boolean isSameDocumentReference() {
+        return _scheme == null && authority == null
+            && (path == null || path.length() == 0 || path.equals("."))
+            && query == null;
+    }
+
+    public static IRI resolve(IRI b, String c) throws IOException {
+        return resolve(b, new IRI(c));
+    }
+
+    public static IRI resolve(IRI b, IRI c) {
+
+        if (c == null)
+            return null;
+        if ("".equals(c.toString()) || "#".equals(c.toString())
+            || ".".equals(c.toString())
+            || "./".equals(c.toString()))
+            return b;
+        if (b == null)
+            return c;
+
+        if (c.isOpaque() || b.isOpaque())
+            return c;
+        if (c.isSameDocumentReference()) {
+            String cfragment = c.getFragment();
+            String bfragment = b.getFragment();
+            if ((cfragment == null && bfragment == null) || (cfragment != null && cfragment.equals(bfragment))) {
+                return (IRI)b.clone();
+            } else {
+                return new IRI(b._scheme, b.getAuthority(), b.getUserInfo(), b.getHost(), b.getPort(),
+                               normalize(b.getPath()), b.getQuery(), cfragment);
+            }
+        }
+        if (c.isAbsolute())
+            return c;
+
+        Scheme _scheme = b._scheme;
+        String query = c.getQuery();
+        String fragment = c.getFragment();
+        String userinfo = null;
+        String authority = null;
+        String host = null;
+        int port = -1;
+        String path = null;
+        if (c.getAuthority() == null) {
+            authority = b.getAuthority();
+            userinfo = b.getUserInfo();
+            host = b.getHost();
+            port = b.getPort();
+            path = c.isPathAbsolute() ? normalize(c.getPath()) : resolve(b.getPath(), c.getPath());
+        } else {
+            authority = c.getAuthority();
+            userinfo = c.getUserInfo();
+            host = c.getHost();
+            port = c.getPort();
+            path = normalize(c.getPath());
+        }
+        return new IRI(_scheme, authority, userinfo, host, port, path, query, fragment);
+    }
+
+    public IRI normalize() {
+        return normalize(this);
+    }
+
+    public static String normalizeString(String iri) {
+        return normalize(new IRI(iri)).toString();
+    }
+
+    public static IRI normalize(IRI iri) {
+        if (iri.isOpaque() || iri.getPath() == null)
+            return iri;
+        IRI normalized = null;
+        if (iri._scheme != null)
+            normalized = iri._scheme.normalize(iri);
+        return (normalized != null) ? 
+            normalized : 
+            new IRI(
+                iri._scheme, 
+                iri.getAuthority(), 
+                iri.getUserInfo(), 
+                iri.getHost(), 
+                iri.getPort(), 
+                normalize(iri.getPath()), 
+                UrlEncoding.encode(
+                    UrlEncoding.decode(
+                      iri.getQuery()), 
+                    Profile.IQUERY), 
+                UrlEncoding.encode(
+                    UrlEncoding.decode(
+                      iri.getFragment()), 
+                    Profile.IFRAGMENT));
+    }
+
+    protected static String normalize(String path) {
+      if (path == null || path.length() == 0)
+          return "/";
+      String[] segments = path.split("/");
+      boolean trailingslash = 
+        path.matches(".*/\\.{1}|.*\\./\\.{2}|.*/{1}$");
+      int pos = 0;
+      for (String segment : segments)
+          if ("..".equals(segment)) {
+              pos = (pos-1>-1)?pos-1:0;
+              segments[pos] = null;
+          } else if (!".".equals(segment))
+            segments[pos++] = segment;
+      if (pos < segments.length) 
+        segments[pos] = null;
+      StringBuilder buf = new StringBuilder();
+      buf.ensureCapacity(path.length());
+      if (pos != 0) {
+        pos = 0;
+        while (pos<segments.length) {
+          String segment = segments[pos++];
+          if (segment == null) break;
+          if(pos-1>0||segment.length()>0)
+            buf.append('/');
+          buf.append(
+               UrlEncoding.encode(
+                 UrlEncoding.decode(segment), 
+                   Profile.IPATHNODELIMS_SEG));
+        }
+      }
+      if (trailingslash && buf.length() > 1)
+        buf.append('/');
+      return buf.toString();
+    }
+
+    private static String resolve(String bpath, String cpath) {
+        if (bpath == null && cpath == null)
+            return null;
+        if (bpath == null && cpath != null) {
+            return (!cpath.startsWith("/")) ? "/" + cpath : cpath;
+        }
+        if (bpath != null && cpath == null)
+            return bpath;
+        if (bpath.equals(cpath))
+          return bpath;
+        StringBuilder buf = new StringBuilder("");
+        int n = bpath.lastIndexOf('/');
+        if (n > -1)
+            buf.append(bpath.substring(0, n + 1));
+        if (cpath.length() != 0)
+            buf.append(cpath);
+        if (buf.charAt(0) != '/')
+            buf.insert(0, '/');
+        return normalize(buf.toString());
+    }
+
+    public IRI resolve(IRI iri) {
+        return resolve(this, iri);
+    }
+
+    public IRI resolve(String iri) {
+        return resolve(this, new IRI(iri));
+    }
+
+    public String toString() {
+        StringBuilder buf = new StringBuilder();
+        String scheme = getScheme();
+        if (scheme != null && scheme.length() != 0)
+            buf.append(scheme)
+               .append(':');
+        buf.append(getSchemeSpecificPart());
+        return UrlEncoding.encode(buf.toString(), Profile.SCHEMESPECIFICPART);
+    }
+
+    public String toASCIIString() {
+        StringBuilder buf = new StringBuilder();
+        String scheme = getScheme();
+        if (scheme != null && scheme.length() != 0)
+            buf.append(scheme)
+               .append(':');
+        buf.append(getASCIISchemeSpecificPart());
+        return buf.toString();
+    }
+
+    public java.net.URI toURI() throws URISyntaxException {
+        return new java.net.URI(toASCIIString());
+    }
+
+    public java.net.URL toURL() throws MalformedURLException, URISyntaxException {
+        return toURI().toURL();
+    }
+
+    private void parseAuthority() {
+        if (authority != null) {
+            Matcher auth = AUTHORITYPATTERN.matcher(authority);
+            if (auth.find()) {
+                userinfo = auth.group(1);
+                host = auth.group(2);
+                if (auth.group(3) != null)
+                    port = Integer.parseInt(auth.group(3));
+                else
+                    port = -1;
+            }
+            try {
+                CharUtils.verify(userinfo, Profile.IUSERINFO);
+                CharUtils.verify(host, Profile.IHOST);
+            } catch (InvalidCharacterException e) {
+                throw new IRISyntaxException(e);
+            }
+        }
+    }
+
+    private void parse(String iri) {
+        try {
+            Matcher irim = IRIPATTERN.matcher(iri);
+            if (irim.find()) {
+                String scheme = irim.group(1);
+                _scheme = SchemeRegistry.get(scheme);
+                authority = irim.group(2);
+                path = irim.group(3);
+                query = irim.group(4);
+                fragment = irim.group(5);
+                parseAuthority();
+                try {
+                    CharUtils.verify(scheme, Profile.SCHEME);
+                    CharUtils.verify(path, Profile.IPATH);
+                    CharUtils.verify(query, Profile.IQUERY);
+                    CharUtils.verify(fragment, Profile.IFRAGMENT);
+                } catch (InvalidCharacterException e) {
+                    throw new IRISyntaxException(e);
+                }
+            } else {
+                throw new IRISyntaxException("Invalid Syntax");
+            }
+        } catch (IRISyntaxException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new IRISyntaxException(e);
+        }
+    }
+
+    private static final Pattern IRIPATTERN =
+        Pattern.compile("^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*)(?:\\?([^#]*))?(?:#(.*))?");
+
+    private static final Pattern AUTHORITYPATTERN =
+        Pattern.compile("^(?:(.*)?@)?((?:\\[.*\\])|(?:[^:]*))?(?::(\\d+))?");
+
+    /**
+     * Returns a new IRI with a trailing slash appended to the path, if necessary.
+     * The query and fragment are omitted from the resulting IRI
+     */
+    public IRI trailingSlash() {
+      return this.resolve(path + "/");
+    }
+
+}

Propchange: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/IRI.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/IRISyntaxException.java
URL: http://svn.apache.org/viewvc/abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/IRISyntaxException.java?rev=1173209&view=auto
==============================================================================
--- abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/IRISyntaxException.java (added)
+++ abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/IRISyntaxException.java Tue Sep 20 15:56:46 2011
@@ -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.abdera2.common.iri;
+
+public class IRISyntaxException extends RuntimeException {
+    private static final long serialVersionUID = 5177739661976965423L;
+
+    IRISyntaxException(String message) {
+        super(message);
+    }
+
+    IRISyntaxException(Throwable cause) {
+        super(cause);
+    }
+
+}

Propchange: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/IRISyntaxException.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/Scheme.java
URL: http://svn.apache.org/viewvc/abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/Scheme.java?rev=1173209&view=auto
==============================================================================
--- abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/Scheme.java (added)
+++ abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/Scheme.java Tue Sep 20 15:56:46 2011
@@ -0,0 +1,35 @@
+/*
+ * 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.abdera2.common.iri;
+
+import java.io.Serializable;
+
+/**
+ * Interface implemented by custom IRI scheme parsers
+ */
+public interface Scheme extends Serializable {
+
+    String name();
+
+    IRI normalize(IRI iri);
+
+    String normalizePath(String path);
+
+    int port();
+    
+}

Propchange: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/Scheme.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/SchemeRegistry.java
URL: http://svn.apache.org/viewvc/abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/SchemeRegistry.java?rev=1173209&view=auto
==============================================================================
--- abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/SchemeRegistry.java (added)
+++ abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/SchemeRegistry.java Tue Sep 20 15:56:46 2011
@@ -0,0 +1,91 @@
+/*
+ * 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.abdera2.common.iri;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Static registry of custom IRI schemes.
+ */
+public final class SchemeRegistry {
+
+    private static final SchemeRegistry registry = 
+      new SchemeRegistry();
+
+    public static synchronized SchemeRegistry getInstance() {
+        return registry;
+    }
+    
+    public static Scheme get(String name) {
+      return getInstance().getScheme(name);
+    }
+
+    private final Map<String, Scheme> schemes;
+
+    SchemeRegistry() {
+        schemes = new HashMap<String, Scheme>();
+        schemes.put(
+          HttpScheme.HTTP, 
+          new HttpScheme());
+        schemes.put(
+          HttpScheme.HTTPS, 
+          new HttpScheme(
+            "https",
+            HttpScheme.HTTPS_DEFAULT_PORT));
+        schemes.put(
+          FtpScheme.NAME, 
+          new FtpScheme());
+    }
+
+    @SuppressWarnings("unchecked")
+    public synchronized boolean register(String schemeClass) throws ClassNotFoundException, IllegalAccessException,
+        InstantiationException {
+        Class<Scheme> klass = (Class<Scheme>)Thread.currentThread().getContextClassLoader().loadClass(schemeClass);
+        return register(klass);
+    }
+
+    public synchronized boolean register(Class<Scheme> schemeClass) throws IllegalAccessException,
+        InstantiationException {
+        Scheme scheme = schemeClass.newInstance();
+        return register(scheme);
+    }
+
+    public synchronized boolean register(Scheme scheme) {
+        String name = scheme.name();
+        if (schemes.containsKey(name)) return false;
+        schemes.put(name.toLowerCase(), scheme);
+        return true;
+    }
+
+    public Scheme getScheme(String scheme) {
+        if (scheme == null) return null;
+        Scheme s = schemes.get(scheme.toLowerCase());
+        return (s != null) ? s : new DefaultScheme(scheme);
+    }
+
+    static class DefaultScheme extends AbstractScheme {
+      private static final long serialVersionUID = -4281240995159910129L;
+      public DefaultScheme(String name) {
+          super(name, -1);
+      }
+      public DefaultScheme(String name, int port) {
+          super(name, port);
+      }
+    }
+}

Propchange: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/iri/SchemeRegistry.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/Lang.java
URL: http://svn.apache.org/viewvc/abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/Lang.java?rev=1173209&view=auto
==============================================================================
--- abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/Lang.java (added)
+++ abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/Lang.java Tue Sep 20 15:56:46 2011
@@ -0,0 +1,328 @@
+/*
+ * 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.abdera2.common.lang;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.abdera2.common.lang.Subtag.Type;
+
+/**
+ * Implementation of RFC 4646 Language Tags. Instances are immutable and 
+ * safe for use by multiple threads. Iterators returned by calling 
+ * the iterator() method, however, are not threadsafe and should only
+ * ever be accessed by a single thread.
+ */
+public final class Lang 
+  extends SubtagSet {
+
+    private static final long serialVersionUID = -7095560018906537331L;
+    private final Locale locale;
+
+    private Subtag t_extlang, t_script, t_variant, 
+                   t_region, t_extension, t_privateuse;
+    
+
+    public Lang() {
+        this(init(Locale.getDefault()));
+    }
+
+    public Lang(Locale locale) {
+        this(init(locale));
+    }
+
+    private static Subtag init(Locale locale) {
+        try {
+            return parse(locale.toString()).root;
+        } catch (Exception e) {
+            Subtag c = null, primary = new Subtag(Type.LANGUAGE, locale.getLanguage());
+            String country = locale.getCountry();
+            String variant = locale.getVariant();
+            if (country != null)
+                c = new Subtag(Type.REGION, country, primary);
+            if (variant != null)
+                new Subtag(Type.VARIANT, variant, c);
+            return primary;
+        }
+    }
+
+    public Lang(String lang) {
+        this(parse(lang).root);
+    }
+
+    Lang(Subtag root) {
+        super(root);
+        scan();
+        this.locale = initLocale();
+    }
+
+    private Locale initLocale() {
+        Subtag primary = language();
+        Subtag region = region();
+        Subtag variant = variant();
+        if (variant != null && region != null)
+            return new Locale(primary.toString(), region.toString(), variant.toString());
+        else if (region != null)
+            return new Locale(primary.toString(), region.toString());
+        else
+            return new Locale(primary.toString());
+    }
+
+    public Subtag language() {
+        return root;
+    }
+
+    public Locale locale() {
+        return locale;
+    }
+
+    private void scan() {
+      for (Subtag subtag : this) {
+        switch (subtag.type()) {
+          case EXTLANG:
+            t_extlang = subtag; 
+            break;
+          case SCRIPT:
+            t_script = subtag; 
+            break;
+          case VARIANT:
+            t_variant = subtag; 
+            break;
+          case REGION:
+            t_region = subtag; 
+            break;
+          case SINGLETON:
+            if (subtag.isExtensionSingleton() && t_extension == null)
+              t_extension = subtag;
+            else if (subtag.isPrivateSingleton())
+              t_privateuse = subtag;
+          default: break;
+        }
+      }
+    }
+    
+    public Subtag extlang() {
+      return t_extlang;
+    }
+
+    public Subtag script() {
+      return t_script;
+    }
+
+    public Subtag region() {
+      return t_region;
+    }
+
+    public Subtag variant() {
+      return t_variant;
+    }
+
+    public Subtag extension() {
+      return t_extension;
+    }
+    
+    public Subtag[] extensions() {
+      List<Subtag> list = new ArrayList<Subtag>();
+      Subtag extension = extension();
+      while(extension != null) {
+        list.add(extension);
+        extension = nextExtension(extension);
+      }
+      return list.toArray(new Subtag[list.size()]);
+    }
+    
+    public Subtag extension(String name) {
+      Subtag subtag = extension(); // go to first extension;
+      while(subtag != null && !subtag.name().equals(name))
+        subtag = nextExtension(subtag);
+      return subtag;
+    }
+    
+    private static Subtag nextExtension(Subtag subtag) {
+      subtag = subtag.next();
+      while (!subtag.isSingleton())
+        subtag = subtag.next();
+      return subtag.isExtensionSingleton() ? subtag : null;
+    }
+
+    public Subtag privateUse() {
+      return t_privateuse;
+    }
+
+    public Range asRange() {
+        return new Range(toString());
+    }
+    
+    public Lang parent() {
+        Subtag root, prev, temp;
+        Iterator<Subtag> i = iterator();
+        temp = i.next();
+        root = new Subtag(
+          temp.type(), 
+          temp.name());
+        prev = root;
+        while (i.hasNext()) {
+          temp = i.next();
+          if (i.hasNext())
+            prev = new Subtag(
+              temp.type(), 
+              temp.name(), 
+              prev);
+        }
+        return new Lang(root);
+    }
+    
+    public Lang copy() {
+      Subtag root, prev, temp;
+      Iterator<Subtag> i = iterator();
+      temp = i.next();
+      root = new Subtag(
+        temp.type(), 
+        temp.name());
+      prev = root;
+      while (i.hasNext()) {
+        temp = i.next();
+        prev = new Subtag(
+          temp.type(), 
+          temp.name(), 
+          prev);
+      }
+      return new Lang(root);
+  }
+
+    public boolean isGrandfathered() {
+      for (Subtag tag : this)
+        if (tag.type() == Type.GRANDFATHERED)
+          return true;
+      return false;
+    }
+    
+    public boolean isChildOf(Lang lang) {
+        Range range = new Range(lang).appendWildcard();
+        return range.matches(this);
+    }
+
+    public boolean isParentOf(Lang lang) {
+        return lang.isChildOf(this);
+    }
+
+    // Parsing Logic
+
+    private static final String SEP = "\\s*[-_]\\s*";
+    private static final String language = "((?:[a-zA-Z]{2,3}(?:[-_][a-zA-Z]{3}){0,3})|[a-zA-Z]{4}|[a-zA-Z]{5,8})";
+    private static final String script = "((?:[-_][a-zA-Z]{4})?)";
+    private static final String region = "((?:[-_](?:(?:[a-zA-Z]{2})|(?:[0-9]{3})))?)";
+    private static final String variant = "((?:[-_](?:(?:[a-zA-Z0-9]{5,8})|(?:[0-9][a-zA-Z0-9]{3})))*)";
+    private static final String extension = "((?:[-_][a-wy-zA-WY-Z0-9](?:[-_][a-zA-Z0-9]{2,8})+)*)";
+    private static final String privateuse = "[xX](?:[-_][a-zA-Z0-9]{2,8})+";
+    private static final String _privateuse = "((?:[-_]" + privateuse + ")?)";
+    private static final String grandfathered =
+        "^(?:art[-_]lojban|cel[-_]gaulish|en[-_]GB[-_]oed|i[-_]ami|i[-_]bnn|i[-_]default|i[-_]enochian|i[-_]hak|i[-_]klingon|i[-_]lux|i[-_]mingo|i[-_]navajo|i[-_]pwn|i[-_]tao||i[-_]tay|i[-_]tsu|no[-_]bok|no[-_]nyn|sgn[-_]BE[-_]fr|sgn[-_]BE[-_]nl|sgn[-_]CH[-_]de|zh[-_]cmn|zh[-_]cmn[-_]Hans|zh[-_]cmn[-_]Hant|zh[-_]gan|zh[-_]guoyu|zh[-_]hakka|zh[-_]min|zh[-_]min[-_]nan|zh[-_]wuu|zh[-_]xiang|zh[-_]yue)$";
+    private static final String langtag = "^" + language + script + region + variant + extension + _privateuse + "$";
+
+    private static final Pattern p_langtag = Pattern.compile(langtag);
+    private static final Pattern p_privateuse = Pattern.compile("^" + privateuse + "$");
+    private static final Pattern p_grandfathered = Pattern.compile(grandfathered);
+
+    /**
+     * Parse a Lang tag
+     */
+    public static Lang parse(String lang) {
+        if (lang == null || lang.length() == 0)
+          throw new IllegalArgumentException();
+        Subtag primary = null;
+        Matcher m = p_grandfathered.matcher(lang);
+        if (m.find()) {
+            String[] tags = lang.split(SEP);
+            Subtag current = null;
+            for (String tag : tags)
+                current = current == null ?
+                    primary = new Subtag(Type.GRANDFATHERED, tag) :
+                    new Subtag(Type.GRANDFATHERED, tag, current);
+            return new Lang(primary);
+        }
+        m = p_privateuse.matcher(lang);
+        if (m.find()) {
+            String[] tags = lang.split(SEP);
+            Subtag current = null;
+            for (String tag : tags)
+                current = current == null ?
+                    primary = new Subtag(Type.SINGLETON, tag) :
+                    new Subtag(Type.PRIVATEUSE, tag, current);
+            return new Lang(primary);
+        }
+        m = p_langtag.matcher(lang);
+        if (m.find()) {
+            String langtag = m.group(1);
+            String script = m.group(2);
+            String region = m.group(3);
+            String variant = m.group(4);
+            String extension = m.group(5);
+            String privateuse = m.group(6);
+            Subtag current = null;
+            String[] tags = langtag.split(SEP);
+            for (String tag : tags)
+                current = current == null ? 
+                    primary = new Subtag(Type.LANGUAGE, tag) :
+                    new Subtag(Type.EXTLANG, tag, current);
+            if (script != null && script.length() > 0)
+                current = new Subtag(Type.SCRIPT, script.substring(1), current);
+            if (region != null && region.length() > 0)
+                current = new Subtag(Type.REGION, region.substring(1), current);
+            if (variant != null && variant.length() > 0) {
+                variant = variant.substring(1);
+                tags = variant.split(SEP);
+                for (String tag : tags)
+                    current = new Subtag(Type.VARIANT, tag, current);
+            }
+            if (extension != null && extension.length() > 0) {
+                extension = extension.substring(1);
+                tags = extension.split(SEP);
+                current = new Subtag(Type.SINGLETON, tags[0], current);
+                for (int i = 1; i < tags.length; i++) {
+                    String tag = tags[i];
+                    current = new Subtag(
+                        tag.length() == 1 ? 
+                            Type.SINGLETON : 
+                            Type.EXTENSION, 
+                        tag, 
+                        current);
+                }
+            }
+            if (privateuse != null && privateuse.length() > 0) {
+                privateuse = privateuse.substring(1);
+                tags = privateuse.split(SEP);
+                current = new Subtag(Type.SINGLETON, tags[0], current);
+                for (int i = 1; i < tags.length; i++)
+                    current = new Subtag(Type.PRIVATEUSE, tags[i], current);
+            }
+            return new Lang(primary);
+        }
+        throw new IllegalArgumentException();
+    }
+
+    public static String fromLocale(Locale locale) {
+        return new Lang(locale).toString();
+    }
+}
+
+

Propchange: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/Lang.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/Range.java
URL: http://svn.apache.org/viewvc/abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/Range.java?rev=1173209&view=auto
==============================================================================
--- abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/Range.java (added)
+++ abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/Range.java Tue Sep 20 15:56:46 2011
@@ -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.abdera2.common.lang;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.abdera2.common.lang.Subtag.Type;
+
+/**
+ * A language range used for matching language tags
+ */
+public final class Range 
+  extends SubtagSet {
+
+    private static final long serialVersionUID = -6397227794306856431L;
+    private final boolean extended;
+
+    public Range(String range, boolean extended) {
+        super(parse(range, extended).root);
+        this.extended = extended;
+    }
+
+    public Range(String range) {
+        this(parse(range).root);
+    }
+
+    public Range(Lang lang) {
+        this(lang.toString());
+    }
+
+    public Range(Lang lang, boolean extended) {
+        this(lang.toString(), extended);
+    }
+
+    Range(Subtag primary) {
+        super(primary);
+        this.extended = !checkBasic();
+    }
+
+    public Range append(Subtag subtag) {
+        Subtag last = null;
+        for (Subtag tag : this)
+            last = tag;
+        last.setNext(subtag);
+        return this;
+    }
+
+    public Range appendWildcard() {
+        return append(Subtag.newWildcard());
+    }
+
+    public Range toBasicRange() {
+        if (root.type() == Subtag.Type.WILDCARD) {
+            return new Range("*");
+        } else {
+            List<Subtag> list = new LinkedList<Subtag>();
+            for (Subtag tag : this) {
+                if (tag.type() != Subtag.Type.WILDCARD)
+                    list.add(new Subtag(tag.type(),tag.name(),null));
+            }
+            Subtag primary = null, current = null;
+            for (Subtag tag : list) {
+                tag.setNext(null);
+                tag.setPrevious(null);
+                if (primary == null) {
+                    primary = tag;
+                    current = primary;
+                } else {
+                    current.setNext(tag);
+                    current = tag;
+                }
+            }
+            return new Range(primary);
+        }
+    }
+
+    public boolean isBasic() {
+        return !extended;
+    }
+
+    private boolean checkBasic() {
+        Subtag current = root.next();
+        while (current != null) {
+            if (current.type() == Subtag.Type.WILDCARD)
+                return false;
+            current = current.next();
+        }
+        return true;
+    }
+
+    public boolean matches(String lang) {
+        return matches(new Lang(lang), extended);
+    }
+
+    public boolean matches(String lang, boolean extended) {
+        return matches(new Lang(lang), extended);
+    }
+
+    public boolean matches(Lang lang) {
+        return matches(lang, false);
+    }
+
+    public boolean matches(Lang lang, boolean extended) {
+        Iterator<Subtag> i = iterator();
+        Iterator<Subtag> e = lang.iterator();
+        if (isBasic() && !extended) {
+            if (root.type() == Subtag.Type.WILDCARD)
+                return true;
+            for (; i.hasNext() && e.hasNext();) {
+                Subtag in = i.next();
+                Subtag en = e.next();
+                if (!in.equals(en))
+                    return false;
+            }
+            return true;
+        } else {
+            Subtag icurrent = i.next();
+            Subtag ecurrent = e.next();
+            if (!icurrent.equals(ecurrent))
+                return false;
+
+            while (i.hasNext()) {
+                icurrent = i.next();
+                while (icurrent.type() == Subtag.Type.WILDCARD && i.hasNext())
+                    icurrent = i.next();
+                // the range ends in a wildcard so it will match everything beyond this point
+                if (icurrent.type() == Subtag.Type.WILDCARD)
+                    return true;
+                boolean matched = false;
+                while (e.hasNext()) {
+                    ecurrent = e.next();
+                    if (extended && (ecurrent.type().ordinal() < icurrent.type().ordinal()))
+                        continue;
+                    if (!ecurrent.equals(icurrent))
+                        break;
+                    else {
+                        matched = true;
+                        break;
+                    }
+                }
+                if (!matched)
+                    return false;
+            }
+            return true;
+        }
+    }
+
+    public Lang[] filter(Lang... lang) {
+        List<Lang> langs = new LinkedList<Lang>();
+        for (Lang l : lang)
+            if (matches(l))
+                langs.add(l);
+        return langs.toArray(new Lang[langs.size()]);
+    }
+
+    public String[] filter(String... lang) {
+        List<String> langs = new LinkedList<String>();
+        for (String l : lang)
+            if (matches(l))
+                langs.add(l);
+        return langs.toArray(new String[langs.size()]);
+    }
+
+    public static Lang[] filter(String range, Lang... lang) {
+        return new Range(range).filter(lang);
+    }
+
+    public static String[] filter(String range, String... lang) {
+        return new Range(range).filter(lang);
+    }
+
+    public static boolean matches(String range, Lang lang, boolean extended) {
+        return new Range(range, extended).matches(lang);
+    }
+
+    public static boolean matches(String range, Lang lang) {
+        return new Range(range).matches(lang);
+    }
+
+    public static boolean matches(String range, String lang, boolean extended) {
+        return new Range(range, extended).matches(lang);
+    }
+
+    public static boolean matches(String range, String lang) {
+        return new Range(range).matches(lang);
+    }
+
+    // Parsing logic //
+
+    private static final String SEP = "\\s*[-_]\\s*";
+    private static final String range = "((?:[a-zA-Z]{1,8}|\\*))((?:[-_](?:[a-zA-Z0-9]{1,8}|\\*))*)";
+    private static final String range_component = "[-_]((?:[a-zA-Z0-9]{1,8}|\\*))";
+    private static final Pattern p_range = Pattern.compile(range);
+    private static final Pattern p_range_component = Pattern.compile(range_component);
+
+    private static final String language =
+        "((?:[a-zA-Z]{2,3}(?:[-_](?:[a-zA-Z]{3}|\\*)){0,3})|[a-zA-Z]{4}|[a-zA-Z]{5,8}|\\*)";
+    private static final String script = "((?:[-_](?:[a-zA-Z]{4}|\\*))?)";
+    private static final String region = "((?:[-_](?:(?:[a-zA-Z]{2})|(?:[0-9]{3})|\\*))?)";
+    private static final String variant = "((?:[-_](?:(?:[a-zA-Z0-9]{5,8})|(?:[0-9][a-zA-Z0-9]{3})|\\*))*)";
+    private static final String extension = "((?:[-_](?:(?:[a-wy-zA-WY-Z0-9](?:[-_][a-zA-Z0-9]{2,8})+)|\\*))*)";
+    private static final String privateuse = "[xX](?:[-_][a-zA-Z0-9]{2,8})+";
+    private static final String _privateuse = "((?:[-_](?:" + privateuse + ")+|\\*)?)";
+    private static final String langtag = "^" + language + script + region + variant + extension + _privateuse + "$";
+    private static final String grandfathered =
+        "^(?:art[-_]lojban|cel[-_]gaulish|en[-_]GB[-_]oed|i[-_]ami|i[-_]bnn|i[-_]default|i[-_]enochian|i[-_]hak|i[-_]klingon|i[-_]lux|i[-_]mingo|i[-_]navajo|i[-_]pwn|i[-_]tao||i[-_]tay|i[-_]tsu|no[-_]bok|no[-_]nyn|sgn[-_]BE[-_]fr|sgn[-_]BE[-_]nl|sgn[-_]CH[-_]de|zh[-_]cmn|zh[-_]cmn[-_]Hans|zh[-_]cmn[-_]Hant|zh[-_]gan|zh[-_]guoyu|zh[-_]hakka|zh[-_]min|zh[-_]min[-_]nan|zh[-_]wuu|zh[-_]xiang|zh[-_]yue)$";
+    private static final Pattern p_privateuse = Pattern.compile("^" + privateuse + "$");
+    private static final Pattern p_grandfathered = Pattern.compile(grandfathered);
+    private static final Pattern p_extended_range = Pattern.compile(langtag);
+
+    /**
+     * Parse the language-range
+     */
+    public static Range parse(String range) {
+        return parse(range, false);
+    }
+
+    /**
+     * Parse the language-range
+     * 
+     * @param range The language-range
+     * @param extended true to use extended language rules
+     */
+    public static Range parse(String range, boolean extended) {
+        if (!extended) {
+            Subtag primary = null, current = null;
+            Matcher m = p_range.matcher(range);
+            if (m.find()) {
+                String first = m.group(1);
+                String therest = m.group(2);
+                current = primary =
+                    new Subtag(first.equals("*") ? 
+                        Subtag.Type.WILDCARD : 
+                        Subtag.Type.SIMPLE, first
+                        .toLowerCase(Locale.US));
+                Matcher n = p_range_component.matcher(therest);
+                while (n.find()) {
+                    String name = n.group(1).toLowerCase(Locale.US);
+                    current = new Subtag(
+                        name.equals("*") ? 
+                            Subtag.Type.WILDCARD : 
+                            Subtag.Type.SIMPLE, 
+                        name, 
+                        current);
+                }
+            }
+            return new Range(primary);
+        } else {
+
+            Subtag primary = null;
+            Matcher m = p_grandfathered.matcher(range);
+            if (m.find()) {
+                String[] tags = range.split(SEP);
+                Subtag current = null;
+                for (String tag : tags)
+                    current = current == null ?
+                        primary = new Subtag(Type.GRANDFATHERED, tag) :
+                        new Subtag(Type.GRANDFATHERED,tag,current);
+                return new Range(primary);
+            }
+            m = p_privateuse.matcher(range);
+            if (m.find()) {
+                String[] tags = range.split(SEP);
+                Subtag current = null;
+                for (String tag : tags)
+                    current = current == null ?
+                        primary = new Subtag(tag.equals("*") ? Type.WILDCARD : Type.SINGLETON, tag) :
+                        new Subtag(tag.equals("*") ? Type.WILDCARD : Type.PRIVATEUSE, tag, current);
+                return new Range(primary);
+            }
+            m = p_extended_range.matcher(range);
+            if (m.find()) {
+                String langtag = m.group(1);
+                String script = m.group(2);
+                String region = m.group(3);
+                String variant = m.group(4);
+                String extension = m.group(5);
+                String privateuse = m.group(6);
+                Subtag current = null;
+                String[] tags = langtag.split(SEP);
+                for (String tag : tags)
+                    current = current == null ?
+                        primary = new Subtag(tag.equals("*") ? Type.WILDCARD : Type.LANGUAGE, tag) :
+                        new Subtag(tag.equals("*") ? Type.WILDCARD : Type.EXTLANG, tag, current);
+                if (script != null && script.length() > 0)
+                    current =
+                        new Subtag(
+                            script.substring(1).equals("*") ? 
+                                Type.WILDCARD : 
+                                Type.SCRIPT, 
+                            script.substring(1),
+                            current);
+                if (region != null && region.length() > 0)
+                    current =
+                        new Subtag(
+                            region.substring(1).equals("*") ? 
+                                Type.WILDCARD : 
+                                Type.REGION, 
+                            region.substring(1),
+                            current);
+                if (variant != null && variant.length() > 0) {
+                    variant = variant.substring(1);
+                    tags = variant.split(SEP);
+                    for (String tag : tags)
+                        current = new Subtag(tag.equals("*") ? Type.WILDCARD : Type.VARIANT, tag, current);
+                }
+                if (extension != null && extension.length() > 0) {
+                    extension = extension.substring(1);
+                    tags = extension.split(SEP);
+                    current = new Subtag(tags[0].equals("*") ? Type.WILDCARD : Type.SINGLETON, tags[0], current);
+                    for (int i = 1; i < tags.length; i++) {
+                        String tag = tags[i];
+                        current =
+                            new Subtag(tag.equals("*") ? Type.WILDCARD : tag.length() == 1 ? Type.SINGLETON
+                                : Type.EXTENSION, tag, current);
+                    }
+                }
+                if (privateuse != null && privateuse.length() > 0) {
+                    privateuse = privateuse.substring(1);
+                    tags = privateuse.split(SEP);
+                    current = new Subtag(tags[0].equals("*") ? Type.WILDCARD : Type.SINGLETON, tags[0], current);
+                    for (int i = 1; i < tags.length; i++) {
+                        current = new Subtag(tags[i].equals("*") ? Type.WILDCARD : Type.PRIVATEUSE, tags[i], current);
+                    }
+                }
+                return new Range(primary);
+            }
+        }
+        throw new IllegalArgumentException("Invalid range");
+    }
+
+}

Propchange: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/Range.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/Subtag.java
URL: http://svn.apache.org/viewvc/abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/Subtag.java?rev=1173209&view=auto
==============================================================================
--- abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/Subtag.java (added)
+++ abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/Subtag.java Tue Sep 20 15:56:46 2011
@@ -0,0 +1,232 @@
+/*
+ * 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.abdera2.common.lang;
+
+import java.io.Serializable;
+import java.util.Locale;
+
+/**
+ * A Language Tab Subtag. Instances are immutable and safe to use by
+ * multiple threads.
+ */
+public final class Subtag 
+  implements Serializable, Comparable<Subtag> {
+
+    private static final long serialVersionUID = -4496128268514329138L;
+
+    public enum Type {
+        LANGUAGE,
+        EXTLANG,
+        SCRIPT,
+        REGION,
+        VARIANT,
+        SINGLETON,
+        EXTENSION,
+        PRIVATEUSE,
+        GRANDFATHERED,
+        WILDCARD,
+        SIMPLE
+    }
+
+    private final Type type;
+    private final String name;
+    private Subtag prev;
+    private Subtag next;
+    private Subtag root;
+
+    public Subtag(
+        Type type, 
+        String name) {
+        this(
+            type, 
+            name, 
+            null);
+    }
+
+    Subtag() {
+        this(
+            Type.WILDCARD, 
+            "*");
+    }
+
+    /**
+     * Create a Subtag
+     */
+    public Subtag(
+        Type type, 
+        String name, 
+        Subtag prev) {
+        this.type = type;
+        this.name = name;
+        this.prev = prev;
+        if (prev != null) {
+            prev.setNext(this);
+            this.root = prev.root();
+        } else this.root = null;
+    }
+
+    Subtag(Subtag copy, Subtag parent) {
+      this(copy.type(),copy.name(),parent);
+    }
+    
+    Subtag(
+        Type type, 
+        String name, 
+        Subtag prev, 
+        Subtag next,
+        Subtag root) {
+        this.type = type;
+        this.name = name;
+        this.prev = prev;
+        this.next = next;
+        this.root = root;
+    }
+
+    public Subtag root() {
+      return root != null ? root : this;
+    }
+    
+    public Type type() {
+        return type;
+    }
+
+    public String name() {
+        return toString();
+    }
+
+    void setPrevious(Subtag prev) {
+        this.prev = prev;
+    }
+
+    public Subtag previous() {
+        return prev;
+    }
+
+    void setNext(Subtag next) {
+        this.next = next;
+        if (next != null)
+            next.setPrevious(this);
+    }
+
+    public Subtag next() {
+        return next;
+    }
+
+    public String toString() {
+        switch (type) {
+            case LANGUAGE:
+                return name.toLowerCase(Locale.US);
+            case REGION:
+                return name.toUpperCase(Locale.US);
+            case SCRIPT:
+                return toTitleCase(name);
+            default:
+                return name.toLowerCase(Locale.US);
+        }
+    }
+
+    private static String toTitleCase(String string) {
+        if (string == null || string.length() == 0)
+            return string;
+        char[] chars = string.toLowerCase(Locale.US).toCharArray();
+        chars[0] = Character.toTitleCase(chars[0]);
+        return new String(chars);
+    }
+
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((name == null) ? 0 : 
+          name.toLowerCase(Locale.US).hashCode());
+        result = prime * result + ((type == null) ? 0 : 
+          type.hashCode());
+        return result;
+    }
+
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        final Subtag other = (Subtag)obj;
+        if (other.type() == Type.WILDCARD || type() == Type.WILDCARD)
+            return true;
+        if (name == null) {
+            if (other.name != null)
+                return false;
+        } else if (!name.equalsIgnoreCase(other.name))
+            return false;
+        if (other.type() == Type.SIMPLE || type() == Type.SIMPLE)
+            return true;
+        if (type == null) {
+            if (other.type != null)
+                return false;
+        } else if (!type.equals(other.type))
+            return false;
+        return true;
+    }
+
+    private boolean isX() {
+      return "x".equalsIgnoreCase(name());
+    }
+    
+    public boolean isSingleton() {
+      return type == Type.SINGLETON;
+    }
+    
+    public boolean isExtension() {
+      return type == Type.EXTENSION;
+    }
+    
+    public boolean isPrivateUse() {
+      return type == Type.PRIVATEUSE;
+    }
+    
+    boolean isExtensionOrPrivateUse() {
+      return isExtension() || isPrivateUse();
+    }
+    
+    public boolean isExtensionSingleton() {
+      return isSingleton() && !isX();
+    }
+    
+    public boolean isPrivateSingleton() {
+      return isSingleton() && isX();
+    }
+        
+    public SubtagSet extractExtensionGroup() {
+      if (!isSingleton()) return null;
+      Subtag c = this, p = root = new Subtag(this,null);
+      while((c = c.next()) != null && c.isExtensionOrPrivateUse())
+        p = new Subtag(c,p);
+      return new SubtagSet(root) {
+        private static final long serialVersionUID = 7508549925367514365L;        
+      };
+    }
+    
+    public static Subtag newWildcard() {
+        return new Subtag();
+    }
+    
+    public int compareTo(Subtag o) {
+      int c = o.type.compareTo(type);
+      return c != 0 ? c : o.name.compareTo(name);
+  }
+}

Propchange: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/Subtag.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/SubtagSet.java
URL: http://svn.apache.org/viewvc/abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/SubtagSet.java?rev=1173209&view=auto
==============================================================================
--- abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/SubtagSet.java (added)
+++ abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/SubtagSet.java Tue Sep 20 15:56:46 2011
@@ -0,0 +1,173 @@
+/*
+ * 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.abdera2.common.lang;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.abdera2.common.lang.Subtag.Type;
+
+public abstract class SubtagSet 
+  implements Serializable, 
+             Iterable<Subtag>,
+             Comparable<SubtagSet>{
+
+  private static final long serialVersionUID = -1862650860527311777L;
+    protected final Subtag root;
+
+    protected SubtagSet(Subtag root) {
+        this.root = root;
+    }
+
+    public String toString() {
+        StringBuilder buf = 
+          new StringBuilder();
+        Iterator<Subtag> tags = 
+          iterator();
+        buf.append(
+            tags.next().name());
+        while(tags.hasNext())
+            buf.append('-')
+               .append(tags.next().name());
+        return buf.toString();
+    }
+
+    public Iterator<Subtag> iterator() {
+        return new SubtagIterator(root);
+    }
+
+    public boolean contains(Subtag subtag) {
+        for (Subtag tag : this)
+            if (tag.equals(subtag))
+                return true;
+        return false;
+    }
+
+    public boolean contains(String tag) {
+        return contains(tag, Subtag.Type.SIMPLE);
+    }
+
+    public boolean contains(String tag, Subtag.Type type) {
+        return contains(new Subtag(type, tag));
+    }
+    
+    public int length() {
+        return toString().length();
+    }
+
+    @SuppressWarnings("unused")
+    public int size() {
+        int n = 0;
+        for (Subtag tag : this)
+            n++;
+        return n;
+    }
+
+    public Subtag get(int index) {
+        if (index < 0)
+            throw new IndexOutOfBoundsException();
+        if (index == 0) return root;
+        Subtag tag = root.next();
+        while (tag != null && --index > 0)
+          tag = tag.next();
+        if (tag == null)
+          throw new IndexOutOfBoundsException();
+        return tag;
+    }
+
+    public static class SubtagIterator implements Iterator<Subtag> {
+        private Subtag current;
+
+        SubtagIterator(Subtag current) {
+            this.current = current;
+        }
+
+        public boolean hasNext() {
+            return current != null;
+        }
+
+        public Subtag next() {
+            Subtag tag = current;
+            current = tag.next();
+            return tag;
+        }
+
+        public Type type() {
+          return current != null ? current.type() : null;
+        }
+        
+        public String name() {
+          return current != null ? current.name() : null;
+        }
+        
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        for (Subtag tag : this)
+            result = prime * result + tag.hashCode();
+        return result;
+    }
+
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        final Lang other = (Lang)obj;
+        return hashCode() == other.hashCode();
+    }
+
+    public Subtag[] toArray() {
+        List<Subtag> tags = 
+          new LinkedList<Subtag>();
+        for (Subtag tag : this)
+            tags.add(tag);
+        return tags.toArray(new Subtag[tags.size()]);
+    }
+
+    public List<Subtag> asList() {
+        return Arrays.asList(toArray());
+    }
+
+    public int compareTo(SubtagSet o) {
+      Iterator<Subtag> i = iterator();
+      Iterator<Subtag> e = o.iterator();
+      for (; i.hasNext() && e.hasNext();) {
+          Subtag inext = i.next();
+          Subtag enext = e.next();
+          int c = inext.compareTo(enext);
+          if (c != 0)
+              return c;
+      }
+      if (e.hasNext() && !i.hasNext())
+          return -1;
+      if (i.hasNext() && !e.hasNext())
+          return 1;
+      return 0;
+  }
+}

Propchange: abdera/abdera2/common/src/main/java/org/apache/abdera2/common/lang/SubtagSet.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Mime
View raw message