001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.servicemix.soap.bindings.http.interceptors;
018
019 import java.io.ByteArrayOutputStream;
020 import java.io.InputStream;
021 import java.util.ArrayList;
022 import java.util.List;
023
024 import javax.xml.XMLConstants;
025 import javax.xml.namespace.QName;
026
027 import org.apache.servicemix.soap.util.DomUtil;
028 import org.apache.servicemix.soap.util.IoUtil;
029 import org.apache.ws.commons.schema.XmlSchemaComplexType;
030 import org.apache.ws.commons.schema.XmlSchemaElement;
031 import org.apache.ws.commons.schema.XmlSchemaSequence;
032 import org.w3c.dom.Document;
033 import org.w3c.dom.Element;
034 import org.w3c.dom.Node;
035 import org.w3c.dom.NodeList;
036
037 /**
038 * @author <a href=""mailto:gnodet [at] gmail.com">Guillaume Nodet</a>
039 */
040 public class IriDecoderHelper {
041
042 /**
043 * Simple holder class for a name/value pair.
044 */
045 public static class Param {
046
047 private final String name;
048 private final String value;
049
050 public Param(String name, String value) {
051 this.name = name;
052 this.value = value;
053 }
054 /**
055 * @return the name
056 */
057 public String getName() {
058 return name;
059 }
060 /**
061 * @return the value
062 */
063 public String getValue() {
064 return value;
065 }
066 /* (non-Javadoc)
067 * @see java.lang.Object#toString()
068 */
069 @Override
070 public String toString() {
071 return "[" + name + "=" + value + "]";
072 }
073 /* (non-Javadoc)
074 * @see java.lang.Object#hashCode()
075 */
076 @Override
077 public int hashCode() {
078 final int PRIME = 31;
079 int result = 1;
080 result = PRIME * result + ((name == null) ? 0 : name.hashCode());
081 result = PRIME * result + ((value == null) ? 0 : value.hashCode());
082 return result;
083 }
084 /* (non-Javadoc)
085 * @see java.lang.Object#equals(java.lang.Object)
086 */
087 @Override
088 public boolean equals(Object obj) {
089 if (this == obj)
090 return true;
091 if (getClass() != obj.getClass())
092 return false;
093 final Param other = (Param) obj;
094 if (name == null) {
095 if (other.name != null)
096 return false;
097 } else if (!name.equals(other.name))
098 return false;
099 if (value == null) {
100 if (other.value != null)
101 return false;
102 } else if (!value.equals(other.value))
103 return false;
104 return true;
105 }
106 }
107
108 public static List<Param> decodeIri(String uri, String loc) {
109 List<Param> values = new ArrayList<Param>();
110 String path = getUriPath(uri);
111 String locPath = getUriPath(loc);
112 int idx2 = 0;
113 char c;
114 for (int idx1 = 0; idx1 < locPath.length(); idx1++) {
115 c = locPath.charAt(idx1);
116 if (c == '{') {
117 if (locPath.charAt(idx1 + 1) == '{') {
118 // double curly brace
119 expect(path, idx2++, '{');
120 } else {
121 int locEnd = locPath.indexOf('}', idx1);
122 String name = locPath.substring(idx1 + 1, locEnd);
123 idx1 = locEnd;
124 int end = findPartEnd(path, idx2);
125 String value = path.substring(idx2, end);
126 idx2 = end;
127 values.add(new Param(name, value));
128 }
129 } else {
130 expect(path, idx2++, c);
131 }
132 }
133 if (idx2 < path.length()) {
134 c = path.charAt(idx2++);
135 if (c == '?') {
136 int end = path.indexOf('#', idx2);
137 if (end < 0) {
138 end = path.length();
139 }
140 addParams(path, idx2, end, values);
141 }
142 }
143 return values;
144 }
145
146 public static void addParams(String input, int start, int stop, List<Param> params) {
147 while (start < stop) {
148 int eq = input.indexOf('=', start);
149 int se = input.indexOf('&', eq);
150 if (se < 0) {
151 se = stop;
152 }
153 params.add(new Param(input.substring(start, eq),
154 input.substring(eq + 1, se)));
155 start = se + 1;
156 }
157 }
158
159 /**
160 *
161 */
162 public static int findPartEnd(String path, int c) {
163 int end = path.length();
164 int i = path.indexOf('/', c);
165 if (i >= c && i < end) {
166 end = i;
167 }
168 i = path.indexOf('?', c);
169 if (i >= c && i < end) {
170 end = i;
171 }
172 return end;
173 }
174
175 /**
176 * Check that the next character is the one expected
177 * or throw an exception
178 */
179 public static void expect(String path, int index, char c) {
180 if (path.charAt(index) != c) {
181 throw new IllegalStateException("Unexpected character '" + c + "' at index " + index);
182 }
183 }
184
185 /**
186 * Get the path of a given uri, removing the scheme and authority parts
187 */
188 public static String getUriPath(String uri) {
189 int idx = uri.indexOf("://");
190 int idx2 = uri.indexOf('/', idx + 3);
191 return uri.substring(idx2 + 1);
192 }
193
194 public static String combine(String location, String httpLocation) {
195 if (httpLocation == null) {
196 return location;
197 }
198 if (httpLocation.indexOf("://") != -1) {
199 return httpLocation;
200 }
201 if (location.endsWith("/")) {
202 return location + httpLocation;
203 } else {
204 return location + "/" + httpLocation;
205 }
206 }
207
208 /**
209 * Create a dom document conformant with the given schema element
210 * with the input parameters.
211 *
212 * @param element
213 * @param params
214 * @return
215 */
216 public static Document buildDocument(XmlSchemaElement element, List<Param> params) {
217 Document doc = DomUtil.createDocument();
218 XmlSchemaComplexType cplxType = (XmlSchemaComplexType) element.getSchemaType();
219 XmlSchemaSequence seq = (XmlSchemaSequence) cplxType.getParticle();
220 Element e = doc.createElementNS(element.getQName().getNamespaceURI(), element.getQName().getLocalPart());
221 e.setAttribute(XMLConstants.XMLNS_ATTRIBUTE, element.getQName().getNamespaceURI());
222 doc.appendChild(e);
223 for (int i = 0; i < seq.getItems().getCount(); i++) {
224 XmlSchemaElement elChild = (XmlSchemaElement) seq.getItems().getItem(i);
225 Param param = null;
226 for (Param p : params) {
227 if (p.getName().equals(elChild.getQName().getLocalPart())) {
228 param = p;
229 break;
230 }
231 }
232 Element ec = doc.createElementNS(elChild.getQName().getNamespaceURI(), elChild.getQName().getLocalPart());
233 if (!elChild.getQName().getNamespaceURI().equals(element.getQName().getNamespaceURI())) {
234 ec.setAttribute(XMLConstants.XMLNS_ATTRIBUTE, elChild.getQName().getNamespaceURI());
235 }
236 if (param != null) {
237 params.remove(param);
238 ec.appendChild(doc.createTextNode(param.getValue()));
239 }
240 e.appendChild(ec);
241 }
242 return doc;
243 }
244
245 public static Document interopolateParams(Document doc, XmlSchemaElement element, List<Param> params) {
246 XmlSchemaComplexType cplxType = (XmlSchemaComplexType)element.getSchemaType();
247 XmlSchemaSequence seq = (XmlSchemaSequence)cplxType.getParticle();
248 Element root = doc.getDocumentElement();
249 if (root == null) {
250 root = doc.createElementNS(element.getQName().getNamespaceURI(),
251 element.getQName().getLocalPart());
252 root.setAttribute(XMLConstants.XMLNS_ATTRIBUTE, element.getQName().getNamespaceURI());
253 doc.appendChild(root);
254 }
255
256 for (int i = 0; i < seq.getItems().getCount(); i++) {
257 XmlSchemaElement elChild = (XmlSchemaElement)seq.getItems().getItem(i);
258 Param param = null;
259 for (Param p : params) {
260 if (p.getName().equals(elChild.getQName().getLocalPart())) {
261 param = p;
262 break;
263 }
264 }
265 if (param == null) {
266 continue;
267 }
268
269 Element ec = getElement(root, elChild.getQName());
270 if (ec == null) {
271 ec = doc.createElementNS(elChild.getQName().getNamespaceURI(), elChild.getQName()
272 .getLocalPart());
273 if (!elChild.getQName().getNamespaceURI().equals(element.getQName().getNamespaceURI())) {
274 ec.setAttribute(XMLConstants.XMLNS_ATTRIBUTE, elChild.getQName().getNamespaceURI());
275 }
276
277 // insert the element at the appropriate position
278 Element insertBeforeEl = getIndexedElement(root, i);
279 if (insertBeforeEl != null) {
280 root.insertBefore(ec, insertBeforeEl);
281 } else {
282 root.appendChild(ec);
283 }
284 } else {
285 NodeList childNodes = ec.getChildNodes();
286 for (int j = 0; j < childNodes.getLength(); j++) {
287 Node n = childNodes.item(j);
288 ec.removeChild(n);
289 }
290 }
291
292 if (param != null) {
293 params.remove(param);
294 ec.appendChild(doc.createTextNode(param.getValue()));
295 }
296 }
297 return doc;
298 }
299
300 public static List<Param> decode(String uri, String loc, InputStream is) {
301 List<Param> params = IriDecoderHelper.decodeIri(uri, loc);
302 if (is != null) {
303 ByteArrayOutputStream baos = new ByteArrayOutputStream();
304 IoUtil.copyStream(is, baos);
305 IriDecoderHelper.addParams(baos.toString(), 0, baos.size(), params);
306 }
307 return params;
308 }
309
310 private static Element getIndexedElement(Element e, int i) {
311 NodeList childNodes = e.getChildNodes();
312 int elNum = 0;
313 for (int j = 0; j < childNodes.getLength(); j++) {
314 Node n = childNodes.item(j);
315 if (n.getNodeType() == Node.ELEMENT_NODE) {
316 if (i == elNum) {
317 return (Element) n;
318 }
319 elNum++;
320 }
321 }
322 return null;
323 }
324
325 private static Element getElement(Element element, QName name) {
326 NodeList childNodes = element.getChildNodes();
327 for (int j = 0; j < childNodes.getLength(); j++) {
328 Node n = childNodes.item(j);
329 if (n.getNodeType() == Node.ELEMENT_NODE
330 && n.getLocalName().equals(name.getLocalPart())
331 && n.getNamespaceURI().equals(name.getNamespaceURI())) {
332 return (Element)n;
333 }
334 }
335 return null;
336 }
337
338 }