DefaultReferenceService.java
/**
* Copyright (c) 2004-2025 Carnegie Mellon University and others. (see Contributors file).
* All Rights Reserved.
*
* NO WARRANTY. ALL MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE
* OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT
* MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
*
* This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
* SPDX-License-Identifier: EPL-2.0
*
* Created, in part, with funding and support from the United States Government. (see Acknowledgments file).
*
* This program includes and/or can make use of certain third party source code, object code, documentation and other
* files ("Third Party Software"). The Third Party Software that is used by this program is dependent upon your system
* configuration. By using this program, You agree to comply with any and all relevant Third Party Software terms and
* conditions contained in any such Third Party Software or separate license file distributed with such Third Party
* Software. The parties who own the Third Party Software ("Third Party Licensors") are intended third party benefici-
* aries to this license with respect to the terms applicable to their Third Party Software. Third Party Software li-
* censes only apply to the Third Party Software and not any other portion of this program or this program as a whole.
*/
package org.osate.ge.internal.services.impl;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.WeakHashMap;
import java.util.stream.Collectors;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.ui.statushandlers.StatusManager;
import org.osate.ge.CanonicalBusinessObjectReference;
import org.osate.ge.RelativeBusinessObjectReference;
import org.osate.ge.businessobjecthandling.BusinessObjectHandler;
import org.osate.ge.businessobjecthandling.ReferenceContext;
import org.osate.ge.internal.Activator;
import org.osate.ge.internal.businessobjecthandlers.BusinessObjectHandlerProvider;
import org.osate.ge.internal.services.ExtensionRegistryService;
import org.osate.ge.internal.services.ProjectReferenceService;
import org.osate.ge.internal.services.ReferenceService;
import org.osate.ge.referencehandling.GetCanonicalReferenceLabelContext;
import org.osate.ge.referencehandling.GetRelativeReferenceLabelContext;
import org.osate.ge.referencehandling.ReferenceLabelProvider;
import org.osate.ge.referencehandling.internal.ReferenceLabelProviderRegistry;
/**
* {@link ReferenceService} implementation
*
*/
public class DefaultReferenceService implements ReferenceService {
private final static ReferenceQueue<ProjectReferenceService> serviceReferenceQueue = new ReferenceQueue<>();
// Start a thread which will monitor the service reference queue and call the dispose method of the reference.
static {
final Thread referenceDisposalThread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
ProjectReferenceServiceReference ref;
try {
while ((ref = (ProjectReferenceServiceReference) serviceReferenceQueue.remove()) != null) {
ref.dispose();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
referenceDisposalThread.setDaemon(true);
referenceDisposalThread.start();
}
private static class ProjectReferenceServiceReference extends WeakReference<DefaultProjectReferenceService> {
private final Runnable cleanup;
public ProjectReferenceServiceReference(final DefaultProjectReferenceService service,
final ReferenceQueue<? super ProjectReferenceService> queue) {
super(service, queue);
this.cleanup = service.getCleanupRunnable();
}
public void dispose() {
cleanup.run();
}
}
/**
* Context function which instantiates this service
*/
public static class ContextFunction extends SimpleServiceContextFunction<ReferenceService> {
@Override
public ReferenceService createService(final IEclipseContext context) {
return new DefaultReferenceService(context.get(ExtensionRegistryService.class));
}
}
private final ReferenceLabelProviderRegistry labelProviderRegistry;
private final WeakHashMap<IProject, ProjectReferenceServiceReference> projectToProjectReferenceService = new WeakHashMap<>();
private final BusinessObjectHandlerProvider bohProvider;
/**
* Creates a new instance. In most cases, it is not appropriate to use this. The registered {@link ReferenceService} should be used.
* @param bohProvider an object which provides the registered business object handlers
*/
public DefaultReferenceService(
final BusinessObjectHandlerProvider bohProvider) {
this.labelProviderRegistry = new ReferenceLabelProviderRegistry(Platform.getExtensionRegistry());
this.bohProvider = Objects.requireNonNull(bohProvider, "bohProvider must not be null");
}
@Override
public CanonicalBusinessObjectReference getCanonicalReference(final Object bo) {
Objects.requireNonNull(bo, "bo must not be null");
final BusinessObjectHandler boh = bohProvider.getApplicableBusinessObjectHandler(bo);
if (boh == null) {
return null;
}
try {
return boh.getCanonicalReference(new ReferenceContext(bo, this));
} catch (RuntimeException ex) {
StatusManager.getManager().handle(
new Status(IStatus.ERROR, Activator.PLUGIN_ID,
"Error retrieving canonical reference for " + bo.getClass().getCanonicalName()
+ ".",
ex),
StatusManager.LOG | StatusManager.SHOW);
return null;
}
}
@Override
public RelativeBusinessObjectReference getRelativeReference(final Object bo) {
Objects.requireNonNull(bo, "bo must not be null");
final BusinessObjectHandler boh = bohProvider.getApplicableBusinessObjectHandler(bo);
if (boh == null) {
return null;
}
try {
return Objects.requireNonNull(boh.getRelativeReference(new ReferenceContext(bo, this)),
() -> "Business object handlers must not return a null relative reference. Null reference returned by '"
+ boh.getClass().getCanonicalName() + "'");
} catch (RuntimeException ex) {
StatusManager.getManager().handle(
new Status(IStatus.ERROR, Activator.PLUGIN_ID,
"Error retrieving relative reference for " + bo.getClass().getCanonicalName() + ".",
ex),
StatusManager.LOG | StatusManager.SHOW);
return null;
}
}
@Override
public ProjectReferenceService getProjectReferenceService(final IProject project) {
ProjectReferenceServiceReference serviceRef = projectToProjectReferenceService.get(project);
DefaultProjectReferenceService result = serviceRef == null ? null : serviceRef.get();
if (result == null) {
// Create the reference service
result = new DefaultProjectReferenceService(this, this, project);
// Create the reference
final ProjectReferenceServiceReference newServiceRef = new ProjectReferenceServiceReference(result,
serviceReferenceQueue);
projectToProjectReferenceService.put(project, newServiceRef);
}
return result;
}
@Override
public String getLabel(final CanonicalBusinessObjectReference ref) {
Objects.requireNonNull(ref, "ref must not be null");
final GetCanonicalReferenceLabelContext ctx = new GetCanonicalReferenceLabelContext(ref);
for (final ReferenceLabelProvider provider : labelProviderRegistry.getProviders()) {
final Optional<String> label = provider.getCanonicalReferenceLabel(ctx);
if (label.isPresent()) {
return label.get();
}
}
return getFallbackLabel(ref.getSegments());
}
@Override
public String getLabel(final RelativeBusinessObjectReference ref) {
Objects.requireNonNull(ref, "ref must not be null");
final GetRelativeReferenceLabelContext ctx = new GetRelativeReferenceLabelContext(ref);
for (final ReferenceLabelProvider provider : labelProviderRegistry.getProviders()) {
final Optional<String> label = provider.getRelativeReferenceLabel(ctx);
if (label.isPresent()) {
return label.get();
}
}
return getFallbackLabel(ref.getSegments());
}
/**
* Returns a label created by joining the specified segments. Intended for use when a label provider is not available.
* @param segments the segments to join
* @return the label.
*/
private final String getFallbackLabel(final List<String> segments) {
return segments.stream().collect(Collectors.joining(" "));
}
}