AadlSearchQuery.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.search;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.search.ui.ISearchQuery;
import org.eclipse.search.ui.ISearchResult;
import org.eclipse.xtext.resource.IResourceDescription;
import org.osate.aadl2.Aadl2Package;
import org.osate.aadl2.NamedElement;
import org.osate.search.AadlFinder.ResourceConsumer;
import org.osate.search.AadlFinder.Scope;

public final class AadlSearchQuery implements ISearchQuery {
	public enum SearchFor {
		CLASSIFIER {
			@Override
			public EClass declarationEClass() {
				return Aadl2Package.eINSTANCE.getClassifier();
			}

			@Override
			public String toString() {
				return "classifier";
			}
		},

		PROPERTY {
			@Override
			public EClass declarationEClass() {
				return Aadl2Package.eINSTANCE.getProperty();
			}

			@Override
			public String toString() {
				return "property";
			}
		};

		public abstract EClass declarationEClass();
	}

	public enum LimitTo {
		ALL {
			@Override
			public boolean references() {
				return true;
			}

			@Override
			public boolean declarations() {
				return true;
			}

			@Override
			public String toString() {
				return "references and declarations";
			}
		},

		REFERENCES {
			@Override
			public boolean references() {
				return true;
			}

			@Override
			public boolean declarations() {
				return false;
			}

			@Override
			public String toString() {
				return "references";
			}
		},

		DECLARATIONS {
			@Override
			public boolean references() {
				return false;
			}

			@Override
			public boolean declarations() {
				return true;
			}

			@Override
			public String toString() {
				return "declarations";
			}
		};

		public abstract boolean references();
		public abstract boolean declarations();
	}

	/**
	 * The substring to search for in the identifier name.  This is always in all uppercase because AADL identifiers
	 * are case insensitive.
	 */
	private final String substring;
	private final SearchFor searchFor;
	private final LimitTo limitTo;
	private final Scope scope;
	private final AadlSearchResult searchResult;

	public AadlSearchQuery(final String substring, final SearchFor searchFor, final LimitTo limitTo,
			final Scope scope) {
		this.substring = substring.toUpperCase();
		this.searchFor = searchFor;
		this.limitTo = limitTo;
		this.scope = scope;
		this.searchResult = new AadlSearchResult(this);
	}

	private boolean findSubstring(final String testIdentifier) {
		return testIdentifier.toUpperCase().contains(substring);
	}

	@Override
	public IStatus run(final IProgressMonitor monitor) throws OperationCanceledException {
		// TDOD Make a proper task out of this!

		// make sure the progress monitor is not null
		final IProgressMonitor nonNullmonitor = monitor == null ? new NullProgressMonitor() : monitor;
		try {
			final AadlFinder aadlFinder = AadlFinder.getInstance();
			final EClass declarationEClass = searchFor.declarationEClass();
			aadlFinder.processAllAadlFilesInScope(scope, new ResourceConsumer<IResourceDescription>() {
				private SubMonitor subMonitor = null;

				@Override
				protected void begin(final int count) {
					searchResult.setResourceSet(getResourceSet());
					subMonitor = SubMonitor.convert(nonNullmonitor, 2 * count);
				}

				@Override
				protected void skipped(final IResourceDescription rsrcDesc) {
					subMonitor.worked(2);
				}

				@Override
				protected void inScope(final IResourceDescription rsrcDesc) {
					final String fileString = rsrcDesc.getURI().lastSegment();
					subMonitor.subTask(fileString);
					if (limitTo.declarations()) {
						aadlFinder.getAllObjectsOfTypeInResource(rsrcDesc, searchFor.declarationEClass(),
								getResourceSet(), (resourceSet, objDesc) -> {
									// now check the name, which in the last segment (skip over the package names)
									final String testIdentifier = objDesc.getName().getLastSegment();
									if (findSubstring(testIdentifier)) {
										// FOUND A MATCH
										searchResult.addFoundDeclaration(resourceSet, objDesc);
									}
								});
					}
					subMonitor.worked(1);

					if (nonNullmonitor.isCanceled()) {
						throw new OperationCanceledException();
					}

					if (limitTo.references()) {
						aadlFinder.getAllReferencesToTypeInResource(rsrcDesc, getResourceSet(),
								(resourceSet, refDesc) -> {
							final URI targetURI = refDesc.getTargetEObjectUri();
							final EObject eObj = getResourceSet().getEObject(targetURI, true);
									if (eObj != null) { // target object might be null if the file being searched has errors
										// filter by eClass
										if (declarationEClass.isSuperTypeOf(eObj.eClass())) {
											// filter by name
											if (eObj instanceof NamedElement
													&& findSubstring(((NamedElement) eObj).getName())) {
												// FOUND A MATCH
												searchResult.addFoundReference(resourceSet, refDesc);
											}
										}
									}
								}, subMonitor.split(1));
					} else {
						subMonitor.worked(1);
					}

					if (nonNullmonitor.isCanceled()) {
						throw new OperationCanceledException();
					}
				}
			});
		} finally {
			nonNullmonitor.done();
		}
		return Status.OK_STATUS;
	}

	@Override
	public String getLabel() {
		return "\"" + substring + "\" in " + searchFor + " " + limitTo;
	}

	@Override
	public boolean canRerun() {
		return true;
	}

	@Override
	public boolean canRunInBackground() {
		return true;
	}

	@Override
	public ISearchResult getSearchResult() {
		return searchResult;
	}

	public LimitTo getLimitTo() {
		return limitTo;
	}
}