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
|