SandboxCommonPlugin.java
/*******************************************************************************
* Copyright (c) 2026 Carsten Hammer.
*
* 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:
* Carsten Hammer
*******************************************************************************/
package org.sandbox.jdt.triggerpattern.internal;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
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.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.ILog;
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
import org.osgi.framework.BundleContext;
/**
* Bundle lifecycle for Eclipse-specific TriggerPattern infrastructure in
* {@code sandbox_common}.
*
* <p>The listener keeps project-local {@code .sandbox-hint} and NetBeans
* {@code .hint} rules coherent with the workspace. A rule created or edited in
* the same Eclipse session must be visible to both Hint File Quick Assist and
* the Hint File cleanup without requiring a workbench restart.</p>
*/
public final class SandboxCommonPlugin extends Plugin {
private IResourceChangeListener hintFileListener;
@Override
public void start(BundleContext context) throws Exception {
super.start(context);
hintFileListener= SandboxCommonPlugin::handleResourceChange;
ResourcesPlugin.getWorkspace().addResourceChangeListener(hintFileListener,
IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_DELETE);
}
@Override
public void stop(BundleContext context) throws Exception {
if (hintFileListener != null) {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(hintFileListener);
hintFileListener= null;
}
super.stop(context);
}
private static void handleResourceChange(IResourceChangeEvent event) {
IResource eventResource= event.getResource();
if ((event.getType() == IResourceChangeEvent.PRE_CLOSE
|| event.getType() == IResourceChangeEvent.PRE_DELETE)
&& eventResource instanceof IProject project) {
invalidateProjectRules(project);
return;
}
IResourceDelta delta= event.getDelta();
if (delta == null) {
return;
}
Set<IProject> affectedProjects= new HashSet<>();
try {
delta.accept(resourceDelta -> {
IResource resource= resourceDelta.getResource();
if (resource instanceof IFile file && isHintFile(file.getName())) {
affectedProjects.add(file.getProject());
}
addMovedProject(affectedProjects, resourceDelta.getMovedFromPath());
addMovedProject(affectedProjects, resourceDelta.getMovedToPath());
return true;
});
} catch (CoreException e) {
ILog log= PlatformLogHolder.LOG;
log.log(Status.warning("Failed to inspect workspace changes for Hint DSL files", e)); //$NON-NLS-1$
return;
}
affectedProjects.forEach(SandboxCommonPlugin::invalidateProjectRules);
}
private static void addMovedProject(Set<IProject> projects, IPath path) {
if (path == null || path.segmentCount() < 2 || !isHintFile(path.lastSegment())) {
return;
}
projects.add(ResourcesPlugin.getWorkspace().getRoot().getProject(path.segment(0)));
}
private static boolean isHintFile(String name) {
return name != null && (name.endsWith(".sandbox-hint") || name.endsWith(".hint")); //$NON-NLS-1$ //$NON-NLS-2$
}
private static void invalidateProjectRules(IProject project) {
if (project == null) {
return;
}
HintFileRegistry registry= HintFileRegistry.getInstance();
String projectPrefix= "project:" + project.getName() + ":"; //$NON-NLS-1$ //$NON-NLS-2$
for (String id : registry.getRegisteredIds()) {
if (id.startsWith(projectPrefix)) {
registry.unregister(id);
EmbeddedGuardRegistrar.unregisterGuards(id);
EmbeddedFixExecutor.unregisterFixes(id);
}
}
registry.invalidateProject(project);
}
/**
* Defers logger lookup until a workspace change actually needs to report an
* error, avoiding unnecessary plugin activation work during normal startup.
*/
private static final class PlatformLogHolder {
private static final ILog LOG= org.eclipse.core.runtime.Platform.getLog(SandboxCommonPlugin.class);
private PlatformLogHolder() {
}
}
}