Skip to content

Instantly share code, notes, and snippets.

@vorburger
Created January 30, 2014 19:40
Show Gist options
  • Save vorburger/8717137 to your computer and use it in GitHub Desktop.
Save vorburger/8717137 to your computer and use it in GitHub Desktop.
/**
* Copyright (c) 2014 Michael Vorburger and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors: Michael Vorburger - Initial API and implementation
*/
package ch.vorburger.xtext.utils;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.util.OnChangeEvictingCache.CacheAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Util to remove Xtext Parse Tree Nodes.
*
* Useful to save memory, when not needed (e.g. in a Generator, but NOT in the UI).
*
* @author Michael Vorburger
*/
public class NodesZapper {
private static final Logger logger = LoggerFactory.getLogger(NodesZapper.class);
public void removeNodeModel(Resource r) {
if (!(r instanceof XtextResource)) {
return;
}
XtextResource xr = (XtextResource) r;
// If there are errors or warnings, it's maybe safer not to remove the Node Model
// as keeping it may make it easier to find the (location of) the source of such errors.
if (!r.getErrors().isEmpty()) {
logger.warn("removeNodeModel() will *NOT* remove parse tree nodes, because there are errors on this resource: {}", r.getURI());
return;
}
if (!r.getWarnings().isEmpty()) {
logger.warn("removeNodeModel() will *NOT* remove parse tree nodes, because there are warnings on this resource: {}", r.getURI());
return;
}
TreeIterator<EObject> allContents = r.getAllContents();
while (allContents.hasNext()) {
EObject eObj = allContents.next();
// We cannot remove during the iteration over the adapters, else
// we'd get a ConcurrentModificationException, so we do it after:
List<Adapter> nodeAdaptersToRemove = new ArrayList<Adapter>(1);
for (Adapter adapter : eObj.eAdapters()) {
if (adapter instanceof INode) {
nodeAdaptersToRemove.add(adapter);
} else if (adapter instanceof CacheAdapter) {
// removing these seems more risk than gain
// In measure with >10k models, it makes only a tiny difference
// SO DO NOTHING for these (but don't log below them either)
} else {
logger.info("Found unexpected non-INode/CacheAdapter Adapter, ignoring it (not removing; still consumes memory): {}", adapter.getClass().getName());
}
}
if (!nodeAdaptersToRemove.isEmpty()) {
for (Adapter adapter : nodeAdaptersToRemove) {
eObj.eAdapters().remove(adapter);
}
nodeAdaptersToRemove.clear();
}
}
xr.setParseResult(null);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment