/* * Copyright (c) 2008-2013, 2015, 2016 Eike Stepper (Berlin, Germany) 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: * Eike Stepper - initial API and implementation * Simon McDuff - bug 226778 * Simon McDuff - bug 213402 * Martin Taal - Added subtype handling and EClass conversion, bug 283106 */ package org.eclipse.emf.cdo.common.id; import org.eclipse.emf.cdo.common.branch.CDOBranch; import org.eclipse.emf.cdo.common.id.CDOID.ObjectType; import org.eclipse.emf.cdo.common.id.CDOID.Type; import org.eclipse.emf.cdo.common.model.CDOClassifierRef; import org.eclipse.emf.cdo.common.protocol.CDODataInput; import org.eclipse.emf.cdo.common.protocol.CDODataOutput; import org.eclipse.emf.cdo.common.revision.CDOIDAndBranch; import org.eclipse.emf.cdo.common.revision.CDOIDAndVersion; import org.eclipse.emf.cdo.internal.common.bundle.OM; import org.eclipse.emf.cdo.internal.common.id.CDOIDExternalImpl; import org.eclipse.emf.cdo.internal.common.id.CDOIDObjectLongImpl; import org.eclipse.emf.cdo.internal.common.id.CDOIDObjectLongWithClassifierImpl; import org.eclipse.emf.cdo.internal.common.id.CDOIDObjectStringImpl; import org.eclipse.emf.cdo.internal.common.id.CDOIDObjectStringWithClassifierImpl; import org.eclipse.emf.cdo.internal.common.id.CDOIDObjectUUIDImpl; import org.eclipse.emf.cdo.internal.common.id.CDOIDTempObjectExternalImpl; import org.eclipse.emf.cdo.internal.common.id.CDOIDTempObjectImpl; import org.eclipse.emf.cdo.internal.common.messages.Messages; import org.eclipse.emf.cdo.internal.common.revision.CDOIDAndBranchImpl; import org.eclipse.emf.cdo.internal.common.revision.CDOIDAndVersionImpl; import org.eclipse.emf.cdo.spi.common.id.AbstractCDOID; import org.eclipse.emf.cdo.spi.common.id.InternalCDOIDObject; import org.eclipse.net4j.util.UUIDGenerator; import org.eclipse.net4j.util.io.ExtendedDataInput; import org.eclipse.net4j.util.io.ExtendedDataOutput; import org.eclipse.net4j.util.om.trace.ContextTracer; import java.io.IOException; import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; /** * Various static methods that may help with CDO {@link CDOID IDs}. * * @author Eike Stepper * @since 2.0 */ public final class CDOIDUtil { private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG_PROTOCOL, CDOIDUtil.class); private CDOIDUtil() { } /** * @since 4.2 */ public static <V> Map<CDOID, V> createMap() { return new HashMap<CDOID, V>(); } /** * @since 4.5 */ public static <V> Map<CDOID, V> createMap(Map<? extends CDOID, ? extends V> map) { return new HashMap<CDOID, V>(map); } /** * @since 4.0 */ public static CDOIDAndVersion createIDAndVersion(CDOID id, int version) { return new CDOIDAndVersionImpl(id, version); } /** * @since 4.0 */ public static CDOIDAndVersion createIDAndVersion(CDOIDAndVersion source) { return createIDAndVersion(source.getID(), source.getVersion()); } /** * @since 4.0 */ public static CDOIDAndBranch createIDAndBranch(CDOID id, CDOBranch branch) { return new CDOIDAndBranchImpl(id, branch); } /** * @since 4.2 */ public static CDOID getCDOID(Object object) { if (object == null) { return CDOID.NULL; } if (object instanceof CDOID) { return (CDOID)object; } if (object instanceof CDOWithID) { CDOID id = ((CDOWithID)object).cdoID(); return id == null ? CDOID.NULL : id; } if (object instanceof CDOIdentifiable) { CDOID id = ((CDOIdentifiable)object).getID(); return id == null ? CDOID.NULL : id; } return null; } /** * @since 2.0 */ public static boolean isNull(CDOID id) { return id == null || id.isNull(); } public static long getLong(CDOID id) { if (id == null) { return 0L; } switch (id.getType()) { case NULL: return 0L; case OBJECT: if (id instanceof CDOIDObjectLongImpl) { return ((CDOIDObjectLongImpl)id).getLongValue(); } if (id instanceof CDOIDObjectLongWithClassifierImpl) { return ((CDOIDObjectLongWithClassifierImpl)id).getLongValue(); } throw new IllegalArgumentException(MessageFormat.format(Messages.getString("CDOIDUtil.0"), id.getClass().getName())); //$NON-NLS-1$ case TEMP_OBJECT: throw new IllegalArgumentException(Messages.getString("CDOIDUtil.1")); //$NON-NLS-1$ case EXTERNAL_OBJECT: case EXTERNAL_TEMP_OBJECT: throw new IllegalArgumentException(Messages.getString("CDOIDUtil.2")); //$NON-NLS-1$ default: throw new IllegalArgumentException(MessageFormat.format(Messages.getString("CDOIDUtil.3"), id.getClass().getName())); //$NON-NLS-1$ } } /** * @since 4.0 */ public static String getString(CDOID id) { if (id instanceof CDOIDString) { return ((CDOIDString)id).getStringValue(); } return null; } /** * @since 4.1 */ public static byte[] getByteArray(CDOID id) { if (id instanceof CDOIDObjectUUIDImpl) { return ((CDOIDObjectUUIDImpl)id).getByteArrayValue(); } return null; } /** * @since 3.0 */ public static CDOClassifierRef getClassifierRef(CDOID id) { if (id instanceof CDOClassifierRef.Provider) { return ((CDOClassifierRef.Provider)id).getClassifierRef(); } return null; } public static CDOIDTemp createTempObject(int value) { return CDOIDTempObjectImpl.create(value); } /** * @since 3.0 */ public static CDOIDExternal createTempObjectExternal(String uri) { return CDOIDTempObjectExternalImpl.create(uri); } public static CDOID createLong(long value) { if (value == 0L) { return CDOID.NULL; } return CDOIDObjectLongImpl.create(value); } /** * @since 4.2 */ public static CDOID createLongWithClassifier(long value, CDOClassifierRef classifierRef) { return CDOIDObjectLongWithClassifierImpl.create(value, classifierRef); } /** * @since 4.0 */ public static CDOID createString(String value) { return CDOIDObjectStringImpl.create(value); } /** * @since 4.2 */ public static CDOID createStringWithClassifier(String value, CDOClassifierRef classifierRef) { return CDOIDObjectStringWithClassifierImpl.create(value, classifierRef); } /** * @since 4.1 */ public static CDOID createUUID(byte[] value) { return CDOIDObjectUUIDImpl.create(value); } /** * @since 4.1 */ public static CDOID createUUID() { byte[] value = new byte[16]; UUIDGenerator.DEFAULT.generate(value); return createUUID(value); } /** * @since 4.1 */ public static String encodeUUID(byte[] bytes) { return UUIDGenerator.DEFAULT.encode(bytes); } /** * @since 4.1 */ public static byte[] decodeUUID(String string) { return UUIDGenerator.DEFAULT.decode(string); } /** * @since 2.0 */ public static CDOIDExternal createExternal(String uri) { return CDOIDExternalImpl.create(uri); } /** * @since 4.5 */ public static void write(StringBuilder builder, Iterable<?> objects) { boolean first = true; for (Object object : objects) { CDOID id = getCDOID(object); if (id != null) { if (first) { first = false; } else { builder.append(", "); } write(builder, id); } } } /** * Format of the uri fragment. * <p> * Non-legacy: <code><ID TYPE>/<CUSTOM STRING FROM OBJECT FACTORY></code> * <p> * Legacy: <code><ID TYPE>/<PACKAGE URI>/<CLASSIFIER ID>/<CUSTOM STRING FROM OBJECT FACTORY></code> * * @since 2.0 */ public static void write(StringBuilder builder, CDOID id) { if (id == null) { id = CDOID.NULL; } if (id instanceof InternalCDOIDObject) { ObjectType subType = ((InternalCDOIDObject)id).getSubType(); builder.append(subType.getID()); } else { Type type = id.getType(); builder.append(type.getID()); } builder.append(id.toURIFragment()); } /** * Format of the URI fragment. * <p> * Non-legacy: <code><ID TYPE>/<CUSTOM STRING FROM OBJECT FACTORY></code> * <p> * Legacy: <code><ID TYPE>/<PACKAGE URI>/<CLASSIFIER ID>/<CUSTOM STRING FROM OBJECT FACTORY></code> * * @since 3.0 */ public static CDOID read(String uriFragment) { char typeID = uriFragment.charAt(0); Enum<?> literal = CDOID.Type.getLiteral(typeID); if (literal == null) { throw new IllegalArgumentException("Unknown type ID: " + typeID); } String fragment = uriFragment.substring(1); if (literal instanceof ObjectType) { return readCDOIDObject(fragment, (ObjectType)literal); } Type type = (Type)literal; switch (type) { case NULL: return CDOID.NULL; case TEMP_OBJECT: return CDOIDTempObjectImpl.create(Integer.valueOf(fragment)); case EXTERNAL_OBJECT: return CDOIDExternalImpl.create(fragment); case EXTERNAL_TEMP_OBJECT: return CDOIDTempObjectExternalImpl.create(fragment); default: throw new IllegalArgumentException(MessageFormat.format(Messages.getString("CDOIDUtil.5"), uriFragment)); //$NON-NLS-1$ } } private static CDOID readCDOIDObject(String fragment, CDOID.ObjectType subType) { switch (subType) { case LONG: return CDOIDObjectLongImpl.create(fragment); case STRING: return CDOIDObjectStringImpl.create(fragment); case LONG_WITH_CLASSIFIER: return CDOIDObjectLongWithClassifierImpl.create(fragment); case STRING_WITH_CLASSIFIER: return CDOIDObjectStringWithClassifierImpl.create(fragment); case UUID: return CDOIDObjectUUIDImpl.create(fragment); default: throw new IllegalArgumentException("Subtype " + subType.name() + " not supported"); } } /** * @since 4.2 */ public static void write(CDODataOutput out, CDOID id) throws IOException { if (id == null) { id = CDOID.NULL; } if (id instanceof InternalCDOIDObject) { CDOID.ObjectType subType = ((InternalCDOIDObject)id).getSubType(); int ordinal = subType.ordinal(); if (TRACER.isEnabled()) { TRACER.format("Writing CDOIDObject of subtype {0} ({1})", ordinal, subType); //$NON-NLS-1$ } // Negated to distinguish between the subtypes and the maintypes. // Note: Added 1 because ordinal start at 0 out.writeByte(-ordinal - 1); } else { CDOID.Type type = id.getType(); int ordinal = type.ordinal(); if (TRACER.isEnabled()) { TRACER.format("Writing CDOID of type {0} ({1})", ordinal, type); //$NON-NLS-1$ } out.writeByte(ordinal); } ((AbstractCDOID)id).write(out); } /** * @since 4.2 */ public static CDOID read(CDODataInput in) throws IOException { byte ordinal = in.readByte(); // A subtype of OBJECT if (ordinal < 0) { // The ordinal value is negated in the stream to distinguish from the main type. // Note: Added 1 because ordinal start at 0, so correct by minus 1. return readCDOIDObject(in, -ordinal - 1); } if (TRACER.isEnabled()) { String type; try { type = Type.values()[ordinal].toString(); } catch (RuntimeException ex) { type = ex.getMessage(); } TRACER.format("Reading CDOID of type {0} ({1})", ordinal, type); //$NON-NLS-1$ } Type type = Type.values()[ordinal]; switch (type) { case NULL: return CDOID.NULL; case TEMP_OBJECT: return CDOIDTempObjectImpl.create(in.readInt()); case EXTERNAL_OBJECT: return CDOIDExternalImpl.create(in.readString()); case EXTERNAL_TEMP_OBJECT: return CDOIDTempObjectExternalImpl.create(in.readString()); default: throw new IOException("Illegal type: " + type); } } private static CDOID readCDOIDObject(CDODataInput in, int subTypeOrdinal) throws IOException { if (TRACER.isEnabled()) { String subType; try { subType = CDOID.ObjectType.values()[subTypeOrdinal].toString(); } catch (RuntimeException ex) { subType = ex.getMessage(); } TRACER.format("Reading CDOIDObject of subtype {0} ({1})", subTypeOrdinal, subType); //$NON-NLS-1$ } CDOID.ObjectType subType = CDOID.ObjectType.values()[subTypeOrdinal]; if (subType == null) { throw new IllegalArgumentException("Subtype may not be null"); } switch (subType) { case LONG: return CDOIDObjectLongImpl.create(in); case STRING: return CDOIDObjectStringImpl.create(in); case LONG_WITH_CLASSIFIER: return CDOIDObjectLongWithClassifierImpl.create(in); case STRING_WITH_CLASSIFIER: return CDOIDObjectStringWithClassifierImpl.create(in); case UUID: return CDOIDObjectUUIDImpl.create(in); default: throw new IllegalArgumentException("Subtype " + subType.name() + " not supported"); } } /** * @since 2.0 */ public static boolean equals(CDOID id1, CDOID id2) { if (id1 == id2) { return true; } if (id1 == null) { return id2 == CDOID.NULL; } if (id2 == null) { return id1 == CDOID.NULL; } return false; } /** * @since 3.0 * @deprecated As of 4.2 use {@link #createLongWithClassifier(long, CDOClassifierRef)}. */ @Deprecated public static CDOID createLongWithClassifier(CDOClassifierRef classifierRef, long value) { return createLongWithClassifier(value, classifierRef); } /** * @since 3.0 * @deprecated As of 4.2 use {@link #createStringWithClassifier(String, CDOClassifierRef)}. */ @Deprecated public static CDOID createStringWithClassifier(CDOClassifierRef classifierRef, String value) { return createStringWithClassifier(value, classifierRef); } /** * Creates the correct implementation class for the passed {@link CDOID.ObjectType}. * * @param subType * the subType for which to create an empty CDOID instance * @return the instance of CDOIDObject which represents the subtype. * @since 3.0 * @deprecated As of 4.2 no longer supported. IDs can't be created without a value anymore. */ @Deprecated public static AbstractCDOID createCDOIDObject(CDOID.ObjectType subType) { throw new UnsupportedOperationException(); } /** * @since 4.1 * @deprecated As of 4.2 use {@link #write(CDODataOutput, CDOID)}. */ @Deprecated public static void write(ExtendedDataOutput out, CDOID id) throws IOException { write((CDODataOutput)out, id); } /** * @since 4.1 * @deprecated As of 4.2 use {@link #read(CDODataInput)}. */ @Deprecated public static CDOID read(ExtendedDataInput in) throws IOException { return read((CDODataInput)in); } }