/* * * * Copyright 2014 Orient Technologies LTD (info(at)orientechnologies.com) * * * * 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. * * * * For more information: http://www.orientechnologies.com * */ package com.orientechnologies.orient.core.metadata.schema; import com.orientechnologies.common.listener.OProgressListener; import com.orientechnologies.orient.core.db.ODatabaseDocumentInternal; import com.orientechnologies.orient.core.db.ODatabaseRecordThreadLocal; import com.orientechnologies.orient.core.db.record.OClassTrigger; import com.orientechnologies.orient.core.index.OIndex; import com.orientechnologies.orient.core.index.OIndexManager; import com.orientechnologies.orient.core.metadata.function.OFunctionTrigger; import com.orientechnologies.orient.core.metadata.schema.clusterselection.OClusterSelectionStrategy; import com.orientechnologies.orient.core.metadata.security.ORole; import com.orientechnologies.orient.core.metadata.security.OSecurityShared; import com.orientechnologies.orient.core.metadata.security.OUser; import com.orientechnologies.orient.core.record.impl.ODocument; import com.orientechnologies.orient.core.schedule.OScheduledEvent; import java.io.IOException; import java.util.*; /** * @author Andrey Lomakin (a.lomakin-at-orientechnologies.com) * @since 10/21/14 */ public class OImmutableClass implements OClass { /** * use OClass.EDGE_CLASS_NAME instead */ @Deprecated public static final String EDGE_CLASS_NAME = OClass.EDGE_CLASS_NAME; /** * use OClass.EDGE_CLASS_NAME instead */ @Deprecated public static final String VERTEX_CLASS_NAME = OClass.VERTEX_CLASS_NAME; private boolean inited = false; private final boolean isAbstract; private final boolean strictMode; private final String name; private final String streamAbleName; private final Map<String, OProperty> properties; private Map<String, OProperty> allPropertiesMap; private Collection<OProperty> allProperties; private final OClusterSelectionStrategy clusterSelection; private final int defaultClusterId; private final int[] clusterIds; private final int[] polymorphicClusterIds; private final Collection<String> baseClassesNames; private final List<String> superClassesNames; private final float overSize; private final float classOverSize; private final String shortName; private final Map<String, String> customFields; private final String description; private final OImmutableSchema schema; // do not do it volatile it is already SAFE TO USE IT in MT mode. private final List<OImmutableClass> superClasses; // do not do it volatile it is already SAFE TO USE IT in MT mode. private Collection<OImmutableClass> subclasses; private boolean restricted; private boolean isVertexType; private boolean isEdgeType; private boolean triggered; private boolean function; private boolean scheduler; private boolean ouser; private boolean orole; private OIndex<?> autoShardingIndex; public OImmutableClass(final OClass oClass, final OImmutableSchema schema) { isAbstract = oClass.isAbstract(); strictMode = oClass.isStrictMode(); this.schema = schema; superClassesNames = oClass.getSuperClassesNames(); superClasses = new ArrayList<OImmutableClass>(superClassesNames.size()); name = oClass.getName(); streamAbleName = oClass.getStreamableName(); clusterSelection = oClass.getClusterSelection(); defaultClusterId = oClass.getDefaultClusterId(); clusterIds = oClass.getClusterIds(); polymorphicClusterIds = oClass.getPolymorphicClusterIds(); baseClassesNames = new ArrayList<String>(); for (OClass baseClass : oClass.getSubclasses()) baseClassesNames.add(baseClass.getName()); overSize = oClass.getOverSize(); classOverSize = oClass.getClassOverSize(); shortName = oClass.getShortName(); properties = new HashMap<String, OProperty>(); for (OProperty p : oClass.declaredProperties()) properties.put(p.getName().toLowerCase(), new OImmutableProperty(p, this)); Map<String, String> customFields = new HashMap<String, String>(); for (String key : oClass.getCustomKeys()) customFields.put(key, oClass.getCustom(key)); this.customFields = Collections.unmodifiableMap(customFields); this.description = oClass.getDescription(); } public void init() { if (!inited) { initSuperClasses(); final Collection<OProperty> allProperties = new ArrayList<OProperty>(); final Map<String, OProperty> allPropsMap = new HashMap<String, OProperty>(20); for (int i = superClasses.size() - 1; i >= 0; i--) { allProperties.addAll(superClasses.get(i).allProperties); allPropsMap.putAll(superClasses.get(i).allPropertiesMap); } allProperties.addAll(properties.values()); for (OProperty p : properties.values()) { final String propName = p.getName(); if (!allPropsMap.containsKey(propName)) allPropsMap.put(propName, p); } this.allProperties = Collections.unmodifiableCollection(allProperties); this.allPropertiesMap = Collections.unmodifiableMap(allPropsMap); this.restricted = isSubClassOf(OSecurityShared.RESTRICTED_CLASSNAME); this.isVertexType = isSubClassOf(OClass.VERTEX_CLASS_NAME); this.isEdgeType = isSubClassOf(OClass.EDGE_CLASS_NAME); this.triggered = isSubClassOf(OClassTrigger.CLASSNAME); this.function = isSubClassOf(OFunctionTrigger.CLASSNAME); this.scheduler = isSubClassOf(OScheduledEvent.CLASS_NAME); this.ouser = isSubClassOf(OUser.CLASS_NAME); this.orole = isSubClassOf(ORole.CLASS_NAME); final ODatabaseDocumentInternal db = ODatabaseRecordThreadLocal.INSTANCE.getIfDefined(); this.autoShardingIndex = db != null && db.getMetadata() != null && db.getMetadata().getIndexManager() != null ? db.getMetadata().getIndexManager().getClassAutoShardingIndex(name) : null; } inited = true; } @Override public boolean isAbstract() { return isAbstract; } @Override public OClass setAbstract(boolean iAbstract) { throw new UnsupportedOperationException(); } @Override public boolean isStrictMode() { return strictMode; } @Override public OClass setStrictMode(boolean iMode) { throw new UnsupportedOperationException(); } @Override public OClass getSuperClass() { initSuperClasses(); return superClasses.isEmpty() ? null : superClasses.get(0); } @Override public OClass setSuperClass(OClass iSuperClass) { throw new UnsupportedOperationException(); } @Override public List<OClass> getSuperClasses() { return Collections.unmodifiableList((List<? extends OClass>) superClasses); } @Override public boolean hasSuperClasses() { return !superClasses.isEmpty(); } @Override public List<String> getSuperClassesNames() { return superClassesNames; } @Override public OClass setSuperClasses(List<? extends OClass> classes) { throw new UnsupportedOperationException(); } @Override public OClass addSuperClass(OClass superClass) { throw new UnsupportedOperationException(); } @Override public OClass removeSuperClass(OClass superClass) { throw new UnsupportedOperationException(); } @Override public String getName() { return name; } @Override public OClass setName(String iName) { throw new UnsupportedOperationException(); } @Override public String getStreamableName() { return streamAbleName; } @Override public Collection<OProperty> declaredProperties() { return Collections.unmodifiableCollection(properties.values()); } @Override public Collection<OProperty> properties() { return allProperties; } @Override public Map<String, OProperty> propertiesMap() { return allPropertiesMap; } public void getIndexedProperties(Collection<OProperty> indexedProperties) { for (OProperty p : properties.values()) if (areIndexed(p.getName())) indexedProperties.add(p); initSuperClasses(); for (OImmutableClass superClass : superClasses) { superClass.getIndexedProperties(indexedProperties); } } @Override public Collection<OProperty> getIndexedProperties() { Collection<OProperty> indexedProps = new HashSet<OProperty>(); getIndexedProperties(indexedProps); return indexedProps; } @Override public OProperty getProperty(String propertyName) { initSuperClasses(); propertyName = propertyName.toLowerCase(); OProperty p = properties.get(propertyName); if (p != null) return p; for (int i = 0; i < superClasses.size() && p == null; i++) { p = superClasses.get(i).getProperty(propertyName); } return p; } @Override public OProperty createProperty(String iPropertyName, OType iType) { throw new UnsupportedOperationException(); } @Override public OProperty createProperty(String iPropertyName, OType iType, OClass iLinkedClass) { throw new UnsupportedOperationException(); } @Override public OProperty createProperty(String iPropertyName, OType iType, OClass iLinkedClass, boolean unsafe) { throw new UnsupportedOperationException(); } @Override public OProperty createProperty(String iPropertyName, OType iType, OType iLinkedType) { throw new UnsupportedOperationException(); } @Override public OProperty createProperty(String iPropertyName, OType iType, OType iLinkedType, boolean unsafe) { throw new UnsupportedOperationException(); } @Override public void dropProperty(String iPropertyName) { throw new UnsupportedOperationException(); } @Override public boolean existsProperty(String propertyName) { propertyName = propertyName.toLowerCase(); boolean result = properties.containsKey(propertyName); if (result) return true; for (OImmutableClass superClass : superClasses) { result = superClass.existsProperty(propertyName); if (result) return true; } return false; } @Override public int getClusterForNewInstance(final ODocument doc) { return clusterSelection.getCluster(this, doc); } @Override public int getDefaultClusterId() { return defaultClusterId; } @Override public void setDefaultClusterId(int iDefaultClusterId) { throw new UnsupportedOperationException(); } @Override public int[] getClusterIds() { return clusterIds; } @Override public OClass addClusterId(int iId) { throw new UnsupportedOperationException(); } @Override public OClusterSelectionStrategy getClusterSelection() { return clusterSelection; } @Override public OClass setClusterSelection(OClusterSelectionStrategy clusterSelection) { throw new UnsupportedOperationException(); } @Override public OClass setClusterSelection(String iStrategyName) { throw new UnsupportedOperationException(); } @Override public OClass addCluster(String iClusterName) { throw new UnsupportedOperationException(); } @Override public OClass truncateCluster(String clusterName) { throw new UnsupportedOperationException(); } @Override public OClass removeClusterId(int iId) { throw new UnsupportedOperationException(); } @Override public int[] getPolymorphicClusterIds() { return Arrays.copyOf(polymorphicClusterIds, polymorphicClusterIds.length); } public OImmutableSchema getSchema() { return schema; } @Override public Collection<OClass> getSubclasses() { initBaseClasses(); ArrayList<OClass> result = new ArrayList<OClass>(); for (OClass c : subclasses) result.add(c); return result; } @Override public Collection<OClass> getAllSubclasses() { initBaseClasses(); final Set<OClass> set = new HashSet<OClass>(); set.addAll(getSubclasses()); for (OImmutableClass c : subclasses) set.addAll(c.getAllSubclasses()); return set; } @Override public Collection<OClass> getBaseClasses() { return getSubclasses(); } @Override public Collection<OClass> getAllBaseClasses() { return getAllSubclasses(); } @Override public Collection<OClass> getAllSuperClasses() { Set<OClass> ret = new HashSet<OClass>(); getAllSuperClasses(ret); return ret; } private void getAllSuperClasses(Set<OClass> set) { set.addAll(superClasses); for (OImmutableClass superClass : superClasses) { superClass.getAllSuperClasses(set); } } @Override public long getSize() { long size = 0; for (int clusterId : clusterIds) size += getDatabase().getClusterRecordSizeById(clusterId); return size; } @Override public float getOverSize() { return overSize; } @Override public float getClassOverSize() { return classOverSize; } @Override public OClass setOverSize(float overSize) { throw new UnsupportedOperationException(); } @Override public long count() { return count(true); } @Override public long count(boolean isPolymorphic) { if (isPolymorphic) return getDatabase().countClusterElements(OClassImpl.readableClusters(getDatabase(), polymorphicClusterIds)); return getDatabase().countClusterElements(OClassImpl.readableClusters(getDatabase(), clusterIds)); } @Override public void truncate() throws IOException { throw new UnsupportedOperationException(); } @Override public boolean isSubClassOf(final String iClassName) { if (iClassName == null) return false; if (iClassName.equalsIgnoreCase(getName()) || iClassName.equalsIgnoreCase(getShortName())) return true; final int s = superClasses.size(); for (int i = 0; i < s; ++i) { if (superClasses.get(i).isSubClassOf(iClassName)) return true; } return false; } @Override public boolean isSubClassOf(final OClass clazz) { if (clazz == null) return false; if (equals(clazz)) return true; final int s = superClasses.size(); for (int i = 0; i < s; ++i) { if (superClasses.get(i).isSubClassOf(clazz)) return true; } return false; } @Override public boolean isSuperClassOf(OClass clazz) { return clazz != null && clazz.isSubClassOf(this); } @Override public String getShortName() { return shortName; } @Override public OClass setShortName(String shortName) { throw new UnsupportedOperationException(); } @Override public String getDescription() { return description; } @Override public OClass setDescription(String iDescription) { throw new UnsupportedOperationException(); } @Override public Object get(ATTRIBUTES iAttribute) { if (iAttribute == null) throw new IllegalArgumentException("attribute is null"); switch (iAttribute) { case NAME: return getName(); case SHORTNAME: return getShortName(); case SUPERCLASS: return getSuperClass(); case SUPERCLASSES: return getSuperClasses(); case OVERSIZE: return getOverSize(); case STRICTMODE: return isStrictMode(); case ABSTRACT: return isAbstract(); case CLUSTERSELECTION: return getClusterSelection(); case CUSTOM: return getCustomInternal(); case DESCRIPTION: return getDescription(); } throw new IllegalArgumentException("Cannot find attribute '" + iAttribute + "'"); } @Override public OClass set(ATTRIBUTES attribute, Object iValue) { throw new UnsupportedOperationException(); } @Override public OIndex<?> createIndex(String iName, INDEX_TYPE iType, String... fields) { throw new UnsupportedOperationException(); } @Override public OIndex<?> createIndex(String iName, String iType, String... fields) { throw new UnsupportedOperationException(); } @Override public OIndex<?> createIndex(String iName, INDEX_TYPE iType, OProgressListener iProgressListener, String... fields) { throw new UnsupportedOperationException(); } @Override public OIndex<?> createIndex(String iName, String iType, OProgressListener iProgressListener, ODocument metadata, String algorithm, String... fields) { throw new UnsupportedOperationException(); } @Override public OIndex<?> createIndex(String iName, String iType, OProgressListener iProgressListener, ODocument metadata, String... fields) { throw new UnsupportedOperationException(); } @Override public Set<OIndex<?>> getInvolvedIndexes(Collection<String> fields) { initSuperClasses(); final Set<OIndex<?>> result = new HashSet<OIndex<?>>(getClassInvolvedIndexes(fields)); for (OImmutableClass superClass : superClasses) { result.addAll(superClass.getInvolvedIndexes(fields)); } return result; } @Override public Set<OIndex<?>> getInvolvedIndexes(String... fields) { return getInvolvedIndexes(Arrays.asList(fields)); } @Override public Set<OIndex<?>> getClassInvolvedIndexes(Collection<String> fields) { final OIndexManager indexManager = getDatabase().getMetadata().getIndexManager(); return indexManager.getClassInvolvedIndexes(name, fields); } @Override public Set<OIndex<?>> getClassInvolvedIndexes(String... fields) { return getClassInvolvedIndexes(Arrays.asList(fields)); } @Override public boolean areIndexed(Collection<String> fields) { final OIndexManager indexManager = getDatabase().getMetadata().getIndexManager(); final boolean currentClassResult = indexManager.areIndexed(name, fields); initSuperClasses(); if (currentClassResult) return true; for (OImmutableClass superClass : superClasses) { if (superClass.areIndexed(fields)) return true; } return false; } @Override public boolean areIndexed(String... fields) { return areIndexed(Arrays.asList(fields)); } @Override public OIndex<?> getClassIndex(String iName) { return getDatabase().getMetadata().getIndexManager().getClassIndex(this.name, iName); } @Override public Set<OIndex<?>> getClassIndexes() { return getDatabase().getMetadata().getIndexManager().getClassIndexes(name); } @Override public void getClassIndexes(final Collection<OIndex<?>> indexes) { getDatabase().getMetadata().getIndexManager().getClassIndexes(name, indexes); } @Override public void getIndexes(final Collection<OIndex<?>> indexes) { initSuperClasses(); getClassIndexes(indexes); for (OClass superClass : superClasses) { superClass.getIndexes(indexes); } } @Override public Set<OIndex<?>> getIndexes() { final Set<OIndex<?>> indexes = new HashSet<OIndex<?>>(); getIndexes(indexes); return indexes; } @Override public OIndex<?> getAutoShardingIndex() { return autoShardingIndex; } @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result; return result; } @Override public boolean equals(final Object obj) { if (this == obj) return true; if (obj == null) return false; if (!OClass.class.isAssignableFrom(obj.getClass())) return false; final OClass other = (OClass) obj; if (name == null) { if (other.getName() != null) return false; } else if (!name.equals(other.getName())) return false; return true; } @Override public String toString() { return name; } @Override public String getCustom(final String iName) { return customFields.get(iName); } @Override public OClass setCustom(String iName, String iValue) { throw new UnsupportedOperationException(); } @Override public void removeCustom(String iName) { throw new UnsupportedOperationException(); } @Override public void clearCustom() { throw new UnsupportedOperationException(); } @Override public Set<String> getCustomKeys() { return Collections.unmodifiableSet(customFields.keySet()); } @Override public boolean hasClusterId(final int clusterId) { return Arrays.binarySearch(clusterIds, clusterId) >= 0; } @Override public boolean hasPolymorphicClusterId(final int clusterId) { return Arrays.binarySearch(polymorphicClusterIds, clusterId) >= 0; } @Override public int compareTo(final OClass other) { return name.compareTo(other.getName()); } private ODatabaseDocumentInternal getDatabase() { return ODatabaseRecordThreadLocal.INSTANCE.get(); } private Map<String, String> getCustomInternal() { return customFields; } private void initSuperClasses() { if (superClassesNames != null && superClassesNames.size() != superClasses.size()) { superClasses.clear(); for (String superClassName : superClassesNames) { OImmutableClass superClass = (OImmutableClass) schema.getClass(superClassName); superClass.init(); superClasses.add(superClass); } } } private void initBaseClasses() { if (subclasses == null) { final List<OImmutableClass> result = new ArrayList<OImmutableClass>(baseClassesNames.size()); for (String clsName : baseClassesNames) result.add((OImmutableClass) schema.getClass(clsName)); subclasses = result; } } public boolean isRestricted() { return restricted; } public boolean isEdgeType() { return isEdgeType; } public boolean isVertexType() { return isVertexType; } public boolean isTriggered() { return triggered; } public boolean isFunction() { return function; } public boolean isScheduler() { return scheduler; } public boolean isOuser() { return ouser; } public boolean isOrole() { return orole; } }