| /* Copyright 2004 The Apache Software Foundation |
| * |
| * Licensed 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.xmlbeans.soap; |
| |
| import javax.xml.namespace.QName; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Iterator; |
| |
| import org.apache.xmlbeans.impl.values.XmlValueOutOfRangeException; |
| import org.apache.xmlbeans.impl.common.XmlWhitespace; |
| import org.apache.xmlbeans.impl.common.QNameHelper; |
| import org.apache.xmlbeans.impl.common.PrefixResolver; |
| |
| public final class SOAPArrayType |
| { |
| // Example foo:bar[,][][,,][7,9] |
| // -> _type = QName(foo:bar) |
| // -> _ranks = {2,1,3} |
| // -> _dimensions = {7,9} |
| private QName _type; |
| private int[] _ranks; // if ranks is empty, it means there are no nested arrays |
| private int[] _dimensions; // Any dimension can be -1 to indicate "any". |
| |
| /** |
| * True if the ranks for the passed SOAPArrayType |
| * are equal to this one. |
| * |
| * Does NOT compare the _type fields. |
| */ |
| public boolean isSameRankAs(SOAPArrayType otherType) |
| { |
| if (_ranks.length != otherType._ranks.length) |
| return false; |
| for (int i = 0; i < _ranks.length; i++) |
| { |
| if (_ranks[i] != otherType._ranks[i]) |
| return false; |
| } |
| if (_dimensions.length != otherType._dimensions.length) |
| return false; |
| return true; |
| } |
| |
| /** |
| * Given SOAP 1.1-formatted index string, returns an array |
| * index. For example, given "[4,3,5]", returns an int array |
| * containing 4, 3, and 5. |
| */ |
| public static int[] parseSoap11Index(String inbraces) |
| { |
| inbraces = XmlWhitespace.collapse(inbraces, XmlWhitespace.WS_COLLAPSE); |
| if (!inbraces.startsWith("[") || !inbraces.endsWith("]")) |
| throw new IllegalArgumentException("Misformed SOAP 1.1 index: must be contained in braces []"); |
| return internalParseCommaIntString(inbraces.substring(1, inbraces.length() - 1)); |
| } |
| |
| private static int[] internalParseCommaIntString(String csl) |
| { |
| List dimStrings = new ArrayList(); |
| int i = 0; |
| for (;;) |
| { |
| int j = csl.indexOf(',', i); |
| if (j < 0) |
| { |
| dimStrings.add(csl.substring(i)); |
| break; |
| } |
| dimStrings.add(csl.substring(i, j)); |
| i = j + 1; |
| } |
| |
| int[] result = new int[dimStrings.size()]; |
| i = 0; |
| for (Iterator it = dimStrings.iterator(); it.hasNext(); i++) |
| { |
| String dimString = XmlWhitespace.collapse((String)it.next(), XmlWhitespace.WS_COLLAPSE); |
| if (dimString.equals("*") || dimString.equals("")) |
| { |
| result[i] = -1; |
| } |
| else |
| { |
| try |
| { |
| result[i] = Integer.parseInt(dimString); |
| } |
| catch (Exception e) |
| { |
| throw new XmlValueOutOfRangeException("Malformed integer in SOAP array index"); |
| } |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Parses a SOAP 1.1 array type string. |
| * |
| * Since an array type string contains a QName, a prefix resolver |
| * must be passed. |
| */ |
| public SOAPArrayType(String s, PrefixResolver m) |
| { |
| int firstbrace = s.indexOf('['); |
| if (firstbrace < 0) |
| throw new XmlValueOutOfRangeException(); |
| |
| // grab the QName |
| String firstpart = XmlWhitespace.collapse(s.substring(0, firstbrace), XmlWhitespace.WS_COLLAPSE); |
| int firstcolon = firstpart.indexOf(':'); |
| String prefix = ""; |
| if (firstcolon >= 0) |
| prefix = firstpart.substring(0, firstcolon); |
| |
| String uri = m.getNamespaceForPrefix(prefix); |
| if (uri == null) |
| throw new XmlValueOutOfRangeException(); |
| |
| _type = QNameHelper.forLNS(firstpart.substring(firstcolon + 1), uri); |
| |
| initDimensions(s, firstbrace); |
| } |
| |
| private static int[] EMPTY_INT_ARRAY = new int[0]; |
| |
| /** |
| * Parses SOAP 1.1(advanced) array type strings. |
| * |
| * Since in SOAP 1.1(advanced) the dimension specification is separated from the |
| * QName for the underlying type, these are passed in separate |
| * arguments. |
| */ |
| public SOAPArrayType(QName name, String dimensions) |
| { |
| int firstbrace = dimensions.indexOf('['); |
| if (firstbrace < 0) |
| { |
| _type = name; |
| _ranks = EMPTY_INT_ARRAY; |
| dimensions = XmlWhitespace.collapse(dimensions, XmlWhitespace.WS_COLLAPSE); |
| String[] dimStrings = dimensions.split(" "); |
| for (int i = 0; i < dimStrings.length; i++) |
| { |
| String dimString = dimStrings[i]; |
| if (dimString.equals("*")) |
| { |
| _dimensions[i] = -1; |
| // _hasIndeterminateDimensions = true; |
| } |
| else |
| { |
| try |
| { |
| _dimensions[i] = Integer.parseInt(dimStrings[i]); |
| } |
| catch (Exception e) |
| { |
| throw new XmlValueOutOfRangeException(); |
| } |
| } |
| } |
| } |
| else |
| { |
| _type = name; |
| initDimensions(dimensions, firstbrace); |
| } |
| } |
| |
| /** |
| * Given a nested SOAPArrayType and a set of dimensions for the outermost |
| * array, comes up with the right SOAPArrayType for the whole thing. |
| * |
| * E.g., |
| * Nested foo:bar[,][][,,][1,2] |
| * Dimensions [6,7,8] |
| * Result -> foo:bar[,][][,,][,][6,7,8] |
| */ |
| public SOAPArrayType(SOAPArrayType nested, int[] dimensions) |
| { |
| _type = nested._type; |
| |
| _ranks = new int[nested._ranks.length + 1]; |
| System.arraycopy(nested._ranks, 0, _ranks, 0, nested._ranks.length); |
| _ranks[_ranks.length - 1] = nested._dimensions.length; |
| |
| _dimensions = new int[dimensions.length]; |
| System.arraycopy(dimensions, 0, _dimensions, 0, dimensions.length); |
| } |
| |
| /** |
| * Initialize dimensions based on SOAP11 parsed dimension substring |
| */ |
| private void initDimensions(String s, int firstbrace) |
| { |
| List braces = new ArrayList(); |
| int lastbrace = -1; |
| for (int i = firstbrace; i >= 0; ) |
| { |
| lastbrace = s.indexOf(']', i); |
| if (lastbrace < 0) |
| throw new XmlValueOutOfRangeException(); |
| braces.add(s.substring(i + 1, lastbrace)); |
| i = s.indexOf('[', lastbrace); |
| } |
| |
| String trailer = s.substring(lastbrace + 1); |
| if (!XmlWhitespace.isAllSpace(trailer)) |
| throw new XmlValueOutOfRangeException(); |
| |
| // now fill in rank array |
| _ranks = new int[braces.size() - 1]; |
| for (int i = 0; i < _ranks.length; i++) |
| { |
| String commas = (String)braces.get(i); |
| int commacount = 0; |
| for (int j = 0; j < commas.length(); j++) |
| { |
| char ch = commas.charAt(j); |
| if (ch == ',') |
| commacount += 1; |
| else if (!XmlWhitespace.isSpace(ch)) |
| throw new XmlValueOutOfRangeException(); |
| } |
| _ranks[i] = commacount + 1; |
| } |
| |
| // finally fill in dimension array |
| _dimensions = internalParseCommaIntString((String)braces.get(braces.size() - 1)); |
| |
| /* |
| for (int i = 0; i < _dimensions.length; i++) |
| { |
| if (_dimensions[i] < 0) |
| _hasIndeterminateDimensions = true; |
| } |
| */ |
| } |
| |
| /** |
| * Returns the QName for the referenced type. |
| */ |
| public QName getQName() |
| { |
| return _type; |
| } |
| |
| /** |
| * Returns the array of ranks for inner nested arrays. |
| * In SOAP 1.1-advanced, this is always an array of length zero. |
| * In SOAP 1.1, this array reflects the ranks of nested |
| * arrays. For example foo:bar[,][,,][][5,6] will produce |
| * a ranks result of 2, 3, 1. |
| */ |
| public int[] getRanks() |
| { |
| int[] result = new int[_ranks.length]; |
| System.arraycopy(_ranks, 0, result, 0, result.length); |
| return result; |
| } |
| |
| /** |
| * Returns the array of dimensions. |
| */ |
| public int[] getDimensions() |
| { |
| int[] result = new int[_dimensions.length]; |
| System.arraycopy(_dimensions, 0, result, 0, result.length); |
| return result; |
| } |
| |
| /** |
| * True if this array contains nested arrays. Equivalent |
| * to (getRanks().length > 0). |
| */ |
| public boolean containsNestedArrays() |
| { |
| return (_ranks.length > 0); |
| } |
| |
| /** |
| * Returns the dimensions as a string, e.g., [,][2,3,4] |
| */ |
| public String soap11DimensionString() |
| { |
| return soap11DimensionString(_dimensions); |
| } |
| |
| /** |
| * Given an actual set of dimensions that may differ from |
| * the default that is stored, outputs the soap arrayType |
| * string. |
| */ |
| public String soap11DimensionString(int[] actualDimensions) |
| { |
| StringBuilder sb = new StringBuilder(); |
| for (int i = 0; i < _ranks.length; i++) |
| { |
| sb.append('['); |
| for (int j = 1; j < _ranks[i]; j++) |
| sb.append(','); |
| sb.append(']'); |
| } |
| |
| sb.append('['); |
| for (int i = 0; i < actualDimensions.length; i++) |
| { |
| if (i > 0) |
| sb.append(','); |
| if (actualDimensions[i] >= 0) |
| sb.append(actualDimensions[i]); |
| } |
| sb.append(']'); |
| return sb.toString(); |
| } |
| |
| private SOAPArrayType() |
| { |
| } |
| |
| /** |
| * SOAP 1.2 |
| * Constructs a SOAPArrayType from soap-enc:itemType and |
| * soap-enc:arraySize attributes |
| * @param itemType the item type QName |
| * @param arraySize a string with dimentions like: * 3 4 |
| * @return a SOAPArrayType to represent this |
| */ |
| public static SOAPArrayType newSoap12Array(QName itemType, String arraySize) |
| { |
| int [] ranks = EMPTY_INT_ARRAY; |
| arraySize = XmlWhitespace.collapse(arraySize, XmlWhitespace.WS_COLLAPSE); |
| String[] dimStrings = arraySize.split(" "); |
| int[] dimensions = new int[dimStrings.length]; |
| for (int i = 0; i < dimStrings.length; i++) |
| { |
| String dimString = dimStrings[i]; |
| if (i==0 && dimString.equals("*")) |
| { |
| dimensions[i] = -1; |
| // _hasIndeterminateDimensions = true; |
| } |
| else |
| { |
| try |
| { |
| dimensions[i] = Integer.parseInt(dimStrings[i]); |
| } |
| catch (Exception e) |
| { |
| throw new XmlValueOutOfRangeException(); |
| } |
| } |
| } |
| SOAPArrayType sot = new SOAPArrayType(); |
| sot._ranks = ranks; |
| sot._type = itemType; |
| sot._dimensions = dimensions; |
| return sot; |
| } |
| |
| /** |
| * SOAP 1.2 |
| * Given an actual set of dimensions that may differ from |
| * the default that is stored, outputs the soap arraySize |
| * string. |
| */ |
| public String soap12DimensionString(int[] actualDimensions) |
| { |
| StringBuilder sb = new StringBuilder(); |
| |
| for (int i = 0; i < actualDimensions.length; i++) |
| { |
| if (i > 0) |
| sb.append(' '); |
| if (actualDimensions[i] >= 0) |
| sb.append(actualDimensions[i]); |
| } |
| return sb.toString(); |
| } |
| |
| /** |
| * Constructs a SOAPArrayType reflecting the dimensions |
| * of the next nested array. |
| */ |
| public SOAPArrayType nestedArrayType() |
| { |
| if (!containsNestedArrays()) |
| throw new IllegalStateException(); |
| |
| SOAPArrayType result = new SOAPArrayType(); |
| |
| result._type = _type; |
| |
| result._ranks = new int[_ranks.length - 1]; |
| System.arraycopy(_ranks, 0, result._ranks, 0, result._ranks.length); |
| |
| result._dimensions = new int[_ranks[_ranks.length - 1]]; |
| for (int i = 0; i < result._dimensions.length; i++) |
| result._dimensions[i] = -1; |
| |
| // result._hasIndeterminateDimensions = (result._dimensions.length > 0); |
| |
| return result; |
| } |
| |
| public int hashCode() |
| { |
| return (_type.hashCode() + _dimensions.length + _ranks.length + (_dimensions.length == 0 ? 0 : _dimensions[0])); |
| } |
| |
| public boolean equals(Object obj) |
| { |
| if (obj == this) |
| return true; |
| |
| if (!obj.getClass().equals(getClass())) |
| return false; |
| |
| SOAPArrayType sat = (SOAPArrayType)obj; |
| |
| if (!_type.equals(sat._type)) |
| return false; |
| |
| if (_ranks.length != sat._ranks.length) |
| return false; |
| |
| if (_dimensions.length != sat._dimensions.length) |
| return false; |
| |
| for (int i = 0; i < _ranks.length; i++) |
| if (_ranks[i] != sat._ranks[i]) |
| return false; |
| |
| for (int i = 0; i < _dimensions.length; i++) |
| if (_dimensions[i] != sat._dimensions[i]) |
| return false; |
| |
| return true; |
| } |
| } |