blob: 0952d2997c3c5eeed8bc6f49b31eee48982d93b5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.j2ee.sun.ddloaders;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.Reader;
import java.lang.ref.WeakReference;
import java.util.Date;
import javax.swing.SwingUtilities;
import org.netbeans.modules.j2ee.sun.dd.api.RootInterface;
import org.netbeans.modules.j2ee.sun.dd.impl.common.DDProviderDataObject;
import org.netbeans.modules.j2ee.sun.ddloaders.api.Transaction;
import org.netbeans.modules.j2ee.sun.ddloaders.api.TransactionSupport;
import org.netbeans.modules.xml.multiview.XmlMultiViewDataObject;
import org.netbeans.modules.xml.multiview.XmlMultiViewDataSynchronizer;
import org.openide.DialogDisplayer;
import org.openide.ErrorManager;
import org.openide.NotifyDescriptor;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileLock;
import org.openide.loaders.DataObjectExistsException;
import org.openide.loaders.MultiFileLoader;
import org.openide.util.NbBundle;
/**
* @author pfiala
* @author Peter Williams
*/
public abstract class DDMultiViewDataObject extends XmlMultiViewDataObject
implements DDProviderDataObject, TransactionSupport {
private WeakReference transactionReference = null;
private static final int HANDLE_UNPARSABLE_TIMEOUT = 2000;
private DDMultiViewDataObject.ModelSynchronizer modelSynchronizer;
public DDMultiViewDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException {
super(pf, loader);
modelSynchronizer = new ModelSynchronizer(this);
}
public void modelUpdatedFromUI() {
modelSynchronizer.requestUpdateData();
}
public XmlMultiViewDataSynchronizer getModelSynchronizer() {
return modelSynchronizer;
}
public void checkParseable() {
if (!isDocumentParseable()) {
NotifyDescriptor desc = new org.openide.NotifyDescriptor.Message(
NbBundle.getMessage(DDMultiViewDataObject.class, "TXT_DocumentUnparsable",
getPrimaryFile().getNameExt()), NotifyDescriptor.WARNING_MESSAGE);
DialogDisplayer.getDefault().notify(desc);
// postpone the "Switch to XML View" action to the end of event dispatching thread
// this enables to finish the current action first (e.g. painting particular view)
// see the issue 67580
SwingUtilities.invokeLater(new Runnable(){
public void run() {
goToXmlView();
}
});
}
}
public InputStream createInputStream() {
return getDataCache().createInputStream();
}
public Reader createReader() throws IOException {
return getDataCache().createReader();
}
public void writeModel(RootInterface model) throws IOException {
if (transactionReference != null && transactionReference.get() != null) {
return;
}
FileLock dataLock = waitForLock();
if (dataLock == null) {
return;
}
try {
if (((ModelSynchronizer) getModelSynchronizer()).mayUpdateData(true)) {
writeModel(model, dataLock);
}
} finally {
dataLock.releaseLock();
}
}
public void writeModel(RootInterface model, FileLock dataLock) {
ModelSynchronizer synchronizer = (ModelSynchronizer) getModelSynchronizer();
modelSynchronizer.getReloadTask().cancel();
((RootInterface) synchronizer.getModel()).merge(model, RootInterface.MERGE_UPDATE);
synchronizer.updateData(dataLock, false);
}
public FileLock getDataLock() {
try {
return getModelSynchronizer().takeLock();
} catch (IOException e) {
ErrorManager.getDefault().notify(e);
}
return null;
}
/**
* Used to detect if data model has already been created or not.
* Method is called before switching to the design view from XML view when the document isn't parseable.
*/
protected abstract boolean isModelCreated();
/**
* @throws IOException
*/
protected abstract void parseDocument() throws IOException;
/**
* @throws IOException
*/
protected abstract void validateDocument() throws IOException;
/**
* Update text document from data model. Called when something is changed in visual editor.
* @param model
*/
protected String generateDocumentFromModel(RootInterface model) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
model.write(out);
out.close();
return out.toString("UTF8"); //NOI18N
} catch (IOException e) {
ErrorManager.getDefault().notify(org.openide.ErrorManager.INFORMATIONAL, e);
} catch (IllegalStateException e) {
ErrorManager.getDefault().notify(org.openide.ErrorManager.INFORMATIONAL, e);
}
return out.toString ();
}
/**
* Returns model of the deployment descriptor
* @return the model
*/
protected abstract RootInterface getDDModel();
/**
* Returns true if xml file is parseable(data model can be created),
* Method is called before switching to the design view from XML view when the document isn't parseable.
*/
protected abstract boolean isDocumentParseable();
public Transaction openTransaction() {
final XmlMultiViewDataSynchronizer.Transaction synchronizerTransaction = getModelSynchronizer().openTransaction();
if (synchronizerTransaction == null) {
return null;
} else {
Transaction transaction = new Transaction() {
public void rollback() {
synchronizerTransaction.rollback();
transactionReference = null;
}
public void commit() throws IOException {
synchronizerTransaction.commit();
transactionReference = null;
}
};
transactionReference = new WeakReference(transaction);
return transaction;
}
}
private class ModelSynchronizer extends XmlMultiViewDataSynchronizer {
private long handleUnparseableTimeout = 0;
private Boolean overwriteUnparseable = Boolean.TRUE;
public ModelSynchronizer(XmlMultiViewDataObject dataObject) {
super(dataObject, 300);
handleUnparseableTimeout = 0;
overwriteUnparseable = Boolean.TRUE;
}
protected boolean mayUpdateData(boolean allowDialog) {
if (isDocumentParseable()) {
return true;
}
if (!allowDialog) {
return false;
}
if (handleUnparseableTimeout != -1) {
long time = new Date().getTime();
if (time > handleUnparseableTimeout) {
handleUnparseableTimeout = -1;
org.netbeans.modules.xml.multiview.Utils.runInAwtDispatchThread(new Runnable() {
public void run() {
String message = NbBundle.getMessage(XmlMultiViewDataObject.class,
"TXT_OverwriteUnparsableDocument", getPrimaryFile().getNameExt());
NotifyDescriptor desc = new NotifyDescriptor.Confirmation(message,
NotifyDescriptor.YES_NO_OPTION, NotifyDescriptor.WARNING_MESSAGE);
DialogDisplayer.getDefault().notify(desc);
overwriteUnparseable = Boolean.valueOf(desc.getValue() == NotifyDescriptor.YES_OPTION);
handleUnparseableTimeout = new Date().getTime() + HANDLE_UNPARSABLE_TIMEOUT;
}
});
}
}
return overwriteUnparseable.booleanValue();
}
public void updateData(FileLock dataLock, boolean modify) {
super.updateData(dataLock, modify);
try {
validateDocument();
} catch (IOException e) {
ErrorManager.getDefault().notify(ErrorManager.EXCEPTION, e);
}
}
protected void updateDataFromModel(Object model, FileLock lock, boolean modify) {
String newDocument = generateDocumentFromModel((RootInterface) model);
try {
getDataCache().setData(lock, newDocument, modify);
} catch (IOException e) {
ErrorManager.getDefault().notify(e);
}
}
protected Object getModel() {
return getDDModel();
}
protected void reloadModelFromData() {
try {
parseDocument();
} catch (IOException e) {
ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e);
}
}
}
public void notifyError(Exception ex) {
NotifyDescriptor ndd = new NotifyDescriptor.Message(ex.getMessage(), NotifyDescriptor.ERROR_MESSAGE);
DialogDisplayer.getDefault().notify(ndd);
}
}