/** * Copyright (c) 2004-2007 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM - Initial API and implementation */ package org.eclipse.emf.ecore.xml.type.util; import java.util.concurrent.atomic.AtomicReference; import javax.xml.datatype.DatatypeConstants; import javax.xml.datatype.Duration; import javax.xml.datatype.XMLGregorianCalendar; import javax.xml.namespace.QName; import org.eclipse.emf.ecore.EValidator; import org.eclipse.emf.ecore.xml.type.internal.DataValue; import org.eclipse.emf.ecore.xml.type.internal.RegEx; /** * This class contains convenient static methods for working with XML-related information. */ public final class XMLTypeUtil { public static final int EQUALS = 0; public static final int LESS_THAN = -1; public static final int GREATER_THAN = 1; public static final int INDETERMINATE = 2; public static int compareCalendar(Object calendar1, Object calendar2) { switch (((XMLGregorianCalendar)calendar1).compare((XMLGregorianCalendar)calendar2)) { case DatatypeConstants.EQUAL: { return EQUALS; } case DatatypeConstants.LESSER: { return LESS_THAN; } case DatatypeConstants.GREATER: { return GREATER_THAN; } default: { return INDETERMINATE; } } } public static int compareDuration(Object duration1, Object duration2) { switch (((Duration)duration1).compare((Duration)duration2)) { case DatatypeConstants.EQUAL: { return EQUALS; } case DatatypeConstants.LESSER: { return LESS_THAN; } case DatatypeConstants.GREATER: { return GREATER_THAN; } default: { return INDETERMINATE; } } } public static boolean isSpace(char value) { return DataValue.XMLChar.isSpace(value); } // This is faster than many charAt() calls. // private static final class CharArrayPool { private static final int MAX_CACHE_CAPACITY; static { // Set a reasonably small default limit. // int result = 10000; try { String property = System.getProperty("org.eclipse.emf.ecore.xml.type.util.XMLTypeUtil.CharArrayThreadLocal.MAX_CACHE_CAPACITY"); if (property != null) { result = Integer.valueOf(property); } } catch (Throwable throwable) { // Ignore all exceptions, including security exceptions. } MAX_CACHE_CAPACITY = result; } private class Buffer { Buffer next; char[] value; public char[] get(int capacity) { if (value == null || value.length < capacity) { value = new char [capacity < 20 ? 20 : capacity]; } return value; } public void finished() { if (value.length <= MAX_CACHE_CAPACITY) { for (;;) { next = head.get(); if (head.compareAndSet(next, this)) { break; } } } } } private AtomicReference<Buffer> head = new AtomicReference<Buffer>(); public Buffer get() { for (;;) { Buffer buffer = head.get(); if (buffer != null) { if (head.compareAndSet(buffer, buffer.next)) { return buffer; } } else { return new Buffer(); } } } } private static final CharArrayPool VALUE = new CharArrayPool(); public static String normalize(String value, boolean collapse) { if (value == null) { return null; } int length = value.length(); if (length == 0) { return ""; } CharArrayPool.Buffer pooledBuffer = VALUE.get(); char [] valueArray = pooledBuffer.get(length); value.getChars(0, length, valueArray, 0); StringBuffer buffer = null; boolean skipSpace = collapse; for (int i = 0, offset = 0; i < length; i++) { char c = valueArray[i]; if (isSpace(c)) { if (skipSpace) { if (buffer == null) { buffer = new StringBuffer(value); } buffer.deleteCharAt(i - offset++); } else { skipSpace = collapse; if (c != ' ') { if (buffer == null) { buffer = new StringBuffer(value); } buffer.setCharAt(i - offset, ' '); } } } else { skipSpace = false; } } pooledBuffer.finished(); if (skipSpace) { if (buffer == null) { return value.substring(0, length - 1); } else { length = buffer.length(); if (length > 0) { return buffer.substring(0, length - 1); } else { return ""; } } } else { if (buffer == null) { return value; } else { return buffer.toString(); } } } public static EValidator.PatternMatcher createPatternMatcher(String pattern) { return new PatternMatcherImpl(pattern); } /** * Creates a new QName object with the specified values * @param namespaceUri namespace uri value or null * @param localPart localPart (not null) * @param prefix prefix value or null (if null, an empty string will actually be used in the resulting QName) * @return The newly created QName object */ public static Object createQName(String namespaceUri, String localPart, String prefix) { return new org.eclipse.emf.ecore.xml.type.internal.QName(namespaceUri, localPart, prefix); } /** * Sets the QName object values to the specified once * @param namespaceUri namespace uri value or null * @param localPart localPart (not null) * @param prefix prefix value or null */ @Deprecated public static void setQNameValues(Object qName, String namespaceUri, String localPart, String prefix) { if (!(qName instanceof org.eclipse.emf.ecore.xml.type.internal.QName)) { throw new UnsupportedOperationException("QNames are immutable, so this can't be supported"); } if (namespaceUri == null) { namespaceUri = ""; } org.eclipse.emf.ecore.xml.type.internal.QName qn = (org.eclipse.emf.ecore.xml.type.internal.QName)qName; if (!qn.getLocalPart().equals(localPart) || qn.getNamespaceURI().equals(namespaceUri)) { throw new UnsupportedOperationException("QNames are immutable, so this can't be supported"); } qn.setPrefix(prefix); } /** * Updates the QName's prefix, if possible, and returns either the updated result, * or a newly created QName with the new prefix, if the QName could not be directly updated. * @param qName the QName to be updated. * @param prefix the new prefix. * @return a QName with the same namespace URI and local part as the argument, but with the new prefix. */ public static QName setPrefix(QName qName, String prefix) { if (qName instanceof org.eclipse.emf.ecore.xml.type.internal.QName) { org.eclipse.emf.ecore.xml.type.internal.QName result = (org.eclipse.emf.ecore.xml.type.internal.QName)qName; result.setPrefix(prefix); return result; } else { return new org.eclipse.emf.ecore.xml.type.internal.QName(qName.getNamespaceURI(), qName.getLocalPart(), prefix); } } /** * Returns the namespaceURI of a QName. */ public static String getQNameNamespaceURI(Object qName) { return ((QName)qName).getNamespaceURI(); } /** * Returns the localPart of a QName. */ public static String getQNameLocalPart(Object qName) { return ((QName)qName).getLocalPart(); } /** * Returns the prefix of a QName. */ public static String getQNamePrefix(Object qName) { return ((QName)qName).getPrefix(); } private static class PatternMatcherImpl implements EValidator.PatternMatcher { protected RegEx.RegularExpression regularExpression; public PatternMatcherImpl(String pattern) { regularExpression = new RegEx.RegularExpression(pattern, "X"); } public boolean matches(String value) { return regularExpression.matches(value); } @Override public String toString() { return regularExpression.getPattern(); } } /** * Returns whether the code point is the valid start of an XML Name. */ public static boolean isNameStart(int codePoint) { return DataValue.XMLChar.isNameStart(codePoint); } /** * Returns whether the code point is a valid part of an XML Name. */ public static boolean isNamePart(int codePoint) { return DataValue.XMLChar.isName(codePoint); } /** * Returns whether the code point is the valid start of an XML NCName. */ public static boolean isNCNameStart(int codePoint) { return DataValue.XMLChar.isNCNameStart(codePoint); } /** * Returns whether the code point is a valid part of an XML NCName. */ public static boolean isNCNamePart(int codePoint) { return DataValue.XMLChar.isNCName(codePoint); } }