| /* |
| * Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| |
| package org.eclipse.persistence.internal.xr; |
| |
| // Javase imports |
| import static org.eclipse.persistence.internal.helper.ClassConstants.APBYTE; |
| import static org.eclipse.persistence.internal.helper.ClassConstants.BIGDECIMAL; |
| import static org.eclipse.persistence.internal.helper.ClassConstants.BIGINTEGER; |
| import static org.eclipse.persistence.internal.helper.ClassConstants.BOOLEAN; |
| import static org.eclipse.persistence.internal.helper.ClassConstants.BYTE; |
| import static org.eclipse.persistence.internal.helper.ClassConstants.CALENDAR; |
| import static org.eclipse.persistence.internal.helper.ClassConstants.DOUBLE; |
| import static org.eclipse.persistence.internal.helper.ClassConstants.FLOAT; |
| import static org.eclipse.persistence.internal.helper.ClassConstants.INTEGER; |
| import static org.eclipse.persistence.internal.helper.ClassConstants.LONG; |
| import static org.eclipse.persistence.internal.helper.ClassConstants.Object_Class; |
| import static org.eclipse.persistence.internal.helper.ClassConstants.SHORT; |
| import static org.eclipse.persistence.internal.helper.ClassConstants.STRING; |
| import static org.eclipse.persistence.internal.oxm.Constants.ANY_SIMPLE_TYPE_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.BASE_64_BINARY_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.BOOLEAN_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.BYTE_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.DATE_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.DATE_TIME_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.DECIMAL_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.DOUBLE_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.DURATION_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.FLOAT_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.G_DAY_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.G_MONTH_DAY_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.G_MONTH_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.G_YEAR_MONTH_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.G_YEAR_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.HEX_BINARY_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.INTEGER_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.INT_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.LONG_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.QNAME_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.SHORT_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.STRING_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.TIME_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.UNSIGNED_BYTE_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.UNSIGNED_INT_QNAME; |
| import static org.eclipse.persistence.internal.oxm.Constants.UNSIGNED_SHORT_QNAME; |
| import static org.eclipse.persistence.internal.xr.sxf.SimpleXMLFormat.DEFAULT_SIMPLE_XML_FORMAT_TAG; |
| |
| import java.sql.Types; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Locale; |
| import java.util.Map; |
| |
| // Java extension libraries |
| import javax.xml.namespace.QName; |
| |
| // EclipseLink imports |
| import org.eclipse.persistence.internal.databaseaccess.DatabasePlatform; |
| import org.eclipse.persistence.platform.xml.XMLPlatform; |
| import org.eclipse.persistence.platform.xml.XMLPlatformFactory; |
| import org.w3c.dom.Document; |
| |
| /** |
| * <p><b>INTERNAL</b>: provides useful constants, SQL Column <-> to XML name mapping and |
| * a few other misc. features |
| * |
| * @author Mike Norman - michael.norman@oracle.com |
| * @since EclipseLink 1.x |
| */ |
| @SuppressWarnings("serial") |
| public class Util { |
| public static final XMLPlatform XML_PLATFORM = XMLPlatformFactory.getInstance().getXMLPlatform(); |
| public static final Document TEMP_DOC = XML_PLATFORM.createDocument(); |
| public static final int OPAQUE = 2007; |
| public static final String DEFAULT_ATTACHMENT_MIMETYPE = "application/octet-stream"; |
| public static final String WEB_INF_DIR = "WEB-INF/"; |
| public static final String WSDL_DIR = "wsdl/"; |
| public static final String[] META_INF_PATHS = { "META-INF/", "/META-INF/" }; |
| public static final String DBWS_SERVICE_XML = "eclipselink-dbws.xml"; |
| public static final String DBWS_OR_LABEL = "dbws-or"; |
| public static final String DBWS_OX_LABEL = "dbws-ox"; |
| public static final String DBWS_OR_XML = "eclipselink-" + DBWS_OR_LABEL + ".xml"; |
| public static final String DBWS_OX_XML = "eclipselink-" + DBWS_OX_LABEL + ".xml"; |
| public static final String DBWS_SCHEMA_XML = "eclipselink-dbws-schema.xsd"; |
| public static final String DBWS_WSDL = "eclipselink-dbws.wsdl"; |
| public static final String DBWS_SESSIONS_XML = "eclipselink-dbws-sessions.xml"; |
| public static final String DBWS_OR_SESSION_NAME_SUFFIX = DBWS_OR_LABEL + "-session"; |
| public static final String DBWS_OX_SESSION_NAME_SUFFIX = DBWS_OX_LABEL + "-session"; |
| public static final String TARGET_NAMESPACE_PREFIX = "ns1"; |
| public static final String SERVICE_NAMESPACE_PREFIX = "srvc"; |
| public static final String SERVICE_SUFFIX = "Service"; |
| public static final String ALL_QUERYNAME = "findAll"; |
| public static final String PK_QUERYNAME = "findByPrimaryKey"; |
| public static final String XMLTYPE_STR = "XMLTYPE"; |
| public static final String DOT_STR = "."; |
| public static final String UNDERSCORE_STR = "_"; |
| public static final String TYPE_STR = "Type"; |
| public static final String COLLECTION_WRAPPER_STR = "CollectionWrapper"; |
| public static final String DASH_STR = "-"; |
| public static final String EMPTY_STR = ""; |
| public static final char COLON_CHAR = ':'; |
| public static final char SLASH_CHAR = '/'; |
| |
| /** |
| * Convert a SQL name to a valid XML name. Because not all characters that |
| * are valid in a SQL name is valid in an XML name, they need to be escaped |
| * using a special format. See the Oracle paper,"SQL/XML candidate base |
| * document", for more detail |
| * |
| * @param name |
| * the SQL name |
| * |
| * @return the escaped valid XML name |
| */ |
| public static String sqlToXmlName(String name) { |
| |
| int length = name.length(); |
| if (length == 0) { |
| return name; |
| } |
| |
| StringBuilder xmlName = new StringBuilder(); |
| int beginAt = 1; |
| char firstChar = name.charAt(0); |
| // escape : to _x003A_ |
| if (firstChar == ':') { |
| xmlName.append("_x003A_"); |
| } |
| // escape _ of _x to _x005F_ |
| else if ((length >= 2) && name.substring(0, 2).equals("_x")) { |
| xmlName.append("_x005F_"); |
| } |
| // check to see if it is a valid first character |
| else { |
| if ((firstChar >= 0xd800) && (firstChar < 0xdc00)) { |
| // surrogate |
| if (length > 1) { |
| xmlName.append(hexEscape((firstChar << 16) | (name.charAt(1) & 0xffff))); |
| beginAt = 2; |
| } else { |
| xmlName.append(hexEscape(firstChar)); |
| } |
| } else if (isFirstNameChar(firstChar)) { |
| xmlName.append(firstChar); |
| } else { |
| xmlName.append(hexEscape(firstChar)); |
| } |
| } |
| |
| // check each following character to see if it is a valid NameChar |
| char c; |
| for (int x = beginAt; x < length; x++) { |
| c = name.charAt(x); |
| |
| if ((c >= 0xd800) && (c < 0xdc00)) { |
| // surrogate |
| if ((x + 1) < length) { |
| xmlName.append(hexEscape((c << 16) | (name.charAt(x + 1) & 0xffff))); |
| x++; |
| } else { |
| xmlName.append(hexEscape(c)); |
| } |
| } else if (!isNameChar(c)) { |
| // escape |
| xmlName.append(hexEscape(c)); |
| } else { |
| xmlName.append(c); |
| } |
| } |
| return xmlName.toString(); |
| } |
| |
| /** |
| * Convert an escaped XML name back to the original SQL name |
| * |
| * @param name |
| * the escaped XML name |
| * |
| * @return the original SQL name |
| */ |
| public static String xmlToSqlName(String name) { |
| |
| String sqlName = ""; |
| int length = name.length(); |
| boolean unescapeMode = false; |
| String hexString = null; |
| char c; |
| // step through each one |
| for (int x = 0; x < length; x++) { |
| c = name.charAt(x); |
| if (unescapeMode) { |
| // we are in unescape mode |
| if (((c >= 'A') && (c <= 'F')) || ((c >= '0') && (c <= '9'))) { |
| // gather the hex string from the escape sequence |
| hexString = hexString + c; |
| } else if (c == '_') { |
| // done with escape mode |
| unescapeMode = false; |
| if (hexString != null) { |
| int i; |
| int len; |
| if ((len = hexString.length()) > 4) { |
| char i1 = (char) (Integer.parseInt(hexString.substring(0, len - 4), 16)); |
| char i2 = (char) (Integer.parseInt(hexString.substring(len - 4), 16)); |
| sqlName += i1; |
| sqlName += i2; |
| } else { |
| try { |
| i = Integer.parseInt(hexString, 16); |
| if (i != 0xffff) { |
| sqlName += (char) i; |
| } |
| } catch (NumberFormatException nfe) { |
| throw new RuntimeException(nfe); |
| } |
| } |
| } |
| } else { |
| // invalid char in escape sequence! write everything into |
| // sqlName as is, or we could throw an exception here |
| // in the future |
| sqlName += ("_x" + hexString + c); |
| unescapeMode = false; |
| } |
| } else { |
| if ((c == '_') && ((x + 1) < length) && (name.charAt(x + 1) == 'x')) { |
| // found escape beginning _x |
| // go into unescape mode |
| unescapeMode = true; |
| hexString = ""; |
| x++; |
| } else { |
| // just copy src char to destination |
| sqlName += c; |
| } |
| } |
| } |
| return sqlName; |
| } |
| |
| public static String hexEscape(char c) { |
| String outPutString; |
| outPutString = Integer.toHexString(c); |
| switch (outPutString.length()) { |
| case 1: |
| outPutString = "_x000" + outPutString.toUpperCase(Locale.US) + "_"; |
| break; |
| case 2: |
| outPutString = "_x00" + outPutString.toUpperCase(Locale.US) + "_"; |
| break; |
| case 3: |
| outPutString = "_x0" + outPutString.toUpperCase(Locale.US) + "_"; |
| break; |
| case 4: |
| outPutString = "_x" + outPutString.toUpperCase(Locale.US) + "_"; |
| break; |
| } |
| return outPutString; |
| } |
| |
| public static String hexEscape(int c) { |
| String outPutString; |
| outPutString = Integer.toHexString(c); |
| switch (outPutString.length()) { |
| case 1: |
| case 5: |
| outPutString = "_x000" + outPutString.toUpperCase(Locale.US) + "_"; |
| break; |
| case 2: |
| case 6: |
| outPutString = "_x00" + outPutString.toUpperCase(Locale.US) + "_"; |
| break; |
| case 3: |
| case 7: |
| outPutString = "_x0" + outPutString.toUpperCase(Locale.US) + "_"; |
| break; |
| case 4: |
| case 8: |
| outPutString = "_x" + outPutString.toUpperCase(Locale.US) + "_"; |
| break; |
| } |
| return outPutString; |
| } |
| |
| /** |
| * return true if character can be part of a name |
| * |
| * @param c - |
| * char to be checked |
| * @return true/false |
| */ |
| public static boolean isNameChar(char c) { |
| // In most cases the character is less than 256, so this check |
| // is made fast. For the rest of the characters the check is |
| // based on the conformance tests. XML 1.1 has changed the list |
| // of chars allowed, and the check is quite simple. So the |
| // complete check based on XML 1.0 is not performed. |
| boolean res; |
| if (c < 256) |
| res = (chartype[c] & (FLETTER | FDIGIT | FMISCNAME)) != 0; |
| else { |
| // [#x2180-#x2182] | [#x3041-#x3094] | [#x30A1-#x30FA] | |
| // [#x3105-#x312C] | [#xAC00-#xD7A3] |
| // [#x0E47-#x0E4E] |
| if (((c >= 0x2180) && (c <= 0x2182)) || ((c >= 0x3041) && (c <= 0x3094)) |
| || ((c >= 0x30A1) && (c <= 0x30FA)) || ((c >= 0x3105) && (c <= 0x312C)) |
| || ((c >= 0xAC00) && (c <= 0xD7A3)) || ((c >= 0x0E47) && (c <= 0x0E4E))) |
| res = true; |
| else { |
| if ((c == 0x02FF) || (c == 0x0346) || (c == 0x0362) || (c == 0x0487) |
| || (c == 0x05A2) || (c == 0x05BA) || (c == 0x05BE) || (c == 0x05C0) |
| || (c == 0x05C3) || (c == 0x0653) || (c == 0x06B8) || (c == 0x06B9) |
| || (c == 0x06E9) || (c == 0x06EE) || (c == 0x0904) || (c == 0x093B) |
| || (c == 0x094E) || (c == 0x0955) || (c == 0x0964) || (c == 0x0984) |
| || (c == 0x09C5) || (c == 0x09C9) || (c == 0x09CE) || (c == 0x09D8) |
| || (c == 0x09E4) || (c == 0x0A03) || (c == 0x0A3D) || (c == 0x0A46) |
| || (c == 0x0A49) || (c == 0x0A4E) || (c == 0x0A80) || (c == 0x0A84) |
| || (c == 0x0ABB) || (c == 0x0AC6) || (c == 0x0ACA) || (c == 0x0ACE) |
| || (c == 0x0B04) || (c == 0x0B3B) || (c == 0x0B44) || (c == 0x0B4A) |
| || (c == 0x0B4E) || (c == 0x0B58) || (c == 0x0B84) || (c == 0x0BC3) |
| || (c == 0x0BC9) || (c == 0x0BD6) || (c == 0x0C0D) || (c == 0x0C45) |
| || (c == 0x0C49) || (c == 0x0C54) || (c == 0x0C81) || (c == 0x0C84) |
| || (c == 0x0CC5) || (c == 0x0CC9) || (c == 0x0CD4) || (c == 0x0CD7) |
| || (c == 0x0D04) || (c == 0x0D45) || (c == 0x0D49) || (c == 0x0D4E) |
| || (c == 0x0D58) || (c == 0x0E3F) || (c == 0x0E3B) || (c == 0x0E4F) |
| || (c == 0x0EBA) || (c == 0x0EBE) || (c == 0x0ECE) || (c == 0x0F1A) |
| || (c == 0x0F36) || (c == 0x0F38) || (c == 0x0F3B) || (c == 0x0F3A) |
| || (c == 0x0F70) || (c == 0x0F85) || (c == 0x0F8C) || (c == 0x0F96) |
| || (c == 0x0F98) || (c == 0x0FB0) || (c == 0x0FB8) || (c == 0x0FBA) |
| || (c == 0x20DD) || (c == 0x20E2) || (c == 0x3030) || (c == 0x309B) |
| || (c == 0x066A) || (c == 0x06FA) || (c == 0x0970) || (c == 0x09F2) |
| || (c == 0x0AF0) || (c == 0x0B70) || (c == 0x0C65) || (c == 0x0CE5) |
| || (c == 0x0CF0) || (c == 0x0D70) || (c == 0x0E5A) || (c == 0x0EDA) |
| || (c == 0x0F2A) || (c == 0x02D2) || (c == 0x03FE) || (c == 0x065F) |
| || (c == 0x0E5C) || (c == 0x0C04)) |
| res = false; |
| else { |
| // Character.isLetter(c) || Character.isDigit(c) || '-' || '-' || '.' |
| // is known to be true at this point |
| res = true; |
| } |
| } |
| } |
| return res; |
| } |
| |
| /** |
| * return true if character can be part of a name |
| * |
| * @param c - |
| * char to be checked |
| * @return true/false |
| */ |
| public static boolean isFirstNameChar(char c) { |
| // In most cases the character is less than 256, so this check |
| // is made fast. For the rest of the characters the check is |
| // based on the conformance tests. XML 1.1 has changed the list |
| // of chars allowed, and the check is quite simple. So the |
| // complete check based on XML 1.0 is not performed. |
| boolean res; |
| if (c < 256) |
| res = (chartype[c] & (FLETTER | FSTARTNAME)) != 0; |
| else { |
| // [#x2180-#x2182] | [#x3041-#x3094] | [#x30A1-#x30FA] | |
| // [#x3105-#x312C] | [#xAC00-#xD7A3] |
| if (((c >= 0x2180) && (c <= 0x2182)) || (c == 0x3007) |
| || ((c >= 0x3021) && (c <= 0x3029)) || ((c >= 0x3041) && (c <= 0x3094)) |
| || ((c >= 0x30A1) && (c <= 0x30FA)) || ((c >= 0x3105) && (c <= 0x312C)) |
| || ((c >= 0xAC00) && (c <= 0xD7A3))) |
| res = true; |
| else { |
| if ((c == 0x1101) || (c == 0x1104) || (c == 0x1108) || (c == 0x110A) |
| || (c == 0x110D) || (c == 0x113B) || (c == 0x1141) || (c == 0x114D) |
| || (c == 0x114F) || (c == 0x1151) || (c == 0x1156) || (c == 0x1162) |
| || (c == 0x1164) || (c == 0x1166) || (c == 0x116B) || (c == 0x116F) |
| || (c == 0x1174) || (c == 0x119F) || (c == 0x11AC) || (c == 0x11B6) |
| || (c == 0x11B9) || (c == 0x11BB) || (c == 0x11C3) || (c == 0x11F1) |
| || (c == 0x0132) || (c == 0x0133) || (c == 0x013F) || (c == 0x0140) |
| || (c == 0x0149) || (c == 0x017F) || (c == 0x01C4) || (c == 0x01CC) |
| || (c == 0x01F1) || (c == 0x01F3) || (c == 0x0E46) || (c == 0x113F) |
| || (c == 0x01F6) || (c == 0x01F9) || (c == 0x0230) || (c == 0x03D7) |
| || (c == 0x03DD) || (c == 0x03E1) || (c == 0x040D) || (c == 0x0450) |
| || (c == 0x045D) || (c == 0x04EC) || (c == 0x04ED) || (c == 0x06B8) |
| || (c == 0x06BF) || (c == 0x06CF) || (c == 0x0E2F) || (c == 0x0EAF) |
| || (c == 0x0F6A) || (c == 0x4CFF) || (c == 0x212F) || (c == 0x0587)) { |
| res = false; |
| } else { |
| //Character.isLetter(c) || c == '_' is known to be true here |
| res = true; |
| } |
| } |
| } |
| return res; |
| } |
| |
| /** |
| * Char type table |
| */ |
| static final int chartype[] = new int[256]; |
| static final int FWHITESPACE = 1; |
| static final int FDIGIT = 2; |
| static final int FLETTER = 4; |
| static final int FMISCNAME = 8; |
| static final int FSTARTNAME = 16; |
| static { |
| for (int i = 0; i < 256; i++) { |
| char c = (char) i; |
| chartype[i] = 0; |
| if ((c == 32) || (c == 9) || (c == 13) || (c == 10)) |
| chartype[i] = FWHITESPACE; |
| if (Character.isLetter(c)) |
| chartype[i] |= FLETTER; |
| if (Character.isDigit(c)) |
| chartype[i] |= FDIGIT; |
| } |
| chartype['.'] |= FMISCNAME; |
| chartype['-'] |= FMISCNAME; |
| chartype['_'] |= FMISCNAME | FSTARTNAME; |
| chartype[0xb7] |= FMISCNAME; // Extender |
| } |
| |
| public static Class<?> getClassFromJDBCType(String typeName, DatabasePlatform databasePlatform) { |
| Class<?> clz = databasePlatform.getClassTypes().get(typeName); |
| if (clz == null) { |
| return Object_Class; |
| } |
| return clz; |
| } |
| |
| public static final QName SXF_QNAME = new QName("", DEFAULT_SIMPLE_XML_FORMAT_TAG); |
| |
| public static final Map<QName, Class<?>> SCHEMA_2_CLASS; |
| static { |
| SCHEMA_2_CLASS = Collections.unmodifiableMap(new HashMap<QName, Class<?>>() {{ |
| put(ANY_SIMPLE_TYPE_QNAME,Object_Class); |
| put(BASE_64_BINARY_QNAME, APBYTE); |
| put(BOOLEAN_QNAME, BOOLEAN); |
| put(BYTE_QNAME, BYTE); |
| //put(DATE_QNAME, SQLDATE); |
| put(DATE_QNAME, CALENDAR); |
| //put(DATE_TIME_QNAME, TIMESTAMP); |
| put(DATE_TIME_QNAME, CALENDAR); |
| put(DECIMAL_QNAME, BIGDECIMAL); |
| put(DOUBLE_QNAME, DOUBLE); |
| put(DURATION_QNAME, STRING); |
| put(FLOAT_QNAME, FLOAT); |
| put(G_YEAR_MONTH_QNAME, STRING); |
| put(G_YEAR_QNAME, STRING); |
| put(G_MONTH_QNAME, STRING); |
| put(G_MONTH_DAY_QNAME, STRING); |
| put(G_DAY_QNAME, STRING); |
| put(HEX_BINARY_QNAME, APBYTE); |
| put(INT_QNAME, INTEGER); |
| put(INTEGER_QNAME, BIGINTEGER); |
| put(LONG_QNAME, LONG); |
| put(QNAME_QNAME, QName.class); |
| put(SHORT_QNAME, SHORT); |
| put(STRING_QNAME, STRING); |
| //put(TIME_QNAME, TIME); |
| put(TIME_QNAME, CALENDAR); |
| put(UNSIGNED_BYTE_QNAME, SHORT); |
| put(UNSIGNED_INT_QNAME, LONG); |
| put(UNSIGNED_SHORT_QNAME, INTEGER); |
| }}); |
| } |
| |
| /** |
| * Return the type name to be used for a given JDBC type. This will |
| * typically be used when setting the SQL type and type name on a |
| * stored function/procedure argument. Currently, the only case |
| * we need to handle in this manner is oracle.xdb.XMLType - here |
| * we may set 2007 (OPAQUE) or 2009 (SQLXML). |
| * |
| * In the future this method may be required to return more types. |
| */ |
| public static String getTypeNameForJDBCType(int jdbcType) { |
| String typeName = null; |
| switch (jdbcType) { |
| case OPAQUE: |
| case Types.SQLXML: |
| typeName = XMLTYPE_STR; |
| break; |
| default: |
| break; |
| } |
| return typeName; |
| } |
| |
| public static int getJDBCTypeForTypeName(String typeName) { |
| int typeCode = -1; |
| if (typeName.equals(XMLTYPE_STR)) { |
| // we currently use oracle.sql.OPAQUE for XMLType |
| typeCode = OPAQUE; |
| } |
| return typeCode; |
| } |
| } |