Path: blob/jdk8u272-b10-aarch32-20201026/jaxp/src/com/sun/xml/internal/stream/XMLEntityStorage.java
83408 views
/*1* Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.sun.xml.internal.stream;2627import com.sun.org.apache.xerces.internal.impl.Constants;28import com.sun.org.apache.xerces.internal.impl.PropertyManager;29import com.sun.org.apache.xerces.internal.impl.XMLEntityManager;30import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;31import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;32import com.sun.org.apache.xerces.internal.util.URI;33import com.sun.org.apache.xerces.internal.util.XMLResourceIdentifierImpl;34import com.sun.org.apache.xerces.internal.utils.SecuritySupport;35import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;36import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;37import java.util.Collections;38import java.util.Enumeration;39import java.util.HashMap;40import java.util.Map;4142/**43*44* @author K.Venugopal SUN Microsystems45* @author Neeraj Bajaj SUN Microsystems46* @author Andy Clark, IBM47*48*/49public class XMLEntityStorage {5051/** Property identifier: error reporter. */52protected static final String ERROR_REPORTER =53Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;5455/** Feature identifier: warn on duplicate EntityDef */56protected static final String WARN_ON_DUPLICATE_ENTITYDEF =57Constants.XERCES_FEATURE_PREFIX +Constants.WARN_ON_DUPLICATE_ENTITYDEF_FEATURE;5859/** warn on duplicate Entity declaration.60* http://apache.org/xml/features/warn-on-duplicate-entitydef61*/62protected boolean fWarnDuplicateEntityDef;6364/** Entities. */65protected Map<String, Entity> fEntities = new HashMap<>();6667protected Entity.ScannedEntity fCurrentEntity ;6869private XMLEntityManager fEntityManager;70/**71* Error reporter. This property identifier is:72* http://apache.org/xml/properties/internal/error-reporter73*/74protected XMLErrorReporter fErrorReporter;75protected PropertyManager fPropertyManager ;7677/* To keep track whether an entity is declared in external or internal subset*/78protected boolean fInExternalSubset = false;7980/** Creates a new instance of XMLEntityStorage */81public XMLEntityStorage(PropertyManager propertyManager) {82fPropertyManager = propertyManager ;83}8485/** Creates a new instance of XMLEntityStorage */86/*public XMLEntityStorage(Entity.ScannedEntity currentEntity) {87fCurrentEntity = currentEntity ;*/88public XMLEntityStorage(XMLEntityManager entityManager) {89fEntityManager = entityManager;90}9192public void reset(PropertyManager propertyManager){9394fErrorReporter = (XMLErrorReporter)propertyManager.getProperty(Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY);95fEntities.clear();96fCurrentEntity = null;9798}99100public void reset(){101fEntities.clear();102fCurrentEntity = null;103}104/**105* Resets the component. The component can query the component manager106* about any features and properties that affect the operation of the107* component.108*109* @param componentManager The component manager.110*111* @throws SAXException Thrown by component on initialization error.112* For example, if a feature or property is113* required for the operation of the component, the114* component manager may throw a115* SAXNotRecognizedException or a116* SAXNotSupportedException.117*/118public void reset(XMLComponentManager componentManager)119throws XMLConfigurationException {120121122// xerces features123124fWarnDuplicateEntityDef = componentManager.getFeature(WARN_ON_DUPLICATE_ENTITYDEF, false);125126fErrorReporter = (XMLErrorReporter)componentManager.getProperty(ERROR_REPORTER);127128fEntities.clear();129fCurrentEntity = null;130131} // reset(XMLComponentManager)132133/**134* Returns entity declaration.135*136* @param name The name of the entity.137*138* @see SymbolTable139*/140public Entity getEntity(String name) {141return fEntities.get(name);142} // getEntity(String)143144public boolean hasEntities() {145return (fEntities!=null);146} // getEntity(String)147148public int getEntitySize() {149return fEntities.size();150} // getEntity(String)151152public Enumeration getEntityKeys() {153return Collections.enumeration(fEntities.keySet());154}155/**156* Adds an internal entity declaration.157* <p>158* <strong>Note:</strong> This method ignores subsequent entity159* declarations.160* <p>161* <strong>Note:</strong> The name should be a unique symbol. The162* SymbolTable can be used for this purpose.163*164* @param name The name of the entity.165* @param text The text of the entity.166*167* @see SymbolTable168*/169public void addInternalEntity(String name, String text) {170if (!fEntities.containsKey(name)) {171Entity entity = new Entity.InternalEntity(name, text, fInExternalSubset);172fEntities.put(name, entity);173}174else{175if(fWarnDuplicateEntityDef){176fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,177"MSG_DUPLICATE_ENTITY_DEFINITION",178new Object[]{ name },179XMLErrorReporter.SEVERITY_WARNING );180}181}182} // addInternalEntity(String,String)183184/**185* Adds an external entity declaration.186* <p>187* <strong>Note:</strong> This method ignores subsequent entity188* declarations.189* <p>190* <strong>Note:</strong> The name should be a unique symbol. The191* SymbolTable can be used for this purpose.192*193* @param name The name of the entity.194* @param publicId The public identifier of the entity.195* @param literalSystemId The system identifier of the entity.196* @param baseSystemId The base system identifier of the entity.197* This is the system identifier of the entity198* where <em>the entity being added</em> and199* is used to expand the system identifier when200* the system identifier is a relative URI.201* When null the system identifier of the first202* external entity on the stack is used instead.203*204* @see SymbolTable205*/206public void addExternalEntity(String name,207String publicId, String literalSystemId,208String baseSystemId) {209if (!fEntities.containsKey(name)) {210if (baseSystemId == null) {211// search for the first external entity on the stack212//xxx commenting the 'size' variable..213/**214* int size = fEntityStack.size();215* if (size == 0 && fCurrentEntity != null && fCurrentEntity.entityLocation != null) {216* baseSystemId = fCurrentEntity.entityLocation.getExpandedSystemId();217* }218*/219220//xxx we need to have information about the current entity.221if (fCurrentEntity != null && fCurrentEntity.entityLocation != null) {222baseSystemId = fCurrentEntity.entityLocation.getExpandedSystemId();223}224/**225* for (int i = size - 1; i >= 0 ; i--) {226* ScannedEntity externalEntity =227* (ScannedEntity)fEntityStack.elementAt(i);228* if (externalEntity.entityLocation != null && externalEntity.entityLocation.getExpandedSystemId() != null) {229* baseSystemId = externalEntity.entityLocation.getExpandedSystemId();230* break;231* }232* }233*/234}235236fCurrentEntity = fEntityManager.getCurrentEntity();237Entity entity = new Entity.ExternalEntity(name,238new XMLResourceIdentifierImpl(publicId, literalSystemId,239baseSystemId, expandSystemId(literalSystemId, baseSystemId)),240null, fInExternalSubset);241//TODO :: Forced to pass true above remove it.242//(fCurrentEntity == null) ? fasle : fCurrentEntity.isEntityDeclInExternalSubset());243// null, fCurrentEntity.isEntityDeclInExternalSubset());244fEntities.put(name, entity);245}246else{247if(fWarnDuplicateEntityDef){248fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,249"MSG_DUPLICATE_ENTITY_DEFINITION",250new Object[]{ name },251XMLErrorReporter.SEVERITY_WARNING );252}253}254255} // addExternalEntity(String,String,String,String)256257/**258* Checks whether an entity given by name is external.259*260* @param entityName The name of the entity to check.261* @returns True if the entity is external, false otherwise262* (including when the entity is not declared).263*/264public boolean isExternalEntity(String entityName) {265266Entity entity = fEntities.get(entityName);267if (entity == null) {268return false;269}270return entity.isExternal();271}272273/**274* Checks whether the declaration of an entity given by name is275* // in the external subset.276*277* @param entityName The name of the entity to check.278* @returns True if the entity was declared in the external subset, false otherwise279* (including when the entity is not declared).280*/281public boolean isEntityDeclInExternalSubset(String entityName) {282283Entity entity = fEntities.get(entityName);284if (entity == null) {285return false;286}287return entity.isEntityDeclInExternalSubset();288}289290/**291* Adds an unparsed entity declaration.292* <p>293* <strong>Note:</strong> This method ignores subsequent entity294* declarations.295* <p>296* <strong>Note:</strong> The name should be a unique symbol. The297* SymbolTable can be used for this purpose.298*299* @param name The name of the entity.300* @param publicId The public identifier of the entity.301* @param systemId The system identifier of the entity.302* @param notation The name of the notation.303*304* @see SymbolTable305*/306public void addUnparsedEntity(String name,307String publicId, String systemId,308String baseSystemId, String notation) {309310fCurrentEntity = fEntityManager.getCurrentEntity();311if (!fEntities.containsKey(name)) {312Entity entity = new Entity.ExternalEntity(name, new XMLResourceIdentifierImpl(publicId, systemId, baseSystemId, null), notation, fInExternalSubset);313// (fCurrentEntity == null) ? fasle : fCurrentEntity.isEntityDeclInExternalSubset());314// fCurrentEntity.isEntityDeclInExternalSubset());315fEntities.put(name, entity);316}317else{318if(fWarnDuplicateEntityDef){319fErrorReporter.reportError(XMLMessageFormatter.XML_DOMAIN,320"MSG_DUPLICATE_ENTITY_DEFINITION",321new Object[]{ name },322XMLErrorReporter.SEVERITY_WARNING );323}324}325} // addUnparsedEntity(String,String,String,String)326327/**328* Checks whether an entity given by name is unparsed.329*330* @param entityName The name of the entity to check.331* @returns True if the entity is unparsed, false otherwise332* (including when the entity is not declared).333*/334public boolean isUnparsedEntity(String entityName) {335336Entity entity = fEntities.get(entityName);337if (entity == null) {338return false;339}340return entity.isUnparsed();341}342343/**344* Checks whether an entity given by name is declared.345*346* @param entityName The name of the entity to check.347* @returns True if the entity is declared, false otherwise.348*/349public boolean isDeclaredEntity(String entityName) {350351Entity entity = fEntities.get(entityName);352return entity != null;353}354/**355* Expands a system id and returns the system id as a URI, if356* it can be expanded. A return value of null means that the357* identifier is already expanded. An exception thrown358* indicates a failure to expand the id.359*360* @param systemId The systemId to be expanded.361*362* @return Returns the URI string representing the expanded system363* identifier. A null value indicates that the given364* system identifier is already expanded.365*366*/367public static String expandSystemId(String systemId) {368return expandSystemId(systemId, null);369} // expandSystemId(String):String370371// current value of the "user.dir" property372private static String gUserDir;373// escaped value of the current "user.dir" property374private static String gEscapedUserDir;375// which ASCII characters need to be escaped376private static boolean gNeedEscaping[] = new boolean[128];377// the first hex character if a character needs to be escaped378private static char gAfterEscaping1[] = new char[128];379// the second hex character if a character needs to be escaped380private static char gAfterEscaping2[] = new char[128];381private static char[] gHexChs = {'0', '1', '2', '3', '4', '5', '6', '7',382'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};383// initialize the above 3 arrays384static {385for (int i = 0; i <= 0x1f; i++) {386gNeedEscaping[i] = true;387gAfterEscaping1[i] = gHexChs[i >> 4];388gAfterEscaping2[i] = gHexChs[i & 0xf];389}390gNeedEscaping[0x7f] = true;391gAfterEscaping1[0x7f] = '7';392gAfterEscaping2[0x7f] = 'F';393char[] escChs = {' ', '<', '>', '#', '%', '"', '{', '}',394'|', '\\', '^', '~', '[', ']', '`'};395int len = escChs.length;396char ch;397for (int i = 0; i < len; i++) {398ch = escChs[i];399gNeedEscaping[ch] = true;400gAfterEscaping1[ch] = gHexChs[ch >> 4];401gAfterEscaping2[ch] = gHexChs[ch & 0xf];402}403}404// To escape the "user.dir" system property, by using %HH to represent405// special ASCII characters: 0x00~0x1F, 0x7F, ' ', '<', '>', '#', '%'406// and '"'. It's a static method, so needs to be synchronized.407// this method looks heavy, but since the system property isn't expected408// to change often, so in most cases, we only need to return the string409// that was escaped before.410// According to the URI spec, non-ASCII characters (whose value >= 128)411// need to be escaped too.412// REVISIT: don't know how to escape non-ASCII characters, especially413// which encoding to use. Leave them for now.414private static synchronized String getUserDir() {415// get the user.dir property416String userDir = "";417try {418userDir = SecuritySupport.getSystemProperty("user.dir");419}420catch (SecurityException se) {421}422423// return empty string if property value is empty string.424if (userDir.length() == 0)425return "";426427// compute the new escaped value if the new property value doesn't428// match the previous one429if (userDir.equals(gUserDir)) {430return gEscapedUserDir;431}432433// record the new value as the global property value434gUserDir = userDir;435436char separator = java.io.File.separatorChar;437userDir = userDir.replace(separator, '/');438439int len = userDir.length(), ch;440StringBuffer buffer = new StringBuffer(len*3);441// change C:/blah to /C:/blah442if (len >= 2 && userDir.charAt(1) == ':') {443ch = Character.toUpperCase(userDir.charAt(0));444if (ch >= 'A' && ch <= 'Z') {445buffer.append('/');446}447}448449// for each character in the path450int i = 0;451for (; i < len; i++) {452ch = userDir.charAt(i);453// if it's not an ASCII character, break here, and use UTF-8 encoding454if (ch >= 128)455break;456if (gNeedEscaping[ch]) {457buffer.append('%');458buffer.append(gAfterEscaping1[ch]);459buffer.append(gAfterEscaping2[ch]);460// record the fact that it's escaped461}462else {463buffer.append((char)ch);464}465}466467// we saw some non-ascii character468if (i < len) {469// get UTF-8 bytes for the remaining sub-string470byte[] bytes = null;471byte b;472try {473bytes = userDir.substring(i).getBytes("UTF-8");474} catch (java.io.UnsupportedEncodingException e) {475// should never happen476return userDir;477}478len = bytes.length;479480// for each byte481for (i = 0; i < len; i++) {482b = bytes[i];483// for non-ascii character: make it positive, then escape484if (b < 0) {485ch = b + 256;486buffer.append('%');487buffer.append(gHexChs[ch >> 4]);488buffer.append(gHexChs[ch & 0xf]);489}490else if (gNeedEscaping[b]) {491buffer.append('%');492buffer.append(gAfterEscaping1[b]);493buffer.append(gAfterEscaping2[b]);494}495else {496buffer.append((char)b);497}498}499}500501// change blah/blah to blah/blah/502if (!userDir.endsWith("/"))503buffer.append('/');504505gEscapedUserDir = buffer.toString();506507return gEscapedUserDir;508}509510/**511* Expands a system id and returns the system id as a URI, if512* it can be expanded. A return value of null means that the513* identifier is already expanded. An exception thrown514* indicates a failure to expand the id.515*516* @param systemId The systemId to be expanded.517*518* @return Returns the URI string representing the expanded system519* identifier. A null value indicates that the given520* system identifier is already expanded.521*522*/523public static String expandSystemId(String systemId, String baseSystemId) {524525// check for bad parameters id526if (systemId == null || systemId.length() == 0) {527return systemId;528}529// if id already expanded, return530try {531new URI(systemId);532return systemId;533} catch (URI.MalformedURIException e) {534// continue on...535}536// normalize id537String id = fixURI(systemId);538539// normalize base540URI base = null;541URI uri = null;542try {543if (baseSystemId == null || baseSystemId.length() == 0 ||544baseSystemId.equals(systemId)) {545String dir = getUserDir();546base = new URI("file", "", dir, null, null);547}548else {549try {550base = new URI(fixURI(baseSystemId));551}552catch (URI.MalformedURIException e) {553if (baseSystemId.indexOf(':') != -1) {554// for xml schemas we might have baseURI with555// a specified drive556base = new URI("file", "", fixURI(baseSystemId), null, null);557}558else {559String dir = getUserDir();560dir = dir + fixURI(baseSystemId);561base = new URI("file", "", dir, null, null);562}563}564}565// expand id566uri = new URI(base, id);567}568catch (Exception e) {569// let it go through570571}572573if (uri == null) {574return systemId;575}576return uri.toString();577578} // expandSystemId(String,String):String579//580// Protected static methods581//582583/**584* Fixes a platform dependent filename to standard URI form.585*586* @param str The string to fix.587*588* @return Returns the fixed URI string.589*/590protected static String fixURI(String str) {591592// handle platform dependent strings593str = str.replace(java.io.File.separatorChar, '/');594595// Windows fix596if (str.length() >= 2) {597char ch1 = str.charAt(1);598// change "C:blah" to "/C:blah"599if (ch1 == ':') {600char ch0 = Character.toUpperCase(str.charAt(0));601if (ch0 >= 'A' && ch0 <= 'Z') {602str = "/" + str;603}604}605// change "//blah" to "file://blah"606else if (ch1 == '/' && str.charAt(0) == '/') {607str = "file:" + str;608}609}610611// done612return str;613614} // fixURI(String):String615616// indicate start of external subset617public void startExternalSubset() {618fInExternalSubset = true;619}620621public void endExternalSubset() {622fInExternalSubset = false;623}624}625626627