InstanceReferenceResolver.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.aadl2.internal;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.e4.core.contexts.EclipseContextFactory;
import org.eclipse.emf.common.util.URI;
import org.osate.aadl2.instance.SystemInstance;
import org.osate.ge.internal.services.SystemInstanceLoadingService;
import org.osate.ge.referencehandling.CreateReferenceResolverFactoryContext;
import org.osate.ge.referencehandling.ReferenceResolver;
import org.osate.ge.referencehandling.ReferenceResolverFactory;
import org.osate.ge.referencehandling.ResolveContext;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;

public class InstanceReferenceResolver implements ReferenceResolver {
	public static class Factory implements ReferenceResolverFactory {
		@Override
		public ReferenceResolver create(final CreateReferenceResolverFactoryContext ctx) {
			final Bundle bundle = FrameworkUtil.getBundle(Factory.class);
			final SystemInstanceLoadingService systemInstanceLoader = Objects
					.requireNonNull(
							EclipseContextFactory.getServiceContext(bundle.getBundleContext())
							.get(SystemInstanceLoadingService.class),
							"Unable to get system instance loading service");

			return new InstanceReferenceResolver(systemInstanceLoader);
		}
	}

	private final SystemInstanceLoadingService systemInstanceLoader;
	private final Map<String, SystemInstanceInfo> keyToSystemInstanceInfoMap = new HashMap<String, SystemInstanceInfo>();
	private final IResourceChangeListener resourceChangeListener = new IResourceChangeListener() {
		private IResourceDeltaVisitor deltaVisitor = delta -> {
			final IResource resource = delta.getResource();
			if(resource.getType() == IResource.FILE) {
				// Check if the file matches the resource of one of the system instances
				synchronized(keyToSystemInstanceInfoMap) {
					final URI changedUri = URI.createPlatformResourceURI(delta.getResource().getFullPath().toString(), true);
					String systemInstanceKey = null;
					for(final Entry<String, SystemInstanceInfo> mapEntry : keyToSystemInstanceInfoMap.entrySet()) {
						if(changedUri.equals(mapEntry.getValue().systemInstance.eResource().getURI())) {
							systemInstanceKey = mapEntry.getKey();
							break;
						}
					}

					// Remove the entry from the map
					if(systemInstanceKey != null) {
						keyToSystemInstanceInfoMap.remove(systemInstanceKey);
						return false;
					}
				}
			}

			return true;
		};

		@Override
		public void resourceChanged(final IResourceChangeEvent event) {
			if(event.getType() == IResourceChangeEvent.POST_CHANGE) {
				final IResourceDelta delta = event.getDelta();

				try {
					// Process the resource delta
					delta.accept(deltaVisitor);
				} catch (final CoreException e) {
					throw new RuntimeException(e);
				}
			}
		}
	};

	/**
	 * Stores mappings for elements inside of a system instance
	 *
	 */
	private class SystemInstanceInfo {
		public final SystemInstance systemInstance;

		public SystemInstanceInfo(final SystemInstance systemInstance) {
			this.systemInstance = systemInstance;
		}
	}

	public InstanceReferenceResolver(
			final SystemInstanceLoadingService systemInstanceLoader) {
		this.systemInstanceLoader = Objects.requireNonNull(systemInstanceLoader, "systemInstanceLoader must not be null");

		ResourcesPlugin.getWorkspace().addResourceChangeListener(resourceChangeListener);
	}

	@Override
	public void close() {
		// Remove the resource listener
		ResourcesPlugin.getWorkspace().removeResourceChangeListener(resourceChangeListener);

		// Clear the map
		synchronized(keyToSystemInstanceInfoMap) {
			keyToSystemInstanceInfoMap.clear();
		}
	}

	@Override
	public Optional<Object> resolve(final ResolveContext ctx) {
		final List<String> refSegs = ctx.getReference().getSegments();
		if (refSegs.size() < 3) {
			return Optional.empty();
		}

		if (!AadlReferenceUtil.INSTANCE_ID.equals(refSegs.get(0))) {
			return Optional.empty();
		}

		final String type = refSegs.get(1);
		final String systemInstanceKey = refSegs.get(2);
		final SystemInstanceInfo siInfo = getSystemInstanceInfo(systemInstanceKey);
		if(siInfo != null) {
			if (type.equals(AadlReferenceUtil.SYSTEM_INSTANCE_KEY)) {
				return Optional.of(siInfo.systemInstance);
			}
		}

		return Optional.empty();
	}

	private SystemInstanceInfo getSystemInstanceInfo(final String key) {
		synchronized(keyToSystemInstanceInfoMap) {
			SystemInstanceInfo siInfo = keyToSystemInstanceInfoMap.get(key);

			// If it wasn't loaded previously, load the system instance
			if(siInfo == null) {
				final SystemInstance si = systemInstanceLoader.loadSystemInstance(key);
				if(si != null) {
					siInfo = new SystemInstanceInfo(si);

					// Add it the map
					keyToSystemInstanceInfoMap.put(key, siInfo);
				}
			}

			return siInfo;
		}
	}
}