blob: 4b90cfb3ffb5c92a8ab38203aa028d2636d368ff [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2001, 2009 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.xml.ui.internal;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.wst.sse.core.internal.provisional.INodeAdapter;
import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.ui.internal.SSEUIMessages;
import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.CMDocumentManager;
import org.eclipse.wst.xml.core.internal.contentmodel.modelquery.ModelQuery;
import org.eclipse.wst.xml.core.internal.contentmodel.modelqueryimpl.CMDocumentLoader;
import org.eclipse.wst.xml.core.internal.contentmodel.modelqueryimpl.InferredGrammarBuildingCMDocumentLoader;
import org.eclipse.wst.xml.core.internal.modelquery.ModelQueryUtil;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
/**
* This class is used to observe changes in the DOM and perform
* occasional'scans' to deduce information. We use a delay timer mechanism to
* ensure scans are made every couple of seconds to avoid performance
* problems. Currently this class is used to keep track of referenced grammar
* uri's within the document ensure that they are loaded by the
* CMDocumentManager. We might want to generalize this class to perform other
* suplimental information gathering that is suitable for 'time delayed'
* computation (error hints etc.).
*/
// TODO: Where should this class go?
public class DOMObserver {
// An abstract adapter that ensures that the children of a new Node are
// also adapted
//
abstract class DocumentAdapter implements INodeAdapter {
public DocumentAdapter() {
}
public void connect(Document document) {
((INodeNotifier) document).addAdapter(this);
adapt(document.getDocumentElement());
}
public void dicconnect(Document document) {
((INodeNotifier) document).removeAdapter(this);
}
public void adapt(Element element) {
if (element != null) {
if (((INodeNotifier) element).getExistingAdapter(this) == null) {
((INodeNotifier) element).addAdapter(this);
for (Node child = element.getFirstChild(); child != null; child = child.getNextSibling()) {
if (child.getNodeType() == Node.ELEMENT_NODE) {
adapt((Element) child);
}
}
}
}
}
public boolean isAdapterForType(Object type) {
return type == this;
}
abstract public void notifyChanged(INodeNotifier notifier, int eventType, Object feature, Object oldValue, Object newValue, int index);
}
/**
* This class listens to the changes in the CMDocument and triggers a
* CMDocument load
*/
class MyDocumentAdapter extends DocumentAdapter {
public void notifyChanged(INodeNotifier notifier, int eventType, Object feature, Object oldValue, Object newValue, int index) {
switch (eventType) {
case INodeNotifier.ADD : {
if (newValue instanceof Element) {
// System.out.println("ADD (to " +
// ((Node)notifier).getNodeName() + ") " +
// ((Element)newValue).getNodeName() + " old " +
// oldValue);
adapt((Element) newValue);
}
break;
}
// case INodeNotifier.REMOVE:
case INodeNotifier.CHANGE :
case INodeNotifier.STRUCTURE_CHANGED :
case INodeNotifier.CONTENT_CHANGED : {
Node node = (Node) notifier;
if (node.getNodeType() == Node.ELEMENT_NODE) {
switch (eventType) {
case INodeNotifier.CHANGE :
case INodeNotifier.STRUCTURE_CHANGED : {
// structure change
invokeDelayedCMDocumentLoad();
break;
}
case INodeNotifier.CONTENT_CHANGED : {
// some content changed
break;
}
}
}
else if (node.getNodeType() == Node.DOCUMENT_NODE) {
invokeDelayedCMDocumentLoad();
}
break;
}
}
}
}
/**
* Intentionally left visible to the user
*/
class TimerJob extends Job {
public TimerJob() {
super(SSEUIMessages.LoadingReferencedGrammars);
}
public IStatus run(IProgressMonitor monitor) {
monitor.beginTask("", IProgressMonitor.UNKNOWN); //$NON-NLS-1$
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
invokeCMDocumentLoad();
monitor.done();
return Status.OK_STATUS;
}
}
private Job timer = new TimerJob();
protected Document fDocument;
protected boolean isGrammarInferenceEnabled;
/**
* If true, DOMObserver is currently disabled and not loading the content
* model
*/
private boolean fIsDisabled = false;
/**
* If true, DOMObserver is currently trying to load the content model
*/
private boolean fIsLoading = false;
public DOMObserver(IStructuredModel model) {
fDocument = (model instanceof IDOMModel) ? ((IDOMModel) model).getDocument() : null;
if (fDocument != null) {
// here we create and init an adapter that will listen to
// changes to the document and contained elements
MyDocumentAdapter adapter = new MyDocumentAdapter();
adapter.connect(fDocument);
ModelQuery modelQuery = ModelQueryUtil.getModelQuery(fDocument);
if ((modelQuery != null) && (modelQuery.getCMDocumentManager() != null)) {
CMDocumentManager cmDocumentManager = modelQuery.getCMDocumentManager();
cmDocumentManager.setPropertyEnabled(CMDocumentManager.PROPERTY_AUTO_LOAD, false);
}
// attach a dom observer adapter to the document so others have access to
// domobserver if needed
INodeAdapter domObserverAdapter = ((INodeNotifier)fDocument).getExistingAdapter(DOMObserverAdapter.class);
if (domObserverAdapter == null) {
domObserverAdapter = new DOMObserverAdapter();
((INodeNotifier)fDocument).addAdapter(domObserverAdapter);
}
((DOMObserverAdapter)domObserverAdapter).setDOMObserver(this);
}
}
public void init() {
// CS: we seem to expose an XSD initialization problem when we do this
// immediately
// very nasty... I need to revist this problem with Ed Merks
//
timer.schedule();
}
public void invokeCMDocumentLoad() {
if (fIsDisabled) return;
try {
fIsLoading = true;
ModelQuery modelQuery = ModelQueryUtil.getModelQuery(fDocument);
if ((modelQuery != null) && (modelQuery.getCMDocumentManager() != null)) {
CMDocumentLoader loader = isGrammarInferenceEnabled ? new InferredGrammarBuildingCMDocumentLoader(fDocument, modelQuery) : new CMDocumentLoader(fDocument, modelQuery);
loader.loadCMDocuments();
}
} finally {
fIsLoading = false;
}
}
public void invokeDelayedCMDocumentLoad() {
if (fIsDisabled) return;
timer.schedule(2000);
}
public void setGrammarInferenceEnabled(boolean isEnabled) {
isGrammarInferenceEnabled = isEnabled;
}
boolean setDisabled(boolean isDisabled, boolean forced) {
boolean success = true;
if (fIsDisabled != isDisabled) {
fIsDisabled = isDisabled;
if (forced) {
if (isDisabled)
success = timer.cancel();
else
invokeCMDocumentLoad();
}
}
return success;
}
boolean isLoading() {
return fIsLoading;
}
}