You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@santuario.apache.org by co...@apache.org on 2017/04/25 12:34:26 UTC
svn commit: r1792607 - in
/santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/c14n/implementations:
Canonicalizer11.java Canonicalizer20010315.java XmlAttrStack.java
Author: coheigea
Date: Tue Apr 25 12:34:26 2017
New Revision: 1792607
URL: http://svn.apache.org/viewvc?rev=1792607&view=rev
Log:
Abstract the XmlAttrStack so that it is shared between the different canonical xml 1.0/1.1 implementations
Added:
santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/c14n/implementations/XmlAttrStack.java
Modified:
santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/c14n/implementations/Canonicalizer11.java
santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/c14n/implementations/Canonicalizer20010315.java
Modified: santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/c14n/implementations/Canonicalizer11.java
URL: http://svn.apache.org/viewvc/santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/c14n/implementations/Canonicalizer11.java?rev=1792607&r1=1792606&r2=1792607&view=diff
==============================================================================
--- santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/c14n/implementations/Canonicalizer11.java (original)
+++ santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/c14n/implementations/Canonicalizer11.java Tue Apr 25 12:34:26 2017
@@ -19,30 +19,24 @@
package org.apache.xml.security.c14n.implementations;
import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
+
import javax.xml.parsers.ParserConfigurationException;
-import org.w3c.dom.Attr;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-import org.xml.sax.SAXException;
import org.apache.xml.security.c14n.CanonicalizationException;
import org.apache.xml.security.c14n.helper.C14nHelper;
import org.apache.xml.security.signature.XMLSignatureInput;
import org.apache.xml.security.utils.Constants;
import org.apache.xml.security.utils.XMLUtils;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
/**
* Implements <A HREF="http://www.w3.org/TR/2008/PR-xml-c14n11-20080129/">
@@ -54,131 +48,11 @@ public abstract class Canonicalizer11 ex
private static final String XMLNS_URI = Constants.NamespaceSpecNS;
private static final String XML_LANG_URI = Constants.XML_LANG_SPACE_SpecNS;
- private static final org.slf4j.Logger LOG =
- org.slf4j.LoggerFactory.getLogger(Canonicalizer11.class);
private final SortedSet<Attr> result = new TreeSet<Attr>(COMPARE);
private boolean firstCall = true;
- private static class XmlAttrStack {
- static class XmlsStackElement {
- int level;
- boolean rendered = false;
- List<Attr> nodes = new ArrayList<>();
- }
-
- int currentLevel = 0;
- int lastlevel = 0;
- XmlsStackElement cur;
- List<XmlsStackElement> levels = new ArrayList<>();
-
- void push(int level) {
- currentLevel = level;
- if (currentLevel == -1) {
- return;
- }
- cur = null;
- while (lastlevel >= currentLevel) {
- levels.remove(levels.size() - 1);
- int newSize = levels.size();
- if (newSize == 0) {
- lastlevel = 0;
- return;
- }
- lastlevel = levels.get(newSize - 1).level;
- }
- }
-
- void addXmlnsAttr(Attr n) {
- if (cur == null) {
- cur = new XmlsStackElement();
- cur.level = currentLevel;
- levels.add(cur);
- lastlevel = currentLevel;
- }
- cur.nodes.add(n);
- }
-
- void getXmlnsAttr(Collection<Attr> col) {
- int size = levels.size() - 1;
- if (cur == null) {
- cur = new XmlsStackElement();
- cur.level = currentLevel;
- lastlevel = currentLevel;
- levels.add(cur);
- }
- boolean parentRendered = false;
- XmlsStackElement e = null;
- if (size == -1) {
- parentRendered = true;
- } else {
- e = levels.get(size);
- if (e.rendered && e.level + 1 == currentLevel) {
- parentRendered = true;
- }
- }
- if (parentRendered) {
- col.addAll(cur.nodes);
- cur.rendered = true;
- return;
- }
-
- Map<String, Attr> loa = new HashMap<>();
- List<Attr> baseAttrs = new ArrayList<>();
- boolean successiveOmitted = true;
- for (; size >= 0; size--) {
- e = levels.get(size);
- if (e.rendered) {
- successiveOmitted = false;
- }
- Iterator<Attr> it = e.nodes.iterator();
- while (it.hasNext() && successiveOmitted) {
- Attr n = it.next();
- if (n.getLocalName().equals("base") && !e.rendered) {
- baseAttrs.add(n);
- } else if (!loa.containsKey(n.getName())) {
- loa.put(n.getName(), n);
- }
- }
- }
- if (!baseAttrs.isEmpty()) {
- Iterator<Attr> it = col.iterator();
- String base = null;
- Attr baseAttr = null;
- while (it.hasNext()) {
- Attr n = it.next();
- if (n.getLocalName().equals("base")) {
- base = n.getValue();
- baseAttr = n;
- break;
- }
- }
- it = baseAttrs.iterator();
- while (it.hasNext()) {
- Attr n = it.next();
- if (base == null) {
- base = n.getValue();
- baseAttr = n;
- } else {
- try {
- base = joinURI(n.getValue(), base);
- } catch (URISyntaxException ue) {
- LOG.debug(ue.getMessage(), ue);
- }
- }
- }
- if (base != null && base.length() != 0) {
- baseAttr.setValue(base);
- col.add(baseAttr);
- }
- }
-
- cur.rendered = true;
- col.addAll(loa.values());
- }
- }
-
- private XmlAttrStack xmlattrStack = new XmlAttrStack();
+ private XmlAttrStack xmlattrStack = new XmlAttrStack(true);
/**
* Constructor Canonicalizer11
@@ -454,238 +328,4 @@ public abstract class Canonicalizer11 ex
}
}
- private static String joinURI(String baseURI, String relativeURI) throws URISyntaxException {
- String bscheme = null;
- String bauthority = null;
- String bpath = "";
- String bquery = null;
-
- // pre-parse the baseURI
- if (baseURI != null) {
- if (baseURI.endsWith("..")) {
- baseURI = baseURI + "/";
- }
- URI base = new URI(baseURI);
- bscheme = base.getScheme();
- bauthority = base.getAuthority();
- bpath = base.getPath();
- bquery = base.getQuery();
- }
-
- URI r = new URI(relativeURI);
- String rscheme = r.getScheme();
- String rauthority = r.getAuthority();
- String rpath = r.getPath();
- String rquery = r.getQuery();
-
- String tscheme, tauthority, tpath, tquery;
- if (rscheme != null && rscheme.equals(bscheme)) {
- rscheme = null;
- }
- if (rscheme != null) {
- tscheme = rscheme;
- tauthority = rauthority;
- tpath = removeDotSegments(rpath);
- tquery = rquery;
- } else {
- if (rauthority != null) {
- tauthority = rauthority;
- tpath = removeDotSegments(rpath);
- tquery = rquery;
- } else {
- if (rpath.length() == 0) {
- tpath = bpath;
- if (rquery != null) {
- tquery = rquery;
- } else {
- tquery = bquery;
- }
- } else {
- if (rpath.startsWith("/")) {
- tpath = removeDotSegments(rpath);
- } else {
- if (bauthority != null && bpath.length() == 0) {
- tpath = "/" + rpath;
- } else {
- int last = bpath.lastIndexOf('/');
- if (last == -1) {
- tpath = rpath;
- } else {
- tpath = bpath.substring(0, last+1) + rpath;
- }
- }
- tpath = removeDotSegments(tpath);
- }
- tquery = rquery;
- }
- tauthority = bauthority;
- }
- tscheme = bscheme;
- }
- return new URI(tscheme, tauthority, tpath, tquery, null).toString();
- }
-
- private static String removeDotSegments(String path) {
- LOG.debug("STEP OUTPUT BUFFER\t\tINPUT BUFFER");
-
- // 1. The input buffer is initialized with the now-appended path
- // components then replace occurrences of "//" in the input buffer
- // with "/" until no more occurrences of "//" are in the input buffer.
- String input = path;
- while (input.indexOf("//") > -1) {
- input = input.replaceAll("//", "/");
- }
-
- // Initialize the output buffer with the empty string.
- StringBuilder output = new StringBuilder();
-
- // If the input buffer starts with a root slash "/" then move this
- // character to the output buffer.
- if (input.charAt(0) == '/') {
- output.append("/");
- input = input.substring(1);
- }
-
- printStep("1 ", output.toString(), input);
-
- // While the input buffer is not empty, loop as follows
- while (input.length() != 0) {
- // 2A. If the input buffer begins with a prefix of "./",
- // then remove that prefix from the input buffer
- // else if the input buffer begins with a prefix of "../", then
- // if also the output does not contain the root slash "/" only,
- // then move this prefix to the end of the output buffer else
- // remove that prefix
- if (input.startsWith("./")) {
- input = input.substring(2);
- printStep("2A", output.toString(), input);
- } else if (input.startsWith("../")) {
- input = input.substring(3);
- if (!output.toString().equals("/")) {
- output.append("../");
- }
- printStep("2A", output.toString(), input);
- // 2B. if the input buffer begins with a prefix of "/./" or "/.",
- // where "." is a complete path segment, then replace that prefix
- // with "/" in the input buffer; otherwise,
- } else if (input.startsWith("/./")) {
- input = input.substring(2);
- printStep("2B", output.toString(), input);
- } else if (input.equals("/.")) {
- // FIXME: what is complete path segment?
- input = input.replaceFirst("/.", "/");
- printStep("2B", output.toString(), input);
- // 2C. if the input buffer begins with a prefix of "/../" or "/..",
- // where ".." is a complete path segment, then replace that prefix
- // with "/" in the input buffer and if also the output buffer is
- // empty, last segment in the output buffer equals "../" or "..",
- // where ".." is a complete path segment, then append ".." or "/.."
- // for the latter case respectively to the output buffer else
- // remove the last segment and its preceding "/" (if any) from the
- // output buffer and if hereby the first character in the output
- // buffer was removed and it was not the root slash then delete a
- // leading slash from the input buffer; otherwise,
- } else if (input.startsWith("/../")) {
- input = input.substring(3);
- if (output.length() == 0) {
- output.append("/");
- } else if (output.toString().endsWith("../")) {
- output.append("..");
- } else if (output.toString().endsWith("..")) {
- output.append("/..");
- } else {
- int index = output.lastIndexOf("/");
- if (index == -1) {
- output = new StringBuilder();
- if (input.charAt(0) == '/') {
- input = input.substring(1);
- }
- } else {
- output = output.delete(index, output.length());
- }
- }
- printStep("2C", output.toString(), input);
- } else if (input.equals("/..")) {
- // FIXME: what is complete path segment?
- input = input.replaceFirst("/..", "/");
- if (output.length() == 0) {
- output.append("/");
- } else if (output.toString().endsWith("../")) {
- output.append("..");
- } else if (output.toString().endsWith("..")) {
- output.append("/..");
- } else {
- int index = output.lastIndexOf("/");
- if (index == -1) {
- output = new StringBuilder();
- if (input.charAt(0) == '/') {
- input = input.substring(1);
- }
- } else {
- output = output.delete(index, output.length());
- }
- }
- printStep("2C", output.toString(), input);
- // 2D. if the input buffer consists only of ".", then remove
- // that from the input buffer else if the input buffer consists
- // only of ".." and if the output buffer does not contain only
- // the root slash "/", then move the ".." to the output buffer
- // else delte it.; otherwise,
- } else if (input.equals(".")) {
- input = "";
- printStep("2D", output.toString(), input);
- } else if (input.equals("..")) {
- if (!output.toString().equals("/")) {
- output.append("..");
- }
- input = "";
- printStep("2D", output.toString(), input);
- // 2E. move the first path segment (if any) in the input buffer
- // to the end of the output buffer, including the initial "/"
- // character (if any) and any subsequent characters up to, but not
- // including, the next "/" character or the end of the input buffer.
- } else {
- int end = -1;
- int begin = input.indexOf('/');
- if (begin == 0) {
- end = input.indexOf('/', 1);
- } else {
- end = begin;
- begin = 0;
- }
- String segment;
- if (end == -1) {
- segment = input.substring(begin);
- input = "";
- } else {
- segment = input.substring(begin, end);
- input = input.substring(end);
- }
- output.append(segment);
- printStep("2E", output.toString(), input);
- }
- }
-
- // 3. Finally, if the only or last segment of the output buffer is
- // "..", where ".." is a complete path segment not followed by a slash
- // then append a slash "/". The output buffer is returned as the result
- // of remove_dot_segments
- if (output.toString().endsWith("..")) {
- output.append("/");
- printStep("3 ", output.toString(), input);
- }
-
- return output.toString();
- }
-
- private static void printStep(String step, String output, String input) {
- if (LOG.isDebugEnabled()) {
- LOG.debug(" " + step + ": " + output);
- if (output.length() == 0) {
- LOG.debug("\t\t\t\t" + input);
- } else {
- LOG.debug("\t\t\t" + input);
- }
- }
- }
}
Modified: santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/c14n/implementations/Canonicalizer20010315.java
URL: http://svn.apache.org/viewvc/santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/c14n/implementations/Canonicalizer20010315.java?rev=1792607&r1=1792606&r2=1792607&view=diff
==============================================================================
--- santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/c14n/implementations/Canonicalizer20010315.java (original)
+++ santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/c14n/implementations/Canonicalizer20010315.java Tue Apr 25 12:34:26 2017
@@ -19,12 +19,7 @@
package org.apache.xml.security.c14n.implementations;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
@@ -55,88 +50,7 @@ public abstract class Canonicalizer20010
private boolean firstCall = true;
private final SortedSet<Attr> result = new TreeSet<Attr>(COMPARE);
- private static class XmlAttrStack {
- static class XmlsStackElement {
- int level;
- boolean rendered = false;
- List<Attr> nodes = new ArrayList<>();
- }
-
- int currentLevel = 0;
- int lastlevel = 0;
- XmlsStackElement cur;
- List<XmlsStackElement> levels = new ArrayList<>();
-
- void push(int level) {
- currentLevel = level;
- if (currentLevel == -1) {
- return;
- }
- cur = null;
- while (lastlevel >= currentLevel) {
- levels.remove(levels.size() - 1);
- int newSize = levels.size();
- if (newSize == 0) {
- lastlevel = 0;
- return;
- }
- lastlevel = levels.get(newSize - 1).level;
- }
- }
-
- void addXmlnsAttr(Attr n) {
- if (cur == null) {
- cur = new XmlsStackElement();
- cur.level = currentLevel;
- levels.add(cur);
- lastlevel = currentLevel;
- }
- cur.nodes.add(n);
- }
-
- void getXmlnsAttr(Collection<Attr> col) {
- int size = levels.size() - 1;
- if (cur == null) {
- cur = new XmlsStackElement();
- cur.level = currentLevel;
- lastlevel = currentLevel;
- levels.add(cur);
- }
- boolean parentRendered = false;
- XmlsStackElement e = null;
- if (size == -1) {
- parentRendered = true;
- } else {
- e = levels.get(size);
- if (e.rendered && e.level + 1 == currentLevel) {
- parentRendered = true;
- }
- }
- if (parentRendered) {
- col.addAll(cur.nodes);
- cur.rendered = true;
- return;
- }
-
- Map<String, Attr> loa = new HashMap<>();
- for (; size >= 0; size--) {
- e = levels.get(size);
- Iterator<Attr> it = e.nodes.iterator();
- while (it.hasNext()) {
- Attr n = it.next();
- if (!loa.containsKey(n.getName())) {
- loa.put(n.getName(), n);
- }
- }
- }
-
- cur.rendered = true;
- col.addAll(loa.values());
- }
-
- }
-
- private XmlAttrStack xmlattrStack = new XmlAttrStack();
+ private XmlAttrStack xmlattrStack = new XmlAttrStack(false);
/**
* Constructor Canonicalizer20010315
Added: santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/c14n/implementations/XmlAttrStack.java
URL: http://svn.apache.org/viewvc/santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/c14n/implementations/XmlAttrStack.java?rev=1792607&view=auto
==============================================================================
--- santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/c14n/implementations/XmlAttrStack.java (added)
+++ santuario/xml-security-java/trunk/src/main/java/org/apache/xml/security/c14n/implementations/XmlAttrStack.java Tue Apr 25 12:34:26 2017
@@ -0,0 +1,408 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.xml.security.c14n.implementations;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.w3c.dom.Attr;
+
+/**
+ * An XmlAttrStack that is shared between the Canonical XML 1.0 and 1.1 implementations.
+ */
+class XmlAttrStack {
+
+ private static final org.slf4j.Logger LOG =
+ org.slf4j.LoggerFactory.getLogger(XmlAttrStack.class);
+
+ static class XmlsStackElement {
+ int level;
+ boolean rendered = false;
+ List<Attr> nodes = new ArrayList<>();
+ }
+
+ private int currentLevel = 0;
+ private int lastlevel = 0;
+ private XmlsStackElement cur;
+ private List<XmlsStackElement> levels = new ArrayList<>();
+ private boolean c14n11;
+
+ public XmlAttrStack(boolean c14n11) {
+ this.c14n11 = c14n11;
+ }
+
+ void push(int level) {
+ currentLevel = level;
+ if (currentLevel == -1) {
+ return;
+ }
+ cur = null;
+ while (lastlevel >= currentLevel) {
+ levels.remove(levels.size() - 1);
+ int newSize = levels.size();
+ if (newSize == 0) {
+ lastlevel = 0;
+ return;
+ }
+ lastlevel = levels.get(newSize - 1).level;
+ }
+ }
+
+ void addXmlnsAttr(Attr n) {
+ if (cur == null) {
+ cur = new XmlsStackElement();
+ cur.level = currentLevel;
+ levels.add(cur);
+ lastlevel = currentLevel;
+ }
+ cur.nodes.add(n);
+ }
+
+ void getXmlnsAttr(Collection<Attr> col) {
+ int size = levels.size() - 1;
+ if (cur == null) {
+ cur = new XmlsStackElement();
+ cur.level = currentLevel;
+ lastlevel = currentLevel;
+ levels.add(cur);
+ }
+ boolean parentRendered = false;
+ XmlsStackElement e = null;
+ if (size == -1) {
+ parentRendered = true;
+ } else {
+ e = levels.get(size);
+ if (e.rendered && e.level + 1 == currentLevel) {
+ parentRendered = true;
+ }
+ }
+ if (parentRendered) {
+ col.addAll(cur.nodes);
+ cur.rendered = true;
+ return;
+ }
+
+ Map<String, Attr> loa = new HashMap<>();
+ if (c14n11) {
+ List<Attr> baseAttrs = new ArrayList<>();
+ boolean successiveOmitted = true;
+ for (; size >= 0; size--) {
+ e = levels.get(size);
+ if (e.rendered) {
+ successiveOmitted = false;
+ }
+ Iterator<Attr> it = e.nodes.iterator();
+ while (it.hasNext() && successiveOmitted) {
+ Attr n = it.next();
+ if (n.getLocalName().equals("base") && !e.rendered) {
+ baseAttrs.add(n);
+ } else if (!loa.containsKey(n.getName())) {
+ loa.put(n.getName(), n);
+ }
+ }
+ }
+ if (!baseAttrs.isEmpty()) {
+ Iterator<Attr> it = col.iterator();
+ String base = null;
+ Attr baseAttr = null;
+ while (it.hasNext()) {
+ Attr n = it.next();
+ if (n.getLocalName().equals("base")) {
+ base = n.getValue();
+ baseAttr = n;
+ break;
+ }
+ }
+ it = baseAttrs.iterator();
+ while (it.hasNext()) {
+ Attr n = it.next();
+ if (base == null) {
+ base = n.getValue();
+ baseAttr = n;
+ } else {
+ try {
+ base = joinURI(n.getValue(), base);
+ } catch (URISyntaxException ue) {
+ LOG.debug(ue.getMessage(), ue);
+ }
+ }
+ }
+ if (base != null && base.length() != 0) {
+ baseAttr.setValue(base);
+ col.add(baseAttr);
+ }
+ }
+ } else {
+ for (; size >= 0; size--) {
+ e = levels.get(size);
+ Iterator<Attr> it = e.nodes.iterator();
+ while (it.hasNext()) {
+ Attr n = it.next();
+ if (!loa.containsKey(n.getName())) {
+ loa.put(n.getName(), n);
+ }
+ }
+ }
+ }
+
+ cur.rendered = true;
+ col.addAll(loa.values());
+ }
+
+ private static String joinURI(String baseURI, String relativeURI) throws URISyntaxException {
+ String bscheme = null;
+ String bauthority = null;
+ String bpath = "";
+ String bquery = null;
+
+ // pre-parse the baseURI
+ if (baseURI != null) {
+ if (baseURI.endsWith("..")) {
+ baseURI = baseURI + "/";
+ }
+ URI base = new URI(baseURI);
+ bscheme = base.getScheme();
+ bauthority = base.getAuthority();
+ bpath = base.getPath();
+ bquery = base.getQuery();
+ }
+
+ URI r = new URI(relativeURI);
+ String rscheme = r.getScheme();
+ String rauthority = r.getAuthority();
+ String rpath = r.getPath();
+ String rquery = r.getQuery();
+
+ String tscheme, tauthority, tpath, tquery;
+ if (rscheme != null && rscheme.equals(bscheme)) {
+ rscheme = null;
+ }
+ if (rscheme != null) {
+ tscheme = rscheme;
+ tauthority = rauthority;
+ tpath = removeDotSegments(rpath);
+ tquery = rquery;
+ } else {
+ if (rauthority != null) {
+ tauthority = rauthority;
+ tpath = removeDotSegments(rpath);
+ tquery = rquery;
+ } else {
+ if (rpath.length() == 0) {
+ tpath = bpath;
+ if (rquery != null) {
+ tquery = rquery;
+ } else {
+ tquery = bquery;
+ }
+ } else {
+ if (rpath.startsWith("/")) {
+ tpath = removeDotSegments(rpath);
+ } else {
+ if (bauthority != null && bpath.length() == 0) {
+ tpath = "/" + rpath;
+ } else {
+ int last = bpath.lastIndexOf('/');
+ if (last == -1) {
+ tpath = rpath;
+ } else {
+ tpath = bpath.substring(0, last+1) + rpath;
+ }
+ }
+ tpath = removeDotSegments(tpath);
+ }
+ tquery = rquery;
+ }
+ tauthority = bauthority;
+ }
+ tscheme = bscheme;
+ }
+ return new URI(tscheme, tauthority, tpath, tquery, null).toString();
+ }
+
+ private static String removeDotSegments(String path) {
+ LOG.debug("STEP OUTPUT BUFFER\t\tINPUT BUFFER");
+
+ // 1. The input buffer is initialized with the now-appended path
+ // components then replace occurrences of "//" in the input buffer
+ // with "/" until no more occurrences of "//" are in the input buffer.
+ String input = path;
+ while (input.indexOf("//") > -1) {
+ input = input.replaceAll("//", "/");
+ }
+
+ // Initialize the output buffer with the empty string.
+ StringBuilder output = new StringBuilder();
+
+ // If the input buffer starts with a root slash "/" then move this
+ // character to the output buffer.
+ if (input.charAt(0) == '/') {
+ output.append("/");
+ input = input.substring(1);
+ }
+
+ printStep("1 ", output.toString(), input);
+
+ // While the input buffer is not empty, loop as follows
+ while (input.length() != 0) {
+ // 2A. If the input buffer begins with a prefix of "./",
+ // then remove that prefix from the input buffer
+ // else if the input buffer begins with a prefix of "../", then
+ // if also the output does not contain the root slash "/" only,
+ // then move this prefix to the end of the output buffer else
+ // remove that prefix
+ if (input.startsWith("./")) {
+ input = input.substring(2);
+ printStep("2A", output.toString(), input);
+ } else if (input.startsWith("../")) {
+ input = input.substring(3);
+ if (!output.toString().equals("/")) {
+ output.append("../");
+ }
+ printStep("2A", output.toString(), input);
+ // 2B. if the input buffer begins with a prefix of "/./" or "/.",
+ // where "." is a complete path segment, then replace that prefix
+ // with "/" in the input buffer; otherwise,
+ } else if (input.startsWith("/./")) {
+ input = input.substring(2);
+ printStep("2B", output.toString(), input);
+ } else if (input.equals("/.")) {
+ // FIXME: what is complete path segment?
+ input = input.replaceFirst("/.", "/");
+ printStep("2B", output.toString(), input);
+ // 2C. if the input buffer begins with a prefix of "/../" or "/..",
+ // where ".." is a complete path segment, then replace that prefix
+ // with "/" in the input buffer and if also the output buffer is
+ // empty, last segment in the output buffer equals "../" or "..",
+ // where ".." is a complete path segment, then append ".." or "/.."
+ // for the latter case respectively to the output buffer else
+ // remove the last segment and its preceding "/" (if any) from the
+ // output buffer and if hereby the first character in the output
+ // buffer was removed and it was not the root slash then delete a
+ // leading slash from the input buffer; otherwise,
+ } else if (input.startsWith("/../")) {
+ input = input.substring(3);
+ if (output.length() == 0) {
+ output.append("/");
+ } else if (output.toString().endsWith("../")) {
+ output.append("..");
+ } else if (output.toString().endsWith("..")) {
+ output.append("/..");
+ } else {
+ int index = output.lastIndexOf("/");
+ if (index == -1) {
+ output = new StringBuilder();
+ if (input.charAt(0) == '/') {
+ input = input.substring(1);
+ }
+ } else {
+ output = output.delete(index, output.length());
+ }
+ }
+ printStep("2C", output.toString(), input);
+ } else if (input.equals("/..")) {
+ // FIXME: what is complete path segment?
+ input = input.replaceFirst("/..", "/");
+ if (output.length() == 0) {
+ output.append("/");
+ } else if (output.toString().endsWith("../")) {
+ output.append("..");
+ } else if (output.toString().endsWith("..")) {
+ output.append("/..");
+ } else {
+ int index = output.lastIndexOf("/");
+ if (index == -1) {
+ output = new StringBuilder();
+ if (input.charAt(0) == '/') {
+ input = input.substring(1);
+ }
+ } else {
+ output = output.delete(index, output.length());
+ }
+ }
+ printStep("2C", output.toString(), input);
+ // 2D. if the input buffer consists only of ".", then remove
+ // that from the input buffer else if the input buffer consists
+ // only of ".." and if the output buffer does not contain only
+ // the root slash "/", then move the ".." to the output buffer
+ // else delte it.; otherwise,
+ } else if (input.equals(".")) {
+ input = "";
+ printStep("2D", output.toString(), input);
+ } else if (input.equals("..")) {
+ if (!output.toString().equals("/")) {
+ output.append("..");
+ }
+ input = "";
+ printStep("2D", output.toString(), input);
+ // 2E. move the first path segment (if any) in the input buffer
+ // to the end of the output buffer, including the initial "/"
+ // character (if any) and any subsequent characters up to, but not
+ // including, the next "/" character or the end of the input buffer.
+ } else {
+ int end = -1;
+ int begin = input.indexOf('/');
+ if (begin == 0) {
+ end = input.indexOf('/', 1);
+ } else {
+ end = begin;
+ begin = 0;
+ }
+ String segment;
+ if (end == -1) {
+ segment = input.substring(begin);
+ input = "";
+ } else {
+ segment = input.substring(begin, end);
+ input = input.substring(end);
+ }
+ output.append(segment);
+ printStep("2E", output.toString(), input);
+ }
+ }
+
+ // 3. Finally, if the only or last segment of the output buffer is
+ // "..", where ".." is a complete path segment not followed by a slash
+ // then append a slash "/". The output buffer is returned as the result
+ // of remove_dot_segments
+ if (output.toString().endsWith("..")) {
+ output.append("/");
+ printStep("3 ", output.toString(), input);
+ }
+
+ return output.toString();
+ }
+
+ private static void printStep(String step, String output, String input) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(" " + step + ": " + output);
+ if (output.length() == 0) {
+ LOG.debug("\t\t\t\t" + input);
+ } else {
+ LOG.debug("\t\t\t" + input);
+ }
+ }
+ }
+}