/plugins/org.eclipse.ecr.core.storage.sql/src/org/eclipse/ecr/core/storage/sql/Node.java
Java | 396 lines | 259 code | 46 blank | 91 comment | 48 complexity | 65b0683927bb123cbc933a16158b3993 MD5 | raw file
- /*
- * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Florent Guillaume
- */
- package org.eclipse.ecr.core.storage.sql;
- import java.io.Serializable;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.LinkedHashSet;
- import java.util.List;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Set;
- import org.apache.commons.collections.map.ReferenceMap;
- import org.eclipse.ecr.core.storage.StorageException;
- /**
- * A {@code Node} implementation. The actual data is stored in contained objects
- * that are {@link Fragment}s.
- */
- public class Node {
- /** The persistence context used. */
- private final PersistenceContext context;
- private final Model model;
- /** The hierarchy/main fragment. */
- protected final SimpleFragment hierFragment;
- /** Fragment information for each additional mixin or inherited fragment. */
- private final FragmentsMap fragments;
- /**
- * Cache of property objects already retrieved. They are dumb objects, just
- * providing an indirection to an underlying {@link Fragment}.
- */
- private final Map<String, BaseProperty> propertyCache;
- private Boolean isVersion;
- /**
- * Creates a Node.
- *
- * @param context the persistence context
- * @param fragmentGroup the group of fragments for the node
- */
- @SuppressWarnings("unchecked")
- protected Node(PersistenceContext context, FragmentGroup fragmentGroup)
- throws StorageException {
- this.context = context;
- model = context.model;
- hierFragment = fragmentGroup.hier;
- if (fragmentGroup.fragments == null) {
- fragments = new FragmentsMap();
- } else {
- fragments = fragmentGroup.fragments;
- }
- // memory-sensitive
- propertyCache = new ReferenceMap(ReferenceMap.HARD, ReferenceMap.SOFT);
- }
- // ----- basics -----
- /**
- * Gets the node unique id, usually a Long or a String.
- *
- * @return the node id
- */
- public Serializable getId() {
- /*
- * We don't cache the id as it changes between the initial creation and
- * the first save.
- */
- return hierFragment.getId();
- }
- public String getName() {
- try {
- return getHierFragment().getString(model.HIER_CHILD_NAME_KEY);
- } catch (StorageException e) {
- // do not propagate this unlikely exception as a checked one
- throw new RuntimeException(e);
- }
- }
- public String getPrimaryType() {
- try {
- return hierFragment.getString(model.MAIN_PRIMARY_TYPE_KEY);
- } catch (StorageException e) {
- // do not propagate this unlikely exception as a checked one
- throw new RuntimeException(e);
- }
- }
- public String getParentId() {
- try {
- return getHierFragment().getString(model.HIER_PARENT_KEY);
- } catch (StorageException e) {
- // do not propagate this unlikely exception as a checked one
- throw new RuntimeException(e);
- }
- }
- protected SimpleFragment getHierFragment() {
- return hierFragment;
- }
- // cache the isVersion computation
- public boolean isVersion() {
- if (isVersion == null) {
- try {
- isVersion = (Boolean) getSimpleProperty(
- model.MAIN_IS_VERSION_PROP).getValue();
- } catch (StorageException e) {
- throw new RuntimeException(e);
- }
- if (isVersion == null) {
- isVersion = Boolean.FALSE;
- }
- }
- return isVersion.booleanValue();
- }
- public boolean isProxy() {
- return getPrimaryType().equals(model.PROXY_TYPE);
- }
- private static final String[] NO_MIXINS = {};
- /**
- * Gets the instance mixins. Mixins from the type are not returned.
- * <p>
- * Never returns {@code null}.
- */
- public String[] getMixinTypes() {
- try {
- String[] value = (String[]) hierFragment.get(model.MAIN_MIXIN_TYPES_KEY);
- return value == null ? NO_MIXINS : value.clone();
- } catch (StorageException e) {
- throw new RuntimeException(e);
- }
- }
- /**
- * Gets the mixins. Includes mixins from the type. Returns a fresh set.
- */
- public Set<String> getAllMixinTypes() {
- // linked for deterministic result
- Set<String> mixins = new LinkedHashSet<String>(
- model.getDocumentTypeFacets(getPrimaryType()));
- mixins.addAll(Arrays.asList(getMixinTypes()));
- return mixins;
- }
- /**
- * Checks the mixins. Includes mixins from the type.
- */
- public boolean hasMixinType(String mixin) {
- if (model.getDocumentTypeFacets(getPrimaryType()).contains(mixin)) {
- return true; // present in type
- }
- for (String m : getMixinTypes()) {
- if (m.equals(mixin)) {
- return true; // present in node
- }
- }
- return false;
- }
- /**
- * Adds a mixin.
- */
- public boolean addMixinType(String mixin) {
- if (model.getMixinPropertyInfos(mixin) == null) {
- throw new IllegalArgumentException("No such mixin: " + mixin);
- }
- if (model.getDocumentTypeFacets(getPrimaryType()).contains(mixin)) {
- return false; // already present in type
- }
- List<String> list = new ArrayList<String>(Arrays.asList(getMixinTypes()));
- if (list.contains(mixin)) {
- return false; // already present in node
- }
- list.add(mixin);
- try {
- String[] mixins = list.toArray(new String[list.size()]);
- hierFragment.put(model.MAIN_MIXIN_TYPES_KEY, mixins);
- } catch (StorageException e) {
- throw new RuntimeException(e);
- }
- return true;
- }
- /**
- * Removes a mixin.
- */
- public boolean removeMixinType(String mixin) {
- List<String> list = new ArrayList<String>(Arrays.asList(getMixinTypes()));
- if (!list.remove(mixin)) {
- return false; // not present in node
- }
- try {
- String[] mixins = list.toArray(new String[list.size()]);
- if (mixins.length == 0) {
- mixins = null;
- }
- hierFragment.put(model.MAIN_MIXIN_TYPES_KEY, mixins);
- clearMixinValues(mixin);
- } catch (StorageException e) {
- throw new RuntimeException(e);
- }
- return true;
- }
- protected void clearMixinValues(String mixin) throws StorageException {
- for (Entry<String, ModelProperty> en : model.getMixinPropertyInfos(
- mixin).entrySet()) {
- String name = en.getKey();
- if (getPropertyInfo(name) != null) {
- // don't clear if still exists in primary type or other
- // mixins
- continue;
- }
- ModelProperty propertyInfo = en.getValue();
- if (propertyInfo.propertyType.isArray()) {
- makeCollectionProperty(name, propertyInfo).setValue(null);
- } else {
- makeSimpleProperty(name, propertyInfo).setValue(null);
- }
- }
- propertyCache.clear(); // some properties have now become invalid
- // TODO optim: delete rows if all null
- }
- // ----- properties -----
- /**
- * Gets a simple property from the node, given its name.
- *
- * @param name the property name
- * @return the property
- * @throws IllegalArgumentException if the name is invalid
- */
- public SimpleProperty getSimpleProperty(String name)
- throws StorageException {
- SimpleProperty property = (SimpleProperty) propertyCache.get(name);
- if (property == null) {
- ModelProperty propertyInfo = getPropertyInfo(name);
- if (propertyInfo == null) {
- throw new IllegalArgumentException("Unknown field: " + name);
- }
- property = makeSimpleProperty(name, propertyInfo);
- propertyCache.put(name, property);
- }
- return property;
- }
- protected SimpleProperty makeSimpleProperty(String name,
- ModelProperty propertyInfo) throws StorageException {
- String fragmentName = propertyInfo.fragmentName;
- Fragment fragment = fragments.get(fragmentName);
- if (fragment == null) {
- // lazy fragment, fetch from session
- RowId rowId = new RowId(fragmentName, getId());
- fragment = context.get(rowId, true);
- fragments.put(fragmentName, fragment);
- }
- return new SimpleProperty(name, propertyInfo.propertyType,
- propertyInfo.readonly, (SimpleFragment) fragment,
- propertyInfo.fragmentKey);
- }
- /**
- * Gets a collection property from the node, given its name.
- *
- * @param name the property name
- * @return the property
- * @throws IllegalArgumentException if the name is invalid
- */
- public CollectionProperty getCollectionProperty(String name)
- throws StorageException {
- CollectionProperty property = (CollectionProperty) propertyCache.get(name);
- if (property == null) {
- ModelProperty propertyInfo = getPropertyInfo(name);
- if (propertyInfo == null) {
- throw new IllegalArgumentException("Unknown field: " + name);
- }
- property = makeCollectionProperty(name, propertyInfo);
- propertyCache.put(name, property);
- }
- return property;
- }
- protected CollectionProperty makeCollectionProperty(String name,
- ModelProperty propertyInfo) throws StorageException {
- String fragmentName = propertyInfo.fragmentName;
- RowId rowId = new RowId(fragmentName, getId());
- Fragment fragment = context.get(rowId, true);
- CollectionProperty property = new CollectionProperty(name,
- propertyInfo.propertyType, false, (CollectionFragment) fragment);
- return property;
- }
- public BaseProperty getProperty(String name) throws StorageException {
- BaseProperty property = propertyCache.get(name);
- if (property != null) {
- return property;
- }
- ModelProperty propertyInfo = getPropertyInfo(name);
- if (propertyInfo == null) {
- throw new IllegalArgumentException("Unknown field: " + name);
- }
- if (propertyInfo.propertyType.isArray()) {
- return getCollectionProperty(name);
- } else {
- return getSimpleProperty(name);
- }
- }
- protected ModelProperty getPropertyInfo(String name) {
- // check primary type
- ModelProperty propertyInfo = model.getPropertyInfo(getPrimaryType(),
- name);
- if (propertyInfo != null) {
- return propertyInfo;
- }
- // check mixins
- for (String mixin : getMixinTypes()) {
- propertyInfo = model.getMixinPropertyInfo(mixin, name);
- if (propertyInfo != null) {
- return propertyInfo;
- }
- }
- return null;
- }
- public void setSimpleProperty(String name, Serializable value)
- throws StorageException {
- SimpleProperty property = getSimpleProperty(name);
- property.setValue(value);
- }
- public void setCollectionProperty(String name, Serializable[] value)
- throws StorageException {
- CollectionProperty property = getCollectionProperty(name);
- property.setValue(value);
- }
- // ----- locking -----
- // ----- lifecycle -----
- // ----- versioning -----
- // ----- activities, baselines, configurations -----
- // ----- shared nodes -----
- // ----- retention -----
- /*
- * ----- equals/hashcode -----
- */
- @Override
- public boolean equals(Object other) {
- if (other == this) {
- return true;
- }
- if (other instanceof Node) {
- return equals((Node) other);
- }
- return false;
- }
- private boolean equals(Node other) {
- return getId() == other.getId();
- }
- @Override
- public int hashCode() {
- return getId().hashCode();
- }
- }