001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */package org.apache.cxf.binding.http;
019:
020: import java.io.ByteArrayOutputStream;
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.util.ArrayList;
024: import java.util.Collection;
025: import java.util.List;
026: import java.util.ResourceBundle;
027:
028: import javax.xml.XMLConstants;
029: import javax.xml.namespace.QName;
030:
031: import org.w3c.dom.Document;
032: import org.w3c.dom.Element;
033: import org.w3c.dom.Node;
034: import org.w3c.dom.NodeList;
035:
036: import org.apache.cxf.common.i18n.BundleUtils;
037: import org.apache.cxf.common.i18n.Message;
038: import org.apache.cxf.helpers.DOMUtils;
039: import org.apache.cxf.helpers.IOUtils;
040: import org.apache.cxf.interceptor.Fault;
041: import org.apache.cxf.service.model.SchemaInfo;
042: import org.apache.ws.commons.schema.XmlSchemaAnnotated;
043: import org.apache.ws.commons.schema.XmlSchemaComplexType;
044: import org.apache.ws.commons.schema.XmlSchemaElement;
045: import org.apache.ws.commons.schema.XmlSchemaSequence;
046: import org.apache.ws.commons.schema.XmlSchemaSimpleType;
047: import org.apache.ws.commons.schema.XmlSchemaType;
048:
049: /**
050: * @author <a href=""mailto:gnodet [at] gmail.com">Guillaume Nodet</a>
051: */
052: public final class IriDecoderHelper {
053: private static final ResourceBundle BUNDLE = BundleUtils
054: .getBundle(IriDecoderHelper.class);
055:
056: private IriDecoderHelper() {
057:
058: }
059:
060: public static List<Param> decodeIri(String uri, String loc) {
061: List<Param> values = new ArrayList<Param>();
062: String path = getUriPath(uri);
063: String locPath = getUriPath(loc);
064: int idx2 = 0;
065: char c;
066: for (int idx1 = 0; idx1 < locPath.length(); idx1++) {
067: c = locPath.charAt(idx1);
068: if (c == '{') {
069: if (locPath.charAt(idx1 + 1) == '{') {
070: // double curly brace
071: expect(path, idx2++, '{');
072: } else {
073: int locEnd = locPath.indexOf('}', idx1);
074: String name = locPath.substring(idx1 + 1, locEnd);
075: idx1 = locEnd;
076: String endFragment = getEndFragment(locEnd + 1,
077: locPath);
078:
079: int end = findPartEnd(path, idx2, endFragment);
080: String value = path.substring(idx2, end);
081: idx2 = end;
082: values.add(new Param(name, value));
083: }
084: } else {
085: expect(path, idx2++, c);
086: }
087: }
088: if (idx2 < path.length()) {
089: c = path.charAt(idx2++);
090: if (c == '?') {
091: int end = path.indexOf('#', idx2);
092: if (end < 0) {
093: end = path.length();
094: }
095: addParams(path, idx2, end, values);
096: }
097: }
098: return values;
099: }
100:
101: private static String getEndFragment(int i, String locPath) {
102: int end = locPath.indexOf('{', i);
103:
104: if (end == -1) {
105: end = locPath.length();
106: } else if (locPath.charAt(end + 1) == '{') {
107: return getEndFragment(end + 1, locPath);
108: }
109:
110: return locPath.substring(i, end);
111: }
112:
113: public static void addParams(String input, int start, int stop,
114: List<Param> params) {
115: while (start < stop) {
116: int eq = input.indexOf('=', start);
117: int se = input.indexOf('&', eq);
118: if (se < 0) {
119: se = stop;
120: }
121: params.add(new Param(input.substring(start, eq), input
122: .substring(eq + 1, se)));
123: start = se + 1;
124: }
125: }
126:
127: /**
128: * @param endFragment
129: *
130: */
131: public static int findPartEnd(String path, int c, String endFragment) {
132: int end = path.length();
133: int i = end;
134:
135: if (!"".equals(endFragment)) {
136: i = path.indexOf(endFragment, c);
137: if (i >= c && i < end) {
138: end = i;
139: }
140: }
141:
142: i = path.indexOf('?', c);
143: if (i >= c && i < end) {
144: end = i;
145: }
146:
147: return end;
148: }
149:
150: /**
151: * Check that the next character is the one expected or throw an exception
152: */
153: public static void expect(String path, int index, char c) {
154: if (path.charAt(index) != c) {
155: throw new IllegalStateException("Unexpected character '"
156: + c + "' at index " + index);
157: }
158: }
159:
160: /**
161: * Get the path of a given uri, removing the scheme and authority parts
162: */
163: public static String getUriPath(String uri) {
164: int idx = uri.indexOf("://");
165: int idx2 = uri.indexOf('/', idx + 3);
166: return uri.substring(idx2 + 1);
167: }
168:
169: public static String combine(String location, String httpLocation) {
170: if (httpLocation == null) {
171: return location;
172: }
173: if (httpLocation.indexOf("://") != -1) {
174: return httpLocation;
175: }
176: if (location.endsWith("/")) {
177: return location + httpLocation;
178: } else {
179: return location + "/" + httpLocation;
180: }
181: }
182:
183: private static XmlSchemaType findSchemaType(
184: Collection<SchemaInfo> schemas, QName name) {
185: for (SchemaInfo inf : schemas) {
186: if (inf.getNamespaceURI().equals(name.getNamespaceURI())) {
187: return inf.getSchema().getTypeByName(name);
188: }
189: }
190: return null;
191: }
192:
193: /**
194: * Create a dom document conformant with the given schema element with the
195: * input parameters.
196: *
197: * @param element
198: * @param params
199: * @return
200: */
201: public static Document buildDocument(
202: XmlSchemaAnnotated schemaAnnotation,
203: Collection<SchemaInfo> schemas, List<Param> params) {
204:
205: XmlSchemaElement element = null;
206: QName qname = null;
207: XmlSchemaComplexType cplxType = null;
208: if (schemaAnnotation instanceof XmlSchemaElement) {
209: element = (XmlSchemaElement) schemaAnnotation;
210: qname = element.getQName();
211: cplxType = (XmlSchemaComplexType) element.getSchemaType();
212: if (cplxType == null) {
213: cplxType = (XmlSchemaComplexType) findSchemaType(
214: schemas, element.getSchemaTypeName());
215: }
216: } else if (schemaAnnotation instanceof XmlSchemaComplexType) {
217: cplxType = (XmlSchemaComplexType) schemaAnnotation;
218: qname = cplxType.getQName();
219: } else if (schemaAnnotation instanceof XmlSchemaSimpleType) {
220: throw new Fault(new Message("SIMPLE_TYPE", BUNDLE));
221: }
222:
223: Document doc = DOMUtils.createDocument();
224:
225: XmlSchemaSequence seq = (XmlSchemaSequence) cplxType
226: .getParticle();
227: Element e = doc.createElementNS(qname.getNamespaceURI(), qname
228: .getLocalPart());
229: e.setAttribute(XMLConstants.XMLNS_ATTRIBUTE, qname
230: .getNamespaceURI());
231: doc.appendChild(e);
232:
233: if (seq == null || seq.getItems() == null) {
234: return doc;
235: }
236:
237: for (int i = 0; i < seq.getItems().getCount(); i++) {
238: XmlSchemaElement elChild = (XmlSchemaElement) seq
239: .getItems().getItem(i);
240: Param param = null;
241: for (Param p : params) {
242: if (p.getName().equals(
243: elChild.getQName().getLocalPart())) {
244: param = p;
245: break;
246: }
247: }
248: Element ec = doc.createElementNS(elChild.getQName()
249: .getNamespaceURI(), elChild.getQName()
250: .getLocalPart());
251: if (!elChild.getQName().getNamespaceURI().equals(
252: qname.getNamespaceURI())) {
253: ec.setAttribute(XMLConstants.XMLNS_ATTRIBUTE, elChild
254: .getQName().getNamespaceURI());
255: }
256: if (param != null) {
257: params.remove(param);
258: ec.appendChild(doc.createTextNode(param.getValue()));
259: }
260: e.appendChild(ec);
261: }
262: return doc;
263: }
264:
265: public static Document interopolateParams(Document doc,
266: XmlSchemaAnnotated schemaAnnotation,
267: Collection<SchemaInfo> schemas, List<Param> params) {
268: XmlSchemaElement element = null;
269: QName qname = null;
270: XmlSchemaComplexType cplxType = null;
271: if (schemaAnnotation instanceof XmlSchemaElement) {
272: element = (XmlSchemaElement) schemaAnnotation;
273: qname = element.getQName();
274: cplxType = (XmlSchemaComplexType) element.getSchemaType();
275: if (cplxType == null) {
276: cplxType = (XmlSchemaComplexType) findSchemaType(
277: schemas, element.getSchemaTypeName());
278: }
279: }
280: if (schemaAnnotation instanceof XmlSchemaComplexType) {
281: cplxType = (XmlSchemaComplexType) schemaAnnotation;
282: qname = cplxType.getQName();
283: }
284: XmlSchemaSequence seq = (XmlSchemaSequence) cplxType
285: .getParticle();
286: Element root = doc.getDocumentElement();
287: if (root == null) {
288: root = doc.createElementNS(qname.getNamespaceURI(), qname
289: .getLocalPart());
290: root.setAttribute(XMLConstants.XMLNS_ATTRIBUTE, qname
291: .getNamespaceURI());
292: doc.appendChild(root);
293: }
294:
295: for (int i = 0; i < seq.getItems().getCount(); i++) {
296: XmlSchemaElement elChild = (XmlSchemaElement) seq
297: .getItems().getItem(i);
298: Param param = null;
299: for (Param p : params) {
300: if (p.getName().equals(
301: elChild.getQName().getLocalPart())) {
302: param = p;
303: break;
304: }
305: }
306: if (param == null) {
307: continue;
308: }
309:
310: Element ec = getElement(root, elChild.getQName());
311: if (ec == null) {
312: ec = doc.createElementNS(elChild.getQName()
313: .getNamespaceURI(), elChild.getQName()
314: .getLocalPart());
315: if (!elChild.getQName().getNamespaceURI().equals(
316: qname.getNamespaceURI())) {
317: ec.setAttribute(XMLConstants.XMLNS_ATTRIBUTE,
318: elChild.getQName().getNamespaceURI());
319: }
320:
321: // insert the element at the appropriate position
322: Element insertBeforeEl = getIndexedElement(root, i);
323: if (insertBeforeEl != null) {
324: root.insertBefore(ec, insertBeforeEl);
325: } else {
326: root.appendChild(ec);
327: }
328: } else {
329: NodeList childNodes = ec.getChildNodes();
330: for (int j = 0; j < childNodes.getLength(); j++) {
331: Node n = childNodes.item(j);
332: ec.removeChild(n);
333: }
334: }
335:
336: if (param != null) {
337: params.remove(param);
338: ec.appendChild(doc.createTextNode(param.getValue()));
339: }
340: }
341: return doc;
342: }
343:
344: private static Element getIndexedElement(Element e, int i) {
345: NodeList childNodes = e.getChildNodes();
346: int elNum = 0;
347: for (int j = 0; j < childNodes.getLength(); j++) {
348: Node n = childNodes.item(j);
349: if (n.getNodeType() == Node.ELEMENT_NODE) {
350: if (i == elNum) {
351: return (Element) n;
352: }
353: elNum++;
354: }
355: }
356: return null;
357: }
358:
359: private static Element getElement(Element element, QName name) {
360: NodeList childNodes = element.getChildNodes();
361: for (int j = 0; j < childNodes.getLength(); j++) {
362: Node n = childNodes.item(j);
363: if (n.getNodeType() == Node.ELEMENT_NODE
364: && n.getLocalName().equals(name.getLocalPart())
365: && n.getNamespaceURI().equals(
366: name.getNamespaceURI())) {
367: return (Element) n;
368: }
369: }
370: return null;
371: }
372:
373: public static List<Param> decode(String uri, String loc,
374: InputStream is) {
375: List<Param> params = IriDecoderHelper.decodeIri(uri, loc);
376: if (is != null) {
377: ByteArrayOutputStream baos = new ByteArrayOutputStream();
378: try {
379: IOUtils.copy(is, baos);
380: } catch (IOException e) {
381: throw new RuntimeException(e);
382: }
383: IriDecoderHelper.addParams(baos.toString(), 0, baos.size(),
384: params);
385: }
386: return params;
387: }
388:
389: /**
390: * Simple holder class for a name/value pair.
391: */
392: public static class Param {
393:
394: private final String name;
395: private final String value;
396:
397: public Param(String name, String value) {
398: this .name = name;
399: this .value = value;
400: }
401:
402: /**
403: * @return the name
404: */
405: public String getName() {
406: return name;
407: }
408:
409: /**
410: * @return the value
411: */
412: public String getValue() {
413: return value;
414: }
415:
416: /*
417: * (non-Javadoc)
418: *
419: * @see java.lang.Object#toString()
420: */
421: @Override
422: public String toString() {
423: return "[" + name + "=" + value + "]";
424: }
425:
426: /*
427: * (non-Javadoc)
428: *
429: * @see java.lang.Object#hashCode()
430: */
431: @Override
432: public int hashCode() {
433: final int prime = 31;
434: int result = 1;
435: result = prime * result
436: + ((name == null) ? 0 : name.hashCode());
437: result = prime * result
438: + ((value == null) ? 0 : value.hashCode());
439: return result;
440: }
441:
442: /*
443: * (non-Javadoc)
444: *
445: * @see java.lang.Object#equals(java.lang.Object)
446: */
447: @Override
448: public boolean equals(Object obj) {
449: if (this == obj) {
450: return true;
451: }
452: if (getClass() != obj.getClass()) {
453: return false;
454: }
455: final Param other = (Param) obj;
456: if (name == null) {
457: if (other.name != null) {
458: return false;
459: }
460: } else if (!name.equals(other.name)) {
461: return false;
462: }
463: if (value == null) {
464: if (other.value != null) {
465: return false;
466: }
467: } else if (!value.equals(other.value)) {
468: return false;
469: }
470: return true;
471: }
472: }
473:
474: }
|