blob: c013fa7d17fd13d0aad0475859f20b4914fc17ca [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2019 Christian W. Damus and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Christian W. Damus - initial API and implementation
******************************************************************************/
package org.eclipse.emf.ecp.common.spi;
import java.lang.ref.ReferenceQueue;
import java.util.Arrays;
import java.util.Collection;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.util.Pool;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.IDisposable;
import org.eclipse.emf.edit.provider.IItemLabelProvider;
import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
/**
* API for obtaining user-friendly labels for {@link EObject}s, intended especially for
* {@link Object#toString() debug strings} of objects that encapsulate {@link EObject}s
* but that could be appropriate in other end-user (such as UI) scenarios.
*
* @since 1.20
*/
public final class Labelizer {
private static final Pool<Labelizer> POOL = new PoolImpl();
private final EPackage ePackage;
private AdapterFactory adapterFactory;
/**
* Initializes me with the package that I support.
*/
private Labelizer(EPackage ePackage) {
super();
this.ePackage = ePackage;
}
/**
* Initialize my adapter factory.
*
* @return something to dispose when I am no longer needed
*/
IDisposable initialize() {
final Collection<?> types = Arrays.asList(ePackage, IItemLabelProvider.class);
final ComposedAdapterFactory.Descriptor desc = ComposedAdapterFactory.Descriptor.Registry.INSTANCE
.getDescriptor(types);
if (desc != null) {
adapterFactory = desc.createAdapterFactory();
}
if (adapterFactory == null) {
adapterFactory = new ReflectiveItemProviderAdapterFactory();
}
return adapterFactory instanceof IDisposable
? (IDisposable) adapterFactory
: Labelizer::pass;
}
private static void pass() {
// Nothing to do
}
/**
* Get a labelizer for the given {@code object}.
*
* @param object the objectfor which to get a labelizer
* @return the labelizer
*/
public static Labelizer get(EObject object) {
return get(object.eClass().getEPackage());
}
/**
* Get a labelizer for the given {@code ePackage}.
*
* @param ePackage the package for which to get a labelizer
* @return the labelizer
*/
public static Labelizer get(EPackage ePackage) {
final Labelizer result = new Labelizer(ePackage);
return POOL.intern(result);
}
/**
* Obtain a friendly label for the given {@code object}. Where possible,
* this will be based on the label provided by the model's EMF.Edit providers.
*
* @param object an object
* @return a best-effort friendly readable label for it
*/
public String getLabel(EObject object) {
String result = null;
final IItemLabelProvider labels = (IItemLabelProvider) adapterFactory.adapt(object, IItemLabelProvider.class);
if (labels != null) {
result = labels.getText(object);
}
if (result == null) {
result = String.valueOf(object);
}
return result;
}
@Override
public int hashCode() {
return ePackage.hashCode();
}
@Override
public boolean equals(Object obj) {
return obj instanceof Labelizer && ((Labelizer) obj).ePackage.equals(ePackage);
}
//
// Nested types
//
/**
* Interning labelizer pool implementation.
*/
@SuppressWarnings("serial")
private static final class PoolImpl extends Pool<Labelizer> {
PoolImpl() {
super(8, null, null); // Default object access units and reference queue
}
@Override
protected void putEntry(int index, Entry<Labelizer> entry) {
// initialize the labelizer now
((EntryImpl) entry).initialize();
super.putEntry(index, entry);
}
@Override
protected boolean removeEntry(int index, Entry<Labelizer> entry) {
// dispose the labelizer
((EntryImpl) entry).dispose();
return super.removeEntry(index, entry);
}
@Override
protected Entry<Labelizer> newExternalEntry(Labelizer object, int hashCode) {
return new EntryImpl(this, object, hashCode, externalQueue);
}
//
// Nested types
//
/**
* Custom pool entry that tracks the encapsulation of the disposing of the
* labelizer.
*/
private static final class EntryImpl extends PoolEntry<Labelizer> {
private IDisposable disposer;
EntryImpl(PoolImpl pool, Labelizer labelizer, int hashCode, ReferenceQueue<Object> externalQueue) {
super(pool, labelizer, hashCode, externalQueue);
}
void initialize() {
disposer = get().initialize();
}
void dispose() {
if (disposer != null) {
disposer.dispose();
disposer = null;
}
}
}
}
}