blob: a9512d20fb68d650a190e1c52e900aececc47034 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2004 IBM Corporation 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:
* IBM Corporation - initial API and implementation
* Jens Lukowski/Innoopract - initial renaming/restructuring
*
*******************************************************************************/
package org.eclipse.wst.sse.core.internal.model;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocumentExtension3;
import org.eclipse.wst.sse.core.internal.Logger;
import org.eclipse.wst.sse.core.internal.document.IDocumentLoader;
import org.eclipse.wst.sse.core.internal.encoding.EncodingMemento;
import org.eclipse.wst.sse.core.internal.encoding.EncodingRule;
import org.eclipse.wst.sse.core.internal.ltk.modelhandler.IModelHandler;
import org.eclipse.wst.sse.core.internal.ltk.parser.BlockMarker;
import org.eclipse.wst.sse.core.internal.ltk.parser.BlockTagParser;
import org.eclipse.wst.sse.core.internal.ltk.parser.RegionParser;
import org.eclipse.wst.sse.core.internal.ltk.parser.StructuredDocumentRegionHandler;
import org.eclipse.wst.sse.core.internal.ltk.parser.StructuredDocumentRegionHandlerExtension;
import org.eclipse.wst.sse.core.internal.ltk.parser.StructuredDocumentRegionParser;
import org.eclipse.wst.sse.core.internal.ltk.parser.StructuredDocumentRegionParserExtension;
import org.eclipse.wst.sse.core.internal.provisional.IModelLoader;
import org.eclipse.wst.sse.core.internal.provisional.INodeAdapterFactory;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.document.IEncodedDocument;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredPartitioning;
import org.eclipse.wst.sse.core.internal.text.BasicStructuredDocument;
import org.eclipse.wst.sse.core.internal.text.rules.StructuredTextPartitioner;
import org.eclipse.wst.sse.core.internal.util.Assert;
/**
* This class reads a file and creates an Structured Model.
*/
public abstract class AbstractModelLoader implements IModelLoader {
protected static final int encodingNameSearchLimit = 1000;
private static long computeMem() {
for (int i = 0; i < 5; i++) {
System.gc();
System.runFinalization();
}
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
}
private boolean DEBUG = false;
protected IDocumentLoader documentLoaderInstance;
/**
* AbstractLoader constructor also initializes encoding converter/mapper
*/
public AbstractModelLoader() {
super();
}
protected void addFactories(IStructuredModel model, List factoryList) {
Assert.isNotNull(model);
FactoryRegistry registry = model.getFactoryRegistry();
Assert.isNotNull(registry, "IStructuredModel " + model.getId() + " has a null FactoryRegistry"); //$NON-NLS-1$ //$NON-NLS-2$
if (factoryList != null) {
Iterator iterator = factoryList.iterator();
while (iterator.hasNext()) {
INodeAdapterFactory factory = (INodeAdapterFactory) iterator.next();
registry.addFactory(factory);
}
}
}
/**
* This method should perform all the model initialization required before
* it contains content, namely, it should call newModel, the
* createNewStructuredDocument(), then add those adapter factories which
* must be set before content is applied. This method should be called by
* "load" method. (this is tentative API)
*/
public IStructuredModel createModel() {
documentLoaderInstance = null;
IStructuredModel model = newModel();
IEncodedDocument structuredDocument = getDocumentLoader().createNewStructuredDocument();
if (structuredDocument instanceof IStructuredDocument) {
model.setStructuredDocument((IStructuredDocument) structuredDocument);
addFactories(model, getAdapterFactories());
//
initEmbeddedTypePre(model);
initEmbeddedTypePost(model);
// For types with propagating adapters, its important
// that the propagating adapter be in place before the contents
// are set.
preLoadAdapt(model);
}
return model;
}
public IStructuredModel createModel(IStructuredDocument structuredDocument, String baseLocation, IModelHandler handler) {
documentLoaderInstance = null;
IStructuredModel model = newModel();
model.setBaseLocation(baseLocation);
// handler must be set early, incase a re-init is
// required during creation.
model.setModelHandler(handler);
addFactories(model, getAdapterFactories());
// For types with propagating adapters, it's important
// that the propagating adapter be in place before the contents
// are set.
preLoadAdapt(model);
initEmbeddedTypePre(model);
model.setStructuredDocument(structuredDocument);
//
initEmbeddedTypePost(model);
return model;
}
/**
* This method is used for cloning models.
*/
public IStructuredModel createModel(IStructuredModel oldModel) {
documentLoaderInstance = null;
IStructuredModel newModel = newModel();
IStructuredDocument oldStructuredDocument = oldModel.getStructuredDocument();
IStructuredDocument newStructuredDocument = oldStructuredDocument.newInstance();
newModel.setStructuredDocument(newStructuredDocument);
// NOTE: we DO NOT simply add the standard ones to the new model
// addFactories(newModel, getAdapterFactories());
// Now, we take the opportunity to add Factories from the oldModel's
// registry to the new model's registry .. if they do not already
// exist there.
duplicateFactoryRegistry(newModel, oldModel);
if (newModel instanceof AbstractStructuredModel) {
((AbstractStructuredModel) newModel).setContentTypeIdentifier(oldModel.getContentTypeIdentifier());
}
// addFactories(newModel, oldModel);
initEmbeddedType(oldModel, newModel);
// For types with propagating adapters, its important
// that the propagating adapter be in place before the contents
// are set.
preLoadAdapt(newModel);
return newModel;
}
private void duplicateFactoryRegistry(IStructuredModel newModel, IStructuredModel oldModel) {
List oldAdapterFactories = oldModel.getFactoryRegistry().getFactories();
List newAdapterFactories = new ArrayList();
Iterator oldListIterator = oldAdapterFactories.iterator();
while (oldListIterator.hasNext()) {
INodeAdapterFactory oldAdapterFactory = (INodeAdapterFactory) oldListIterator.next();
// now "clone" the adapterfactory
newAdapterFactories.add(oldAdapterFactory.copy());
}
// now that we have the "cloned" list, add to new model
addFactories(newModel, newAdapterFactories);
}
/**
* This method must return those factories which must be attached to the
* structuredModel before content is applied.
*/
public List getAdapterFactories() {
// abstract method returns none
return new ArrayList(0);
}
abstract public IDocumentLoader getDocumentLoader();
/**
* Method initEmbeddedType. Nothing to do here in super class.
*
* @param model
*/
protected void initEmbeddedTypePre(IStructuredModel model) {
}
protected void initEmbeddedTypePost(IStructuredModel model) {
}
/**
* Method initEmbeddedType. Nothing to do here in super class.
*
* @param oldModel
* @param newModel
*/
protected void initEmbeddedType(IStructuredModel oldModel, IStructuredModel newModel) {
}
public void load(IFile file, IStructuredModel model) throws IOException, CoreException {
IEncodedDocument structuredDocument = model.getStructuredDocument();
if (file == null)
structuredDocument = getDocumentLoader().createNewStructuredDocument();
else
structuredDocument = getDocumentLoader().createNewStructuredDocument(file);
// TODO: need to straighten out IEncodedDocument mess
if (structuredDocument instanceof IStructuredDocument)
transformInstance(model.getStructuredDocument(), (IStructuredDocument) structuredDocument);
else
model.getStructuredDocument().set(structuredDocument.get());
// original hack
// model.setStructuredDocument((IStructuredDocument)
// structuredDocument);
// ((IStructuredDocument) structuredDocument).fireNewDocument(this);
documentLoaderInstance = null;
// technicq of future
// model.setStructuredDocument((IStructuredDocument)
// structuredDocument);
// documentLoaderInstance = null;
}
public void load(InputStream inputStream, IStructuredModel model, EncodingRule encodingRule) throws UnsupportedEncodingException, java.io.IOException {
// note we don't open the stream, so we don't close it
IEncodedDocument structuredDocument = model.getStructuredDocument();
if (inputStream == null) {
structuredDocument = getDocumentLoader().createNewStructuredDocument();
}
else {
// assume's model has been initialized already with base location
structuredDocument = getDocumentLoader().createNewStructuredDocument(model.getBaseLocation(), inputStream, encodingRule);
// TODO: model's not designed for this!
// we want to move to this "set" method, but the 'fire' was needed
// as
// a work around for strucutredModel not handling 'set' right, but
// that 'fireNewDocument' method was causing unbalance
// "aboutToChange" and "changed"
// events.
// model.setStructuredDocument((IStructuredDocument)
// structuredDocument);
// ((IStructuredDocument)
// structuredDocument).fireNewDocument(this);
model.getStructuredDocument().set(structuredDocument.get());
}
documentLoaderInstance = null;
}
/**
* deprecated -- use EncodingRule form
*/
synchronized public void load(InputStream inputStream, IStructuredModel model, String encodingName, String lineDelimiter) throws UnsupportedEncodingException, java.io.IOException {
// note we don't open the stream, so we don't close it
// TEMP work around to maintain previous function,
// until everyone can change to EncodingRule.FORCE_DEFAULT
if (encodingName != null && encodingName.trim().length() == 0) {
// redirect to new method
load(inputStream, model, EncodingRule.FORCE_DEFAULT);
}
else {
load(inputStream, model, EncodingRule.CONTENT_BASED);
}
}
public void load(String filename, InputStream inputStream, IStructuredModel model, String junk, String dummy) throws UnsupportedEncodingException, java.io.IOException {
long memoryUsed = 0;
if (DEBUG) {
memoryUsed = computeMem();
System.out.println("measuring heap memory for " + filename); //$NON-NLS-1$
// System.out.println("heap memory used before load: " +
// memoryUsed);
}
// during an initial load, we expect the olddocument to be empty
// during re-load, however, it would be full.
IEncodedDocument newstructuredDocument = null;
IEncodedDocument oldStructuredDocument = model.getStructuredDocument();
// get new document
if (inputStream == null) {
newstructuredDocument = getDocumentLoader().createNewStructuredDocument();
}
else {
newstructuredDocument = getDocumentLoader().createNewStructuredDocument(filename, inputStream);
}
if (DEBUG) {
long memoryAtEnd = computeMem();
// System.out.println("heap memory used after loading new
// document: " + memoryAtEnd);
System.out.println(" heap memory implied used by document: " + (memoryAtEnd - memoryUsed)); //$NON-NLS-1$
}
// TODO: need to straighten out IEncodedDocument mess
if (newstructuredDocument instanceof IStructuredDocument) {
transformInstance((IStructuredDocument) oldStructuredDocument, (IStructuredDocument) newstructuredDocument);
}
else {
// we don't really expect this case, just included for safety
oldStructuredDocument.set(newstructuredDocument.get());
}
// original hack
// model.setStructuredDocument((IStructuredDocument)
// structuredDocument);
// ((IStructuredDocument) structuredDocument).fireNewDocument(this);
documentLoaderInstance = null;
// technicq of future
// model.setStructuredDocument((IStructuredDocument)
// structuredDocument);
// documentLoaderInstance = null;
if (DEBUG) {
long memoryAtEnd = computeMem();
// System.out.println("heap memory used after setting to model: "
// + memoryAtEnd);
System.out.println(" heap memory implied used by document and model: " + (memoryAtEnd - memoryUsed)); //$NON-NLS-1$
}
}
/**
* required by interface, being declared here abstractly just as another
* reminder.
*/
abstract public IStructuredModel newModel();
/**
* There's nothing to do here in abstract class for initializing adapters.
* Subclasses can and should override this method and provide proper
* intialization (For example, to get DOM document and 'getAdapter' on it,
* so that the first node/notifier has the adapter on it.)
*/
protected void preLoadAdapt(IStructuredModel structuredModel) {
}
/**
* Normally, here in the abstact class, there's nothing to do, but we will
* reset text, since this MIGHT end up being called to recover from error
* conditions (e.g. IStructuredDocument exceptions) And, can be called by
* subclasses.
*/
public IStructuredModel reinitialize(IStructuredModel model) {
// Note: the "minimumization" routines
// of 'replaceText' allow many old nodes to pass through, when
// really its assumed they are created anew.
// so we need to use 'setText' (I think "setText' ends up
// throwing a 'newModel' event though, that may have some
// implications.
model.getStructuredDocument().setText(this, model.getStructuredDocument().get());
return model;
}
/**
* This method gets a fresh copy of the data, and repopulates the models
* ... by a call to setText on the structuredDocument. This method is
* needed in some cases where clients are sharing a model and then changes
* canceled. Say for example, one editor and several "displays" are
* sharing a model, if the editor is closed without saving changes, then
* the displays still need a model, but they should revert to the original
* unsaved version.
*/
synchronized public void reload(InputStream inputStream, IStructuredModel structuredModel) {
documentLoaderInstance = null;
try {
// temp solution ... we should be able to do better (more
// efficient) in future.
// Adapters will (probably) need to be sensitive to the fact that
// the document instance changed
// (by being life cycle listeners)
load(inputStream, structuredModel, EncodingRule.CONTENT_BASED);
// // Note: we apparently read the data (and encoding) correctly
// // before, we just need to make sure we followed the same rule
// as
// // before.
// EncodingMemento previousMemento =
// structuredModel.getStructuredDocument().getEncodingMemento();
// EncodingRule previousRule = previousMemento.getEncodingRule();
// //IFile file = ResourceUtil.getFileFor(structuredModel);
// // Note: there's opportunity here for some odd behavior, if the
// // settings have changed from the first load to the reload.
// But,
// // hopefully,
// // will result in the intended behavior.
// Reader allTextReader =
// getDocumentLoader().readInputStream(inputStream, previousRule);
//
// // TODO: avoid use of String instance
// getDocumentLoader().reload(structuredModel.getStructuredDocument(),
// allTextReader);
// // and now "reset" encoding memento to keep it current with the
// // one
// // that was just determined.
// structuredModel.getStructuredDocument().setEncodingMemento(getDocumentLoader().getEncodingMemento());
// structuredModel.setDirtyState(false);
// StructuredTextUndoManager undoMgr =
// structuredModel.getUndoManager();
// if (undoMgr != null) {
// undoMgr.reset();
// }
}
catch (UnsupportedEncodingException e) {
// couldn't happen. The program has apparently
// read the model once, and there'd be no reason the encoding
// could not be used again.
Logger.logException("Warning: XMLLoader::reload. This exception really should not have happened!! But will attemp to continue after dumping stack trace", e); //$NON-NLS-1$
throw new Error("Program Error", e); //$NON-NLS-1$
}
catch (IOException e) {
// couldn't happen. The program has apparently
// read the model once, and there'd be no (common) reason it
// couldn't be loaded again.
Logger.logException("Warning: XMLLoader::reload. This exception really should not have happened!! But will attemp to continue after dumping stack trace", e); //$NON-NLS-1$
throw new Error("Program Error", e); //$NON-NLS-1$
}
}
/**
* this work is done better elsewhere, but done here for this version to
* reduce changes. especially since the need for it should go away once we
* no longer need to re-use old document instance.
*/
private void transformInstance(IStructuredDocument oldInstance, IStructuredDocument newInstance) {
/**
* https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=4920
*
* JSP taglib support broken, correct by duplicating extended setup
* information (BlockTagParser extension,
* StructuredDocumentRegionParser extensions)
*/
RegionParser oldParser = oldInstance.getParser();
RegionParser newParser = newInstance.getParser().newInstance();
// Register all of the old StructuredDocumentRegionHandlers on the new
// parser
if (oldParser instanceof StructuredDocumentRegionParserExtension && newParser instanceof StructuredDocumentRegionParserExtension) {
List oldHandlers = ((StructuredDocumentRegionParserExtension) oldParser).getStructuredDocumentRegionHandlers();
for (int i = 0; i < oldHandlers.size(); i++) {
StructuredDocumentRegionHandler handler = ((StructuredDocumentRegionHandler) oldHandlers.get(i));
if (handler instanceof StructuredDocumentRegionHandlerExtension) {
/**
* Skip the transferring here, the handler will do this
* after everything else but the source is transferred.
*/
}
else {
((StructuredDocumentRegionParser) oldParser).removeStructuredDocumentRegionHandler(handler);
((StructuredDocumentRegionParser) newParser).addStructuredDocumentRegionHandler(handler);
handler.resetNodes();
}
}
}
// Add any global BlockMarkers to the new parser
if (oldParser instanceof BlockTagParser && newParser instanceof BlockTagParser) {
List oldBlockMarkers = ((BlockTagParser) oldParser).getBlockMarkers();
for (int i = 0; i < oldBlockMarkers.size(); i++) {
BlockMarker blockMarker = ((BlockMarker) oldBlockMarkers.get(i));
if (blockMarker.isGlobal()) {
((BlockTagParser) newParser).addBlockMarker(blockMarker);
}
}
}
((BasicStructuredDocument) oldInstance).setParser(newParser);
((BasicStructuredDocument) oldInstance).setReParser(newInstance.getReParser().newInstance());
if (newInstance.getDocumentPartitioner() instanceof StructuredTextPartitioner) {
StructuredTextPartitioner partitioner = null;
if (oldInstance instanceof IDocumentExtension3 && newInstance instanceof IDocumentExtension3) {
partitioner = ((StructuredTextPartitioner) ((IDocumentExtension3) newInstance).getDocumentPartitioner(IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING));
if (partitioner != null) {
partitioner = (StructuredTextPartitioner) partitioner.newInstance();
}
((IDocumentExtension3) oldInstance).setDocumentPartitioner(IStructuredPartitioning.DEFAULT_STRUCTURED_PARTITIONING, partitioner);
}
if (partitioner == null) {
partitioner = (StructuredTextPartitioner) ((StructuredTextPartitioner) newInstance.getDocumentPartitioner()).newInstance();
oldInstance.setDocumentPartitioner(partitioner);
}
if (partitioner != null) {
partitioner.connect(oldInstance);
}
}
String existingLineDelimiter = null;
try {
existingLineDelimiter = newInstance.getLineDelimiter(0);
}
catch (BadLocationException e) {
// if empty file, assume platform default
// TODO: should be using user set preference, per content type?
existingLineDelimiter = System.getProperty("line.separator"); //$NON-NLS-1$
}
oldInstance.setLineDelimiter(existingLineDelimiter); //$NON-NLS-1$);
if (newInstance.getEncodingMemento() != null) {
oldInstance.setEncodingMemento((EncodingMemento) newInstance.getEncodingMemento().clone());
}
/**
* https://w3.opensource.ibm.com/bugzilla/show_bug.cgi?id=4920
*
* JSP taglib support broken, correct by duplicating extended setup
* information (BlockTagParser extension,
* StructuredDocumentRegionParser extensions)
*/
if (oldParser instanceof StructuredDocumentRegionParserExtension && newParser instanceof StructuredDocumentRegionParserExtension) {
List oldHandlers = ((StructuredDocumentRegionParserExtension) oldParser).getStructuredDocumentRegionHandlers();
for (int i = 0; i < oldHandlers.size(); i++) {
StructuredDocumentRegionHandler handler = ((StructuredDocumentRegionHandler) oldHandlers.get(i));
if (handler instanceof StructuredDocumentRegionHandlerExtension) {
StructuredDocumentRegionHandlerExtension handlerExtension = (StructuredDocumentRegionHandlerExtension) handler;
handlerExtension.setStructuredDocument(oldInstance);
}
}
}
String holdString = newInstance.get();
newInstance = null;
oldInstance.set(holdString);
}
}