blob: b83bf1f83e3dd8a64e122368b9ba72d299f2b631 [file] [log] [blame]
/* 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.impl.schema;
import org.apache.xmlbeans.*;
import org.apache.xmlbeans.impl.common.QNameHelper;
import org.apache.xmlbeans.impl.common.XBeanDebug;
import org.apache.xmlbeans.impl.util.FilerImpl;
import org.apache.xmlbeans.impl.util.HexBin;
import org.apache.xmlbeans.impl.util.LongUTFDataInputStream;
import org.apache.xmlbeans.impl.util.LongUTFDataOutputStream;
import javax.xml.namespace.QName;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class SchemaTypeSystemImpl extends SchemaTypeLoaderBase implements SchemaTypeSystem {
public static final int DATA_BABE = 0xDA7ABABE;
public static final int MAJOR_VERSION = 2; // must match == to be compatible
public static final int MINOR_VERSION = 24; // must be <= to be compatible
public static final int RELEASE_NUMBER = 0; // should be compatible even if < or >
public static final int FILETYPE_SCHEMAINDEX = 1;
public static final int FILETYPE_SCHEMATYPE = 2;
public static final int FILETYPE_SCHEMAELEMENT = 3;
public static final int FILETYPE_SCHEMAATTRIBUTE = 4;
public static final int FILETYPE_SCHEMAPOINTER = 5;
public static final int FILETYPE_SCHEMAMODELGROUP = 6;
public static final int FILETYPE_SCHEMAATTRIBUTEGROUP = 7;
public static final int FILETYPE_SCHEMAIDENTITYCONSTRAINT = 8;
public static final int FLAG_PART_SKIPPABLE = 1;
public static final int FLAG_PART_FIXED = 4;
public static final int FLAG_PART_NILLABLE = 8;
public static final int FLAG_PART_BLOCKEXT = 16;
public static final int FLAG_PART_BLOCKREST = 32;
public static final int FLAG_PART_BLOCKSUBST = 64;
public static final int FLAG_PART_ABSTRACT = 128;
public static final int FLAG_PART_FINALEXT = 256;
public static final int FLAG_PART_FINALREST = 512;
public static final int FLAG_PROP_ISATTR = 1;
public static final int FLAG_PROP_JAVASINGLETON = 2;
public static final int FLAG_PROP_JAVAOPTIONAL = 4;
public static final int FLAG_PROP_JAVAARRAY = 8;
public static final int FIELD_NONE = 0;
public static final int FIELD_GLOBAL = 1;
public static final int FIELD_LOCALATTR = 2;
public static final int FIELD_LOCALELT = 3;
// type flags
static final int FLAG_SIMPLE_TYPE = 0x1;
static final int FLAG_DOCUMENT_TYPE = 0x2;
static final int FLAG_ORDERED = 0x4;
static final int FLAG_BOUNDED = 0x8;
static final int FLAG_FINITE = 0x10;
static final int FLAG_NUMERIC = 0x20;
static final int FLAG_STRINGENUM = 0x40;
static final int FLAG_UNION_OF_LISTS = 0x80;
static final int FLAG_HAS_PATTERN = 0x100;
static final int FLAG_ORDER_SENSITIVE = 0x200;
static final int FLAG_TOTAL_ORDER = 0x400;
static final int FLAG_COMPILED = 0x800;
static final int FLAG_BLOCK_EXT = 0x1000;
static final int FLAG_BLOCK_REST = 0x2000;
static final int FLAG_FINAL_EXT = 0x4000;
static final int FLAG_FINAL_REST = 0x8000;
static final int FLAG_FINAL_UNION = 0x10000;
static final int FLAG_FINAL_LIST = 0x20000;
static final int FLAG_ABSTRACT = 0x40000;
static final int FLAG_ATTRIBUTE_TYPE = 0x80000;
/**
* regex to identify the type system holder package namespace
*/
private static final Pattern packPat = Pattern.compile("^(.+)(\\.[^.]+){2}$");
/**
* This is to support the feature of a separate/private XMLBeans
* distribution that will not colide with the public org apache
* xmlbeans one.
* METADATA_PACKAGE_GEN will be "" for the original and something like
* com.mycompany.private.xmlbeans for a private distribution of XMLBeans.
* <p>
* There are two properties:
* METADATA_PACKAGE_GEN - used for generating metadata
* and METADATA_PACKAGE_LOAD - used for loading the metadata.
* Most of the time they have the same value, with one exception, during the
* repackage process scomp needs to load from old package and generate into
* a new package.
*/
public static String METADATA_PACKAGE_GEN = "org/apache/xmlbeans/metadata";
private static final SchemaType[] EMPTY_ST_ARRAY = new SchemaType[0];
private static final SchemaGlobalElement[] EMPTY_GE_ARRAY = new SchemaGlobalElement[0];
private static final SchemaGlobalAttribute[] EMPTY_GA_ARRAY = new SchemaGlobalAttribute[0];
private static final SchemaModelGroup[] EMPTY_MG_ARRAY = new SchemaModelGroup[0];
private static final SchemaAttributeGroup[] EMPTY_AG_ARRAY = new SchemaAttributeGroup[0];
private static final SchemaIdentityConstraint[] EMPTY_IC_ARRAY = new SchemaIdentityConstraint[0];
private static final SchemaAnnotation[] EMPTY_ANN_ARRAY = new SchemaAnnotation[0];
private final String _name;
// EXPERIMENTAL: recovery from compilation errors and partial type systems
private boolean _incomplete = false;
// classloader is available for sts's that were compiled and loaded, not dynamic ones
private ClassLoader _classloader;
// the loader for loading .xsb resources
private ResourceLoader _resourceLoader;
// the following is used to link references during load
SchemaTypeLoader _linker;
private SchemaTypePool _localHandles;
private Filer _filer;
// top-level annotations
private List<SchemaAnnotation> _annotations;
// container
private Map<String, SchemaContainer> _containers = new HashMap<>();
// dependencies
private SchemaDependencies _deps;
private List<SchemaComponent.Ref> _redefinedModelGroups;
private List<SchemaComponent.Ref> _redefinedAttributeGroups;
private List<SchemaComponent.Ref> _redefinedGlobalTypes;
// actual type system data, map QNames -> SchemaComponent.Ref
private Map<QName, SchemaComponent.Ref> _globalElements;
private Map<QName, SchemaComponent.Ref> _globalAttributes;
private Map<QName, SchemaComponent.Ref> _modelGroups;
private Map<QName, SchemaComponent.Ref> _attributeGroups;
private Map<QName, SchemaComponent.Ref> _globalTypes;
private Map<QName, SchemaComponent.Ref> _documentTypes;
private Map<QName, SchemaComponent.Ref> _attributeTypes;
private Map<QName, SchemaComponent.Ref> _identityConstraints = Collections.emptyMap();
private Map<String, SchemaComponent.Ref> _typeRefsByClassname = new HashMap<>();
private Set<String> _namespaces;
static String nameToPathString(String nameForSystem) {
nameForSystem = nameForSystem.replace('.', '/');
if (!nameForSystem.endsWith("/") && nameForSystem.length() > 0) {
nameForSystem = nameForSystem + "/";
}
return nameForSystem;
}
protected SchemaTypeSystemImpl() {
String fullname = getClass().getName();
_name = fullname.substring(0, fullname.lastIndexOf('.'));
XBeanDebug.LOG.atTrace().log("Loading type system {}", _name);
_classloader = getClass().getClassLoader();
_linker = this;
_resourceLoader = new ClassLoaderResourceLoader(_classloader);
try {
initFromHeader();
} catch (Error | RuntimeException e) {
XBeanDebug.LOG.atDebug().withThrowable(e).log(e.getMessage());
throw e;
}
XBeanDebug.LOG.atTrace().log("Finished loading type system {}", _name);
}
public SchemaTypeSystemImpl(Class<?> indexclass) {
String fullname = indexclass.getName();
_name = fullname.substring(0, fullname.lastIndexOf('.'));
XBeanDebug.LOG.atTrace().log("Loading type system {}", _name);
_classloader = indexclass.getClassLoader();
_linker = SchemaTypeLoaderImpl.build(null, null, _classloader, getMetadataPath());
_resourceLoader = new ClassLoaderResourceLoader(_classloader);
try {
initFromHeader();
} catch (RuntimeException | Error e) {
XBeanDebug.LOG.atDebug().withThrowable(e).log(e.getMessage());
throw e;
}
XBeanDebug.LOG.atTrace().log("Finished loading type system {}", _name);
}
public static SchemaTypeSystemImpl forName(String name, ClassLoader loader) {
try {
Class<?> c = Class.forName(name + "." + SchemaTypeCodePrinter.INDEX_CLASSNAME, true, loader);
return (SchemaTypeSystemImpl) c.getField("typeSystem").get(null);
} catch (Throwable e) {
return null;
}
}
public SchemaTypeSystemImpl(ResourceLoader resourceLoader, String name, SchemaTypeLoader linker) {
_name = name;
_linker = linker;
_resourceLoader = resourceLoader;
try {
initFromHeader();
} catch (RuntimeException | Error e) {
XBeanDebug.LOG.atDebug().withThrowable(e).log(e.getMessage());
throw e;
}
}
private void initFromHeader() {
XBeanDebug.LOG.atTrace().log("Reading unresolved handles for type system {}", _name);
XsbReader reader = null;
try {
// Read the index file, which starts with a header.
reader = new XsbReader(getTypeSystem(), "index", FILETYPE_SCHEMAINDEX);
// has a handle pool (count, handle/type, handle/type...)
_localHandles = new SchemaTypePool(getTypeSystem());
_localHandles.readHandlePool(reader);
// then a qname map of global elements (count, qname/handle, qname/handle...)
_globalElements = reader.readQNameRefMap();
// qname map of global attributes
_globalAttributes = reader.readQNameRefMap();
// qname map of model groups
_modelGroups = reader.readQNameRefMap();
// qname map of attribute groups
_attributeGroups = reader.readQNameRefMap();
_identityConstraints = reader.readQNameRefMap();
// qname map of global types
_globalTypes = reader.readQNameRefMap();
// qname map of document types, by the qname of the contained element
_documentTypes = reader.readQNameRefMap();
// qname mape of attribute types, by the qname of the contained attribute
_attributeTypes = reader.readQNameRefMap();
// string map of all types, by fully qualified classname
_typeRefsByClassname = reader.readClassnameRefMap();
_namespaces = reader.readNamespaces();
// support for redefine, at the end of the file
List<QName> typeNames = new ArrayList<>();
List<QName> modelGroupNames = new ArrayList<>();
List<QName> attributeGroupNames = new ArrayList<>();
if (reader.atLeast(2, 15, 0)) {
_redefinedGlobalTypes = reader.readQNameRefMapAsList(typeNames);
_redefinedModelGroups = reader.readQNameRefMapAsList(modelGroupNames);
_redefinedAttributeGroups = reader.readQNameRefMapAsList(attributeGroupNames);
}
if (reader.atLeast(2, 19, 0)) {
_annotations = reader.readAnnotations();
}
buildContainers(typeNames, modelGroupNames, attributeGroupNames);
} finally {
if (reader != null) {
reader.readEnd();
}
}
}
void saveIndex() {
String handle = "index";
XsbReader saver = new XsbReader(getTypeSystem(), handle);
saver.writeIndexData();
saver.writeRealHeader(handle, FILETYPE_SCHEMAINDEX);
saver.writeIndexData();
saver.writeEnd();
}
void savePointers() {
savePointersForComponents(globalElements(), getMetadataPath() + "/element/");
savePointersForComponents(globalAttributes(), getMetadataPath() + "/attribute/");
savePointersForComponents(modelGroups(), getMetadataPath() + "/modelgroup/");
savePointersForComponents(attributeGroups(), getMetadataPath() + "/attributegroup/");
savePointersForComponents(globalTypes(), getMetadataPath() + "/type/");
savePointersForComponents(identityConstraints(), getMetadataPath() + "/identityconstraint/");
savePointersForNamespaces(_namespaces, getMetadataPath() + "/namespace/");
savePointersForClassnames(_typeRefsByClassname.keySet(), getMetadataPath() + "/javaname/");
savePointersForComponents(redefinedModelGroups(), getMetadataPath() + "/redefinedmodelgroup/");
savePointersForComponents(redefinedAttributeGroups(), getMetadataPath() + "/redefinedattributegroup/");
savePointersForComponents(redefinedGlobalTypes(), getMetadataPath() + "/redefinedtype/");
}
void savePointersForComponents(SchemaComponent[] components, String dir) {
for (SchemaComponent component : components) {
savePointerFile(dir + QNameHelper.hexsafedir(component.getName()), _name);
}
}
void savePointersForClassnames(Set<String> classnames, String dir) {
for (String classname : classnames) {
savePointerFile(dir + classname.replace('.', '/'), _name);
}
}
void savePointersForNamespaces(Set<String> namespaces, String dir) {
for (String ns : namespaces) {
savePointerFile(dir + QNameHelper.hexsafedir(new QName(ns, "xmlns")), _name);
}
}
void savePointerFile(String filename, String name) {
XsbReader saver = new XsbReader(getTypeSystem(), filename);
saver.writeString(name);
saver.writeRealHeader(filename, FILETYPE_SCHEMAPOINTER);
saver.writeString(name);
saver.writeEnd();
}
private Map<String, SchemaComponent.Ref> buildTypeRefsByClassname(Map<String, SchemaType> typesByClassname) {
Map<String, SchemaComponent.Ref> result = new LinkedHashMap<>();
for (String className : typesByClassname.keySet()) {
result.put(className, typesByClassname.get(className).getRef());
}
return result;
}
private static Map<QName, SchemaComponent.Ref> buildComponentRefMap(SchemaComponent[] components) {
return buildComponentRefMap(Arrays.asList(components));
}
private static Map<QName, SchemaComponent.Ref> buildComponentRefMap(List<? extends SchemaComponent> components) {
return components.stream().collect(Collectors.toMap(SchemaComponent::getName, SchemaComponent::getComponentRef,
(u, v) -> v, LinkedHashMap::new));
}
private static List<SchemaComponent.Ref> buildComponentRefList(SchemaComponent[] components) {
return buildComponentRefList(Arrays.asList(components));
}
private static List<SchemaComponent.Ref> buildComponentRefList(List<? extends SchemaComponent> components) {
return components.stream().map(SchemaComponent::getComponentRef).collect(Collectors.toList());
}
private static Map<QName, SchemaComponent.Ref> buildDocumentMap(SchemaType[] types) {
return buildDocumentMap(Arrays.asList(types));
}
private static Map<QName, SchemaComponent.Ref> buildDocumentMap(List<? extends SchemaComponent> types) {
Map<QName, SchemaComponent.Ref> result = new LinkedHashMap<>();
for (SchemaComponent comp : types) {
SchemaType type = (SchemaType) comp;
result.put(type.getDocumentElementName(), type.getRef());
}
return result;
}
private static Map<QName, SchemaComponent.Ref> buildAttributeTypeMap(SchemaType[] types) {
Map<QName, SchemaComponent.Ref> result = new LinkedHashMap<>();
for (SchemaType type : types) {
result.put(type.getAttributeTypeAttributeName(), type.getRef());
}
return result;
}
private static Map<QName, SchemaComponent.Ref> buildAttributeTypeMap(List<? extends SchemaComponent> types) {
Map<QName, SchemaComponent.Ref> result = new LinkedHashMap<>();
for (SchemaComponent comp : types) {
SchemaType type = (SchemaType) comp;
result.put(type.getAttributeTypeAttributeName(), type.getRef());
}
return result;
}
// Container operation
SchemaContainer getContainer(String namespace) {
return _containers.get(namespace);
}
private void addContainer(String namespace) {
SchemaContainer c = new SchemaContainer(namespace);
c.setTypeSystem(this);
_containers.put(namespace, c);
}
SchemaContainer getContainerNonNull(String namespace) {
SchemaContainer result = getContainer(namespace);
if (result == null) {
addContainer(namespace);
result = getContainer(namespace);
}
return result;
}
@SuppressWarnings("unchecked")
private <T extends SchemaComponent.Ref> void buildContainersHelper(Map<QName, SchemaComponent.Ref> elements, BiConsumer<SchemaContainer, T> adder) {
elements.forEach((k, v) -> adder.accept(getContainerNonNull(k.getNamespaceURI()), (T) v));
}
@SuppressWarnings("unchecked")
private <T extends SchemaComponent.Ref> void buildContainersHelper(List<SchemaComponent.Ref> refs, List<QName> names, BiConsumer<SchemaContainer, T> adder) {
Iterator<SchemaComponent.Ref> it = refs.iterator();
Iterator<QName> itname = names.iterator();
while (it.hasNext()) {
String ns = itname.next().getNamespaceURI();
SchemaContainer sc = getContainerNonNull(ns);
adder.accept(sc, (T) it.next());
}
}
// Only called during init
private void buildContainers(List<QName> redefTypeNames, List<QName> redefModelGroupNames, List<QName> redefAttributeGroupNames) {
// This method walks the reference maps and copies said references
// into the appropriate container
buildContainersHelper(_globalElements, SchemaContainer::addGlobalElement);
buildContainersHelper(_globalAttributes, SchemaContainer::addGlobalAttribute);
buildContainersHelper(_modelGroups, SchemaContainer::addModelGroup);
buildContainersHelper(_attributeGroups, SchemaContainer::addAttributeGroup);
buildContainersHelper(_identityConstraints, SchemaContainer::addIdentityConstraint);
buildContainersHelper(_globalTypes, SchemaContainer::addGlobalType);
buildContainersHelper(_attributeTypes, SchemaContainer::addAttributeType);
// Some earlier .xsb versions don't have records for redefinitions
if (_redefinedGlobalTypes != null && _redefinedModelGroups != null &&
_redefinedAttributeGroups != null) {
assert _redefinedGlobalTypes.size() == redefTypeNames.size();
buildContainersHelper(_redefinedGlobalTypes, redefTypeNames, SchemaContainer::addRedefinedType);
buildContainersHelper(_redefinedModelGroups, redefModelGroupNames, SchemaContainer::addRedefinedModelGroup);
buildContainersHelper(_redefinedAttributeGroups, redefAttributeGroupNames, SchemaContainer::addRedefinedAttributeGroup);
}
// Some earlier .xsb versions don't have records for annotations
if (_annotations != null && !_annotations.isEmpty()) {
// BUGBUG(radup)
_annotations.forEach(getContainerNonNull("")::addAnnotation);
}
_containers.values().forEach(SchemaContainer::setImmutable);
}
/**
* This is the crux of the container work and role.
* It makes a sweep over all containers and fixes each container's
* typesystem to point to this typesystem.
* Because SchemaComponents have a link to their containers, this has as
* effect all components now indirectly pointing to this typesystem
* even though they (as well as the typesystem itself) are immutable.
*/
private void fixupContainers() {
for (SchemaContainer container : _containers.values()) {
container.setTypeSystem(this);
container.setImmutable();
}
}
private void assertContainersHelper(Map<QName, SchemaComponent.Ref> comp, Function<SchemaContainer, List<? extends SchemaComponent>> fun, Function<List<? extends SchemaComponent>, ? extends Map<QName, SchemaComponent.Ref>> fun2) {
final Map<QName, SchemaComponent.Ref> temp = _containers.values().stream()
.map(fun).map(fun2 == null ? SchemaTypeSystemImpl::buildComponentRefMap : fun2)
.map(Map::entrySet).flatMap(Set::stream)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
assert comp.equals(temp);
}
private void assertContainersHelper(List<? extends SchemaComponent.Ref> comp, Function<SchemaContainer, List<? extends SchemaComponent>> fun) {
final Set<SchemaComponent.Ref> temp = _containers.values().stream()
.map(fun).map(SchemaTypeSystemImpl::buildComponentRefList)
.flatMap(List::stream).collect(Collectors.toSet());
assert new HashSet<>(comp).equals(temp);
}
@SuppressWarnings({"AssertWithSideEffects", "ConstantConditions"})
private void assertContainersSynchronized() {
boolean assertEnabled = false;
// This code basically checks whether asserts are enabled so we don't do
// all the work if they arent
assert assertEnabled = true;
if (!assertEnabled) {
return;
}
assertContainersHelper(_globalElements, SchemaContainer::globalElements, null);
assertContainersHelper(_globalAttributes, SchemaContainer::globalAttributes, null);
assertContainersHelper(_modelGroups, SchemaContainer::modelGroups, null);
assertContainersHelper(_modelGroups, SchemaContainer::modelGroups, null);
assertContainersHelper(_redefinedModelGroups, SchemaContainer::redefinedModelGroups);
assertContainersHelper(_attributeGroups, SchemaContainer::attributeGroups, null);
assertContainersHelper(_redefinedAttributeGroups, SchemaContainer::redefinedAttributeGroups);
assertContainersHelper(_globalTypes, SchemaContainer::globalTypes, null);
assertContainersHelper(_redefinedGlobalTypes, SchemaContainer::redefinedGlobalTypes);
assertContainersHelper(_documentTypes, SchemaContainer::documentTypes, SchemaTypeSystemImpl::buildDocumentMap);
assertContainersHelper(_attributeTypes, SchemaContainer::attributeTypes, SchemaTypeSystemImpl::buildAttributeTypeMap);
assertContainersHelper(_identityConstraints, SchemaContainer::identityConstraints, null);
// annotations
Set<SchemaAnnotation> temp3 = _containers.values().stream()
.map(SchemaContainer::annotations).flatMap(List::stream).collect(Collectors.toSet());
assert new HashSet<>(_annotations).equals(temp3);
// namespaces
Set<String> temp4 = _containers.values().stream()
.map(SchemaContainer::getNamespace).collect(Collectors.toSet());
assert _namespaces.equals(temp4);
}
private static Random _random;
private static final byte[] _mask = new byte[128 / 8];
/**
* Fun, fun. Produce 128 bits of uniqueness randomly.
* We used to use SecureRandom, but now we don't because SecureRandom
* hits the filesystem and hangs us on a filesystem lock. It also eats
* a thread and other expensive resources.. :-).
* <p>
* We don't really care that non-secure Random() can only do 48 bits of
* randomness, since we're certainly not going to be called more than 2^48
* times within our process lifetime.
* <p>
* Our real concern is that by seeding Random() with the current
* time, two users will end up with the same bits if they start a
* schema compilation within the same millisecond. That makes the
* probability of collision in the real world slightly too high.
* We're going to have millions of users, remember? With a million
* users, and one-compilation-per-day each, we'd see a collision every
* few months.
* <p>
* So we'll just xor the results of random with our few extra
* bits of information computed below to help reduce the probability
* of collision by a few decimal places. To collide, you will have had
* to have the same amount of free memory, the same user name, timezone,
* and country, the same current directory, the same java classpath,
* the same operating system and jvm version, and the same choices of
* identity hashcodes for a few objects. And be started within the same
* millisecond. Or you can collide if you have a cosmic 128-bit mathematical
* coincidence. No worries.
*/
private static synchronized void nextBytes(byte[] result) {
if (_random == null) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
LongUTFDataOutputStream daos = new LongUTFDataOutputStream(baos);
// at least 10 bits of unqieueness, right? Maybe even 50 or 60.
daos.writeInt(System.identityHashCode(SchemaTypeSystemImpl.class));
String[] props = new String[]{"user.name", "user.dir", "user.timezone", "user.country", "java.class.path", "java.home", "java.vendor", "java.version", "os.version"};
for (String s : props) {
String prop = SystemProperties.getProperty(s);
if (prop != null) {
daos.writeUTF(prop);
daos.writeInt(System.identityHashCode(prop));
}
}
daos.writeLong(Runtime.getRuntime().freeMemory());
daos.close();
byte[] bytes = baos.toByteArray();
for (int i = 0; i < bytes.length; i++) {
int j = i % _mask.length;
_mask[j] *= 21;
_mask[j] += i;
}
} catch (IOException e) {
XBeanDebug.LOG.atDebug().withThrowable(e).log(e.getMessage());
}
_random = new Random(System.currentTimeMillis());
}
_random.nextBytes(result);
for (int i = 0; i < result.length; i++) {
int j = i & _mask.length;
result[i] ^= _mask[j];
}
}
public SchemaTypeSystemImpl(String nameForSystem) {
// if we have no name, select a random one
if (nameForSystem == null) {
// get 128 random bits (that'll be 32 hex digits)
byte[] bytes = new byte[128 / 8];
nextBytes(bytes);
nameForSystem = "s" + new String(HexBin.encode(bytes), StandardCharsets.ISO_8859_1);
}
_name = SchemaTypeSystemImpl.METADATA_PACKAGE_GEN.replace('/', '.') + ".system." + nameForSystem;
_classloader = null;
}
public void loadFromStscState(StscState state) {
assert (_classloader == null);
_localHandles = new SchemaTypePool(getTypeSystem());
_globalElements = buildComponentRefMap(state.globalElements());
_globalAttributes = buildComponentRefMap(state.globalAttributes());
_modelGroups = buildComponentRefMap(state.modelGroups());
_redefinedModelGroups = buildComponentRefList(state.redefinedModelGroups());
_attributeGroups = buildComponentRefMap(state.attributeGroups());
_redefinedAttributeGroups = buildComponentRefList(state.redefinedAttributeGroups());
_globalTypes = buildComponentRefMap(state.globalTypes());
_redefinedGlobalTypes = buildComponentRefList(state.redefinedGlobalTypes());
_documentTypes = buildDocumentMap(state.documentTypes());
_attributeTypes = buildAttributeTypeMap(state.attributeTypes());
_typeRefsByClassname = buildTypeRefsByClassname(state.typesByClassname());
_identityConstraints = buildComponentRefMap(state.idConstraints());
_annotations = state.annotations();
_namespaces = new HashSet<>(Arrays.asList(state.getNamespaces()));
_containers = state.getContainerMap();
fixupContainers();
// Checks that data in the containers matches the lookup maps
assertContainersSynchronized();
setDependencies(state.getDependencies());
}
final SchemaTypeSystemImpl getTypeSystem() {
return this;
}
void setDependencies(SchemaDependencies deps) {
_deps = deps;
}
SchemaDependencies getDependencies() {
return _deps;
}
// EXPERIMENTAL
public boolean isIncomplete() {
return _incomplete;
}
// EXPERIMENTAL
void setIncomplete(boolean incomplete) {
_incomplete = incomplete;
}
static class StringPool {
private final List<String> intsToStrings = new ArrayList<>();
private final Map<String, Integer> stringsToInts = new HashMap<>();
private final String _handle;
private final String _name;
/**
* Constructs an empty StringPool to be filled with strings.
*/
StringPool(String handle, String name) {
_handle = handle;
_name = name;
intsToStrings.add(null);
}
int codeForString(String str) {
if (str == null) {
return 0;
}
Integer result = stringsToInts.get(str);
if (result == null) {
result = intsToStrings.size();
intsToStrings.add(str);
stringsToInts.put(str, result);
}
return result;
}
String stringForCode(int code) {
return code == 0 ? null : intsToStrings.get(code);
}
void writeTo(LongUTFDataOutputStream output) {
try {
int cnt = intsToStrings.size();
output.writeShortOrInt(cnt);
boolean isNext = false;
for (String str : intsToStrings) {
if (isNext) {
output.writeLongUTF(str);
}
isNext = true;
}
} catch (IOException e) {
throw new SchemaTypeLoaderException(e.getMessage(), _name, _handle, SchemaTypeLoaderException.IO_EXCEPTION, e);
}
}
void readFrom(LongUTFDataInputStream input) {
if (intsToStrings.size() != 1 || stringsToInts.size() != 0) {
throw new IllegalStateException();
}
try {
int size = input.readUnsignedShortOrInt();
for (int i = 1; i < size; i++) {
String str = input.readLongUTF().intern();
int code = codeForString(str);
if (code != i) {
throw new IllegalStateException();
}
}
} catch (IOException e) {
throw new SchemaTypeLoaderException(e.getMessage() == null ? e.getMessage() : "IO Exception", _name, _handle, SchemaTypeLoaderException.IO_EXCEPTION, e);
}
}
}
public void saveToDirectory(File classDir) {
save(new FilerImpl(classDir, null, null, false, false));
}
public void save(Filer filer) {
if (_incomplete) {
throw new IllegalStateException("Incomplete SchemaTypeSystems cannot be saved.");
}
if (filer == null) {
throw new IllegalArgumentException("filer must not be null");
}
_filer = filer;
_localHandles.startWriteMode();
saveTypesRecursively(globalTypes());
saveTypesRecursively(documentTypes());
saveTypesRecursively(attributeTypes());
saveGlobalElements(globalElements());
saveGlobalAttributes(globalAttributes());
saveModelGroups(modelGroups());
saveAttributeGroups(attributeGroups());
saveIdentityConstraints(identityConstraints());
saveTypesRecursively(redefinedGlobalTypes());
saveModelGroups(redefinedModelGroups());
saveAttributeGroups(redefinedAttributeGroups());
saveIndex();
savePointers();
}
void saveTypesRecursively(SchemaType[] types) {
for (SchemaType type : types) {
if (type.getTypeSystem() != getTypeSystem()) {
continue;
}
saveType(type);
saveTypesRecursively(type.getAnonymousTypes());
}
}
public void saveGlobalElements(SchemaGlobalElement[] elts) {
if (_incomplete) {
throw new IllegalStateException("This SchemaTypeSystem cannot be saved.");
}
for (SchemaGlobalElement elt : elts) {
saveGlobalElement(elt);
}
}
public void saveGlobalAttributes(SchemaGlobalAttribute[] attrs) {
if (_incomplete) {
throw new IllegalStateException("This SchemaTypeSystem cannot be saved.");
}
for (SchemaGlobalAttribute attr : attrs) {
saveGlobalAttribute(attr);
}
}
public void saveModelGroups(SchemaModelGroup[] groups) {
if (_incomplete) {
throw new IllegalStateException("This SchemaTypeSystem cannot be saved.");
}
for (SchemaModelGroup group : groups) {
saveModelGroup(group);
}
}
public void saveAttributeGroups(SchemaAttributeGroup[] groups) {
if (_incomplete) {
throw new IllegalStateException("This SchemaTypeSystem cannot be saved.");
}
for (SchemaAttributeGroup group : groups) {
saveAttributeGroup(group);
}
}
public void saveIdentityConstraints(SchemaIdentityConstraint[] idcs) {
if (_incomplete) {
throw new IllegalStateException("This SchemaTypeSystem cannot be saved.");
}
for (SchemaIdentityConstraint idc : idcs) {
saveIdentityConstraint(idc);
}
}
public void saveGlobalElement(SchemaGlobalElement elt) {
if (_incomplete) {
throw new IllegalStateException("This SchemaTypeSystem cannot be saved.");
}
String handle = _localHandles.handleForElement(elt);
XsbReader saver = new XsbReader(getTypeSystem(), handle);
saver.writeParticleData((SchemaParticle) elt);
saver.writeString(elt.getSourceName());
saver.writeRealHeader(handle, FILETYPE_SCHEMAELEMENT);
saver.writeParticleData((SchemaParticle) elt);
saver.writeString(elt.getSourceName());
saver.writeEnd();
}
public void saveGlobalAttribute(SchemaGlobalAttribute attr) {
if (_incomplete) {
throw new IllegalStateException("This SchemaTypeSystem cannot be saved.");
}
String handle = _localHandles.handleForAttribute(attr);
XsbReader saver = new XsbReader(getTypeSystem(), handle);
saver.writeAttributeData(attr);
saver.writeString(attr.getSourceName());
saver.writeRealHeader(handle, FILETYPE_SCHEMAATTRIBUTE);
saver.writeAttributeData(attr);
saver.writeString(attr.getSourceName());
saver.writeEnd();
}
public void saveModelGroup(SchemaModelGroup grp) {
if (_incomplete) {
throw new IllegalStateException("This SchemaTypeSystem cannot be saved.");
}
String handle = _localHandles.handleForModelGroup(grp);
XsbReader saver = new XsbReader(getTypeSystem(), handle);
saver.writeModelGroupData(grp);
saver.writeRealHeader(handle, FILETYPE_SCHEMAMODELGROUP);
saver.writeModelGroupData(grp);
saver.writeEnd();
}
public void saveAttributeGroup(SchemaAttributeGroup grp) {
if (_incomplete) {
throw new IllegalStateException("This SchemaTypeSystem cannot be saved.");
}
String handle = _localHandles.handleForAttributeGroup(grp);
XsbReader saver = new XsbReader(getTypeSystem(), handle);
saver.writeAttributeGroupData(grp);
saver.writeRealHeader(handle, FILETYPE_SCHEMAATTRIBUTEGROUP);
saver.writeAttributeGroupData(grp);
saver.writeEnd();
}
public void saveIdentityConstraint(SchemaIdentityConstraint idc) {
if (_incomplete) {
throw new IllegalStateException("This SchemaTypeSystem cannot be saved.");
}
String handle = _localHandles.handleForIdentityConstraint(idc);
XsbReader saver = new XsbReader(getTypeSystem(), handle);
saver.writeIdConstraintData(idc);
saver.writeRealHeader(handle, FILETYPE_SCHEMAIDENTITYCONSTRAINT);
saver.writeIdConstraintData(idc);
saver.writeEnd();
}
void saveType(SchemaType type) {
String handle = _localHandles.handleForType(type);
XsbReader saver = new XsbReader(getTypeSystem(), handle);
saver.writeTypeData(type);
saver.writeRealHeader(handle, FILETYPE_SCHEMATYPE);
saver.writeTypeData(type);
saver.writeEnd();
}
public static String crackPointer(InputStream stream) {
try (LongUTFDataInputStream input = new LongUTFDataInputStream(stream)) {
int magic = input.readInt();
if (magic != DATA_BABE) {
return null;
}
int majorver = input.readShort();
int minorver = input.readShort();
if (majorver != MAJOR_VERSION) {
return null;
}
if (minorver > MINOR_VERSION) {
return null;
}
if (minorver >= 18) {
input.readShort(); // release number present in atLeast(2, 18, 0)
}
int actualfiletype = input.readShort();
if (actualfiletype != FILETYPE_SCHEMAPOINTER) {
return null;
}
StringPool stringPool = new StringPool("pointer", "unk");
stringPool.readFrom(input);
return stringPool.stringForCode(input.readShort());
} catch (IOException e) {
return null;
}
}
static final byte[] SINGLE_ZERO_BYTE = {0};
public SchemaType typeForHandle(String handle) {
synchronized (_resolvedHandles) {
return (SchemaType) _resolvedHandles.get(handle);
}
}
public SchemaType typeForClassname(String classname) {
SchemaType.Ref ref = (SchemaType.Ref) _typeRefsByClassname.get(classname);
return (ref != null) ? ref.get() : null;
}
public SchemaComponent resolveHandle(String handle) {
SchemaComponent result;
synchronized (_resolvedHandles) {
result = _resolvedHandles.get(handle);
}
if (result == null) {
XsbReader reader = new XsbReader(getTypeSystem(), handle, 0xFFFF);
int filetype = reader.getActualFiletype();
switch (filetype) {
case FILETYPE_SCHEMATYPE:
XBeanDebug.LOG.atTrace().log("Resolving type for handle {}", handle);
result = reader.finishLoadingType();
break;
case FILETYPE_SCHEMAELEMENT:
XBeanDebug.LOG.atTrace().log("Resolving element for handle {}", handle);
result = reader.finishLoadingElement();
break;
case FILETYPE_SCHEMAATTRIBUTE:
XBeanDebug.LOG.atTrace().log("Resolving attribute for handle {}", handle);
result = reader.finishLoadingAttribute();
break;
case FILETYPE_SCHEMAMODELGROUP:
XBeanDebug.LOG.atTrace().log("Resolving model group for handle {}", handle);
result = reader.finishLoadingModelGroup();
break;
case FILETYPE_SCHEMAATTRIBUTEGROUP:
XBeanDebug.LOG.atTrace().log("Resolving attribute group for handle {}", handle);
result = reader.finishLoadingAttributeGroup();
break;
case FILETYPE_SCHEMAIDENTITYCONSTRAINT:
XBeanDebug.LOG.atTrace().log("Resolving id constraint for handle {}", handle);
result = reader.finishLoadingIdentityConstraint();
break;
default:
throw new IllegalStateException("Illegal handle type");
}
synchronized (_resolvedHandles) {
if (!_resolvedHandles.containsKey(handle)) {
_resolvedHandles.put(handle, result);
} else {
result = _resolvedHandles.get(handle);
}
}
}
return result;
}
private final Map<String, SchemaComponent> _resolvedHandles = new HashMap<>();
private boolean _allNonGroupHandlesResolved = false;
public void resolve() {
XBeanDebug.LOG.atTrace().log("Resolve called type system {}", _name);
if (_allNonGroupHandlesResolved) {
return;
}
XBeanDebug.LOG.atTrace().log("Resolving all handles for type system {}", _name);
List<SchemaComponent.Ref> refs = new ArrayList<>();
refs.addAll(_globalElements.values());
refs.addAll(_globalAttributes.values());
refs.addAll(_globalTypes.values());
refs.addAll(_documentTypes.values());
refs.addAll(_attributeTypes.values());
refs.addAll(_identityConstraints.values());
for (SchemaComponent.Ref ref : refs) {
// Forces ref to be resolved
ref.getComponent();
}
XBeanDebug.LOG.atTrace().log("Finished resolving type system {}", _name);
_allNonGroupHandlesResolved = true;
}
public boolean isNamespaceDefined(String namespace) {
return _namespaces.contains(namespace);
}
public SchemaType.Ref findTypeRef(QName name) {
return (SchemaType.Ref) _globalTypes.get(name);
}
public SchemaType.Ref findDocumentTypeRef(QName name) {
return (SchemaType.Ref) _documentTypes.get(name);
}
public SchemaType.Ref findAttributeTypeRef(QName name) {
return (SchemaType.Ref) _attributeTypes.get(name);
}
public SchemaGlobalElement.Ref findElementRef(QName name) {
return (SchemaGlobalElement.Ref) _globalElements.get(name);
}
public SchemaGlobalAttribute.Ref findAttributeRef(QName name) {
return (SchemaGlobalAttribute.Ref) _globalAttributes.get(name);
}
public SchemaModelGroup.Ref findModelGroupRef(QName name) {
return (SchemaModelGroup.Ref) _modelGroups.get(name);
}
public SchemaAttributeGroup.Ref findAttributeGroupRef(QName name) {
return (SchemaAttributeGroup.Ref) _attributeGroups.get(name);
}
public SchemaIdentityConstraint.Ref findIdentityConstraintRef(QName name) {
return (SchemaIdentityConstraint.Ref) _identityConstraints.get(name);
}
private static <T, U> U[] refHelper(Map<QName, SchemaComponent.Ref> map, Function<T, U> fun, IntFunction<U[]> target, U[] emptyTarget) {
return refHelper(map == null ? null : map.values(), fun, target, emptyTarget);
}
private static <T, U> U[] refHelper(Collection<SchemaComponent.Ref> list, Function<T, U> fun, IntFunction<U[]> target, U[] emptyTarget) {
//noinspection unchecked
return (list == null || list.isEmpty()) ? emptyTarget : list.stream().map(e -> (T) e).map(fun).toArray(target);
}
public SchemaType[] globalTypes() {
return refHelper(_globalTypes, SchemaType.Ref::get, SchemaType[]::new, EMPTY_ST_ARRAY);
}
public SchemaType[] redefinedGlobalTypes() {
return refHelper(_redefinedGlobalTypes, SchemaType.Ref::get, SchemaType[]::new, EMPTY_ST_ARRAY);
}
public InputStream getSourceAsStream(String sourceName) {
if (!sourceName.startsWith("/")) {
sourceName = "/" + sourceName;
}
return _resourceLoader.getResourceAsStream(getMetadataPath() + "/src" + sourceName);
}
SchemaContainer[] containers() {
return _containers.values().toArray(new SchemaContainer[0]);
}
public SchemaType[] documentTypes() {
return refHelper(_documentTypes, SchemaType.Ref::get, SchemaType[]::new, EMPTY_ST_ARRAY);
}
public SchemaType[] attributeTypes() {
return refHelper(_attributeTypes, SchemaType.Ref::get, SchemaType[]::new, EMPTY_ST_ARRAY);
}
public SchemaGlobalElement[] globalElements() {
return refHelper(_globalElements, SchemaGlobalElement.Ref::get, SchemaGlobalElement[]::new, EMPTY_GE_ARRAY);
}
public SchemaGlobalAttribute[] globalAttributes() {
return refHelper(_globalAttributes, SchemaGlobalAttribute.Ref::get, SchemaGlobalAttribute[]::new, EMPTY_GA_ARRAY);
}
public SchemaModelGroup[] modelGroups() {
return refHelper(_modelGroups, SchemaModelGroup.Ref::get, SchemaModelGroup[]::new, EMPTY_MG_ARRAY);
}
public SchemaModelGroup[] redefinedModelGroups() {
return refHelper(_redefinedModelGroups, SchemaModelGroup.Ref::get, SchemaModelGroup[]::new, EMPTY_MG_ARRAY);
}
public SchemaAttributeGroup[] attributeGroups() {
return refHelper(_attributeGroups, SchemaAttributeGroup.Ref::get, SchemaAttributeGroup[]::new, EMPTY_AG_ARRAY);
}
public SchemaAttributeGroup[] redefinedAttributeGroups() {
return refHelper(_redefinedAttributeGroups, SchemaAttributeGroup.Ref::get, SchemaAttributeGroup[]::new, EMPTY_AG_ARRAY);
}
public SchemaAnnotation[] annotations() {
return (_annotations == null || _annotations.isEmpty()) ? EMPTY_ANN_ARRAY : _annotations.toArray(EMPTY_ANN_ARRAY);
}
public SchemaIdentityConstraint[] identityConstraints() {
return refHelper(_identityConstraints, SchemaIdentityConstraint.Ref::get, SchemaIdentityConstraint[]::new, EMPTY_IC_ARRAY);
}
public ClassLoader getClassLoader() {
return _classloader;
}
/**
* Used INTERNALLY ONLY by the code output AFTER the type system has
* been saved and a handle has been established for each type.
*/
public String handleForType(SchemaType type) {
return _localHandles.handleForType(type);
}
public String getName() {
return _name;
}
/**
* Provide method to be overridden by user typesystems using a different metadata path
*
* @return the metadata directory
* @since XmlBeans 3.1.0
*/
public String getMetadataPath() {
Matcher m = packPat.matcher(_name);
String n = m.find() ? m.group(1) : _name;
return n.replace('.', '/');
}
String getBasePackage() {
return nameToPathString(_name);
}
SchemaTypeLoader getLinker() {
return _linker;
}
SchemaTypePool getTypePool() {
return _localHandles;
}
Set<String> getNamespaces() {
return _namespaces;
}
Map<String, SchemaComponent.Ref> getTypeRefsByClassname() {
return _typeRefsByClassname;
}
OutputStream getSaverStream(String name, String handle) {
try {
return _filer.createBinaryFile(name);
} catch (IOException e) {
throw new SchemaTypeLoaderException(e.getMessage(), getName(), handle, SchemaTypeLoaderException.IO_EXCEPTION, e);
}
}
InputStream getLoaderStream(String resourcename) {
return _resourceLoader.getResourceAsStream(resourcename);
}
}