blob: c63af0312982eb03b8fdc5c8eb004dc6293f0cf0 [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.persistence.unit;
import java.awt.Image;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import org.netbeans.api.project.FileOwnerQuery;
import org.netbeans.api.project.Project;
import org.netbeans.modules.j2ee.persistence.dd.PersistenceMetadata;
import org.netbeans.modules.j2ee.persistence.dd.common.Persistence;
import org.netbeans.modules.j2ee.persistence.dd.common.PersistenceUnit;
import org.netbeans.modules.j2ee.persistence.provider.ProviderUtil;
import org.openide.DialogDisplayer;
import org.netbeans.api.xml.cookies.CheckXMLCookie;
import org.netbeans.api.xml.cookies.ValidateXMLCookie;
import org.netbeans.core.spi.multiview.MultiViewElement;
import org.netbeans.modules.j2ee.persistence.dd.common.JPAParseUtils;
import org.netbeans.modules.schema2beans.BaseBean;
import org.netbeans.modules.schema2beans.Schema2BeansException;
import org.netbeans.modules.xml.multiview.ToolBarMultiViewElement;
import org.netbeans.modules.xml.multiview.XmlMultiViewDataObject;
import org.netbeans.modules.xml.multiview.XmlMultiViewDataSynchronizer;
import org.netbeans.modules.xml.multiview.XmlMultiViewElement;
import org.netbeans.spi.xml.cookies.CheckXMLSupport;
import org.netbeans.spi.xml.cookies.DataObjectAdapters;
import org.netbeans.spi.xml.cookies.ValidateXMLSupport;
import org.openide.NotifyDescriptor;
import org.openide.cookies.EditorCookie;
import org.openide.filesystems.FileLock;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.MIMEResolver;
import org.openide.loaders.DataObjectExistsException;
import org.openide.nodes.Node;
import org.openide.util.Exceptions;
import org.openide.util.ImageUtilities;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.windows.TopComponent;
import org.xml.sax.SAXException;
/**
* Multiview data object for persistence.xml.
*
* @author Martin Adamek
* @author Erno Mononen
*/
@MIMEResolver.Registration(
displayName="",
position=220,
resource="../ui/resources/PUResolver.xml"
)
public class PUDataObject extends XmlMultiViewDataObject {
public static final String HELP_ID_DESIGN_PERSISTENCE_UNIT
= "persistence_multiview_design_persistenceUnitNode"; // NOI18N
private final ModelSynchronizer modelSynchronizer;
/**
* Update delay for model synchronizer.
*/
public static final int UPDATE_DELAY = 200;
private static final int TYPE_TOOLBAR = 0;
private Persistence persistence;
private static final String DESIGN_VIEW_ID = "persistence_multiview_design"; // NOI18N
private static final Logger LOG = Logger.getLogger(PUDataObject.class.getName());
public static final String NO_UI_PU_CLASSES_CHANGED = "non ui pu classes modified"; //NOI18N
public static final String ICON = "org/netbeans/modules/j2ee/persistence/unit/PersistenceIcon.gif"; //NOI18N
public static final String PREFERRED_ID_SOURCE="persistence_multiview_source"; //NOI18N
public static final String PREFERRED_ID_DESIGN="persistence_multiview_design"; //NOI18N
/**
* The property name for the event fired when a persistence unit was added or removed.
*/
static final String PERSISTENCE_UNIT_ADDED_OR_REMOVED = "persistence_unit_added_or_removed"; //NOI18N
protected boolean changedFromUI;
/**
* Creates a new instance of PUDataObject.
*/
public PUDataObject(FileObject pf, PUDataLoader loader) throws DataObjectExistsException {
super(pf, loader);
getEditorSupport().setMIMEType(PUDataLoader.REQUIRED_MIME);
modelSynchronizer = new ModelSynchronizer(this);
org.xml.sax.InputSource in = DataObjectAdapters.inputSource(this);
CheckXMLCookie checkCookie = new CheckXMLSupport(in);
getCookieSet().add(checkCookie);
ValidateXMLCookie validateCookie = new ValidateXMLSupport(in);
getCookieSet().add(validateCookie);
parseDocument();
}
@Override
protected Node createNodeDelegate() {
return new PUDataNode(this);
}
@Override
protected String getEditorMimeType() {
return PUDataLoader.REQUIRED_MIME;
}
@Override
protected int getXMLMultiViewIndex(){
return 1;
}
@MultiViewElement.Registration(
mimeType=PUDataLoader.REQUIRED_MIME,
iconBase=ICON,
persistenceType=TopComponent.PERSISTENCE_ONLY_OPENED,
preferredID=PREFERRED_ID_SOURCE,
displayName="#CTL_SourceTabCaption",
position=2000
)
@Messages("CTL_SourceTabCaption=Source")
public static XmlMultiViewElement createXmlMultiViewElement(Lookup lookup) {
return new XmlMultiViewElement(lookup.lookup(XmlMultiViewDataObject.class));
}
@Override
protected int associateLookup() {
return 1;
}
/**
* Saves the document.
* @see EditorCookie#saveDocument
*/
public void save(){
EditorCookie edit = (EditorCookie) getLookup().lookup(EditorCookie.class);
if (edit != null){
try {
edit.saveDocument();
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
}
/**
* Parses the document.
* @return true if document could be parsed (it was valid), false otwherwise.
*/
public boolean parseDocument() {
if (persistence==null) {
try {
persistence = getPersistence();
} catch (RuntimeException ex) { // must catch RTE (thrown by schema2beans when document is not valid)
LOG.log(Level.INFO, null, ex);
return false;
}
} else if (isModified()){//if it's isn't modified and persistenc eexits (parsed) no need to reparse
try{
String oldVersion = persistence.getVersion();
java.io.InputStream is = getEditorSupport().getInputStream();
String version=Persistence.VERSION_1_0;
try {
version=JPAParseUtils.getVersion(is);
} catch (SAXException ex) {
LOG.log(Level.INFO, null, ex);//persistence.xml may be corrupted, but no need to show exception dialog
}
finally
{
is.close();
}
is = getEditorSupport().getInputStream();
Persistence newPersistence;
Persistence cleanPersistence;
try {
if(Persistence.VERSION_2_1.equals(version)) {
newPersistence = org.netbeans.modules.j2ee.persistence.dd.persistence.model_2_1.Persistence.createGraph(is);
cleanPersistence = new org.netbeans.modules.j2ee.persistence.dd.persistence.model_2_1.Persistence();
} else if(Persistence.VERSION_2_0.equals(version)) {
newPersistence = org.netbeans.modules.j2ee.persistence.dd.persistence.model_2_0.Persistence.createGraph(is);
cleanPersistence = new org.netbeans.modules.j2ee.persistence.dd.persistence.model_2_0.Persistence();
} else {//1.0 - default
newPersistence = org.netbeans.modules.j2ee.persistence.dd.persistence.model_1_0.Persistence.createGraph(is);
cleanPersistence = new org.netbeans.modules.j2ee.persistence.dd.persistence.model_1_0.Persistence();
}
} catch (RuntimeException ex) { // must catch RTE (thrown by schema2beans when document is not valid)
LOG.log(Level.INFO, null, ex);
return false;
}
if (newPersistence!=null) {
try{
((BaseBean)persistence).merge((BaseBean) cleanPersistence, BaseBean.MERGE_UPDATE);
((BaseBean)persistence).merge((BaseBean) newPersistence, BaseBean.MERGE_UPDATE);
} catch (IllegalArgumentException iae) {
// see #104180
LOG.log(Level.FINE, "IAE thrown during merge, see #104180.", iae); //NOI18N
if(!oldVersion.equals(newPersistence.getVersion())){//version may be changed, just replace instead of merge then, see #173233 also
persistence = null;
PersistenceMetadata.getDefault().refresh(getPrimaryFile());
return true;
}
else {
return false;
}
}
}
} catch (IOException ioe){
LOG.log(Level.INFO, null, ioe);
return false;
} catch (IllegalStateException e) {
//issue 134726, sometimes faled to parser document if it's changed during update, just skip, should be parsed with next event
LOG.log(Level.INFO, null, e);
return false;
}
}
return true;
}
/**
* Checks whether the preferred view can be displayed and switches to the
* xml view and displays an appropriate warning if not. In case that
* the preferred view is the design view, it
* can be displayed if <ol><li>document is valid (parseable) and</li>
*<li>the target server is attached></li></ol>.
*@return true if the preferred view can be displayed, false otherwise.
*/
public boolean viewCanBeDisplayed() {
boolean switchView = false;
NotifyDescriptor nd = null;
if(FileOwnerQuery.getOwner(getPrimaryFile())==null) {
nd = new org.openide.NotifyDescriptor.Message(
NbBundle.getMessage(PUDataObject.class, "TXT_StandAlonePersistence",
getPrimaryFile().getNameExt()), NotifyDescriptor.WARNING_MESSAGE);
switchView = true;
} else if (!parseDocument() && getSelectedPerspective().preferredID().startsWith(DESIGN_VIEW_ID)) {
nd = new org.openide.NotifyDescriptor.Message(
NbBundle.getMessage(PUDataObject.class, "TXT_DocumentUnparsable",
getPrimaryFile().getNameExt()), NotifyDescriptor.WARNING_MESSAGE);
switchView = true;
} else if (!ProviderUtil.isValidServerInstanceOrNone(FileOwnerQuery.getOwner(getPrimaryFile()))
&& getSelectedPerspective().preferredID().startsWith(DESIGN_VIEW_ID)){
nd = new org.openide.NotifyDescriptor.Message(
NbBundle.getMessage(PUDataObject.class, "TXT_ServerMissing"),
NotifyDescriptor.WARNING_MESSAGE);
switchView = true;
}
if (switchView){
DialogDisplayer.getDefault().notify(nd);
// 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(){
@Override
public void run() {
goToXmlView();
}
});
}
return !switchView;
}
/**
* Gets the object graph representing the contents of the
* persistence.xml deployment desciptor with which this data object
* is associated.
*
* @return the persistence graph.
*
*/
public Persistence getPersistence(){
if (persistence==null) {
try {
persistence = PersistenceMetadata.getDefault().getRoot(getPrimaryFile());
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
}
assert persistence != null;
return persistence;
}
/**
* Adds given persistence unit and schedules update of data.
*/
public void addPersistenceUnit(PersistenceUnit persistenceUnit){
Project project = FileOwnerQuery.getOwner(getPrimaryFile());
if(project != null) {
ProviderUtil.makePortableIfPossible(project, persistenceUnit);
}
getPersistence().addPersistenceUnit(persistenceUnit);
modelUpdated();
firePropertyChange(PERSISTENCE_UNIT_ADDED_OR_REMOVED, false, true);
}
/**
* Removes given persistence unit and schedules update of data.
*/
public void removePersistenceUnit(PersistenceUnit persistenceUnit){
getPersistence().removePersistenceUnit(persistenceUnit);
modelUpdated();
firePropertyChange(PERSISTENCE_UNIT_ADDED_OR_REMOVED, false, true);
}
/**
* Adds given clazz to the list of given persistence unit's managed
* classes and schedules update of data.
* @param persistenceUnit
* @param clazz fully qualified name of the class to be added.
* @param fromPanel true if added with pu design view, false if added for example with refactoring or added as new entity class in project
* @return true if given class was added, false otherwise (for example when
* it was already added).
*/
public boolean addClass(PersistenceUnit persistenceUnit, String clazz, boolean fromPanel){
String[] existing = persistenceUnit.getClass2();
for (int i = 0; i < existing.length; i++) {
if (clazz.equals(existing[i])){
return false;
}
}
persistenceUnit.addClass2(clazz);
modelUpdated();
if(!fromPanel)//may need to update panels/design view
{
updateUIPanels(persistenceUnit, NO_UI_PU_CLASSES_CHANGED);
}
return true;
}
/**
* Removes given class from the list of given persistence unit's managed
* classes and schedules update of data.
* @param persistenceUnit
* @param clazz fully qualified name of the class to be removed.
* @param fromPanel true if added with pu design view, false if added for example with refactoring
*/
public void removeClass(PersistenceUnit persistenceUnit, String clazz, boolean fromPanel){
persistenceUnit.removeClass2(clazz);
modelUpdated();
if(!fromPanel)//may need to update panels/design view
{
updateUIPanels(persistenceUnit, NO_UI_PU_CLASSES_CHANGED);
}
}
@Override
public void showElement(Object element) {
Object target=null;
if (element instanceof PersistenceUnit) {
openView(0);
target=element;
}
if (target!=null) {
final Object key=target;
org.netbeans.modules.xml.multiview.Utils.runInAwtDispatchThread(new Runnable() {
@Override
public void run() {
getActiveMultiViewElement0().getSectionView().openPanel(key);
}
});
}
}
@Override
protected String getPrefixMark() {
return null;
}
/** Enable to get active MultiViewElement object
*/
public ToolBarMultiViewElement getActiveMultiViewElement0() {
return (ToolBarMultiViewElement)super.getActiveMultiViewElement();
}
public void modelUpdated() {
setModified(true);
modelSynchronizer.requestUpdateData();
}
public boolean isChangedFromUI() {
return changedFromUI;
}
public void setChangedFromUI(boolean changedFromUI) {
this.changedFromUI=changedFromUI;
}
/**
* it's used if need to update
* @param unit
*/
private void updateUIPanels(PersistenceUnit unit, String kind)
{
firePropertyChange(NO_UI_PU_CLASSES_CHANGED, null , unit);
}
/**
* Call this method if the model got updated via UI, such as,visual editor
*/
public void modelUpdatedFromUI() {
modelSynchronizer.requestUpdateData();
}
public void updateDataFromModel(FileLock lock) throws IOException{
modelSynchronizer.updateDataFromModel(getPersistence(), lock, true);
}
@Override
public boolean isDeleteAllowed() {
return true;
}
@Override
public boolean isCopyAllowed() {
return true;
}
@Override
public boolean isMoveAllowed(){
return true;
}
@Override
protected Image getXmlViewIcon() {
return ImageUtilities.loadImage("org/netbeans/modules/j2ee/persistence/unit/PersistenceIcon.gif"); //NOI18N
}
private class ModelSynchronizer extends XmlMultiViewDataSynchronizer {
public ModelSynchronizer(XmlMultiViewDataObject dataObject) {
super(dataObject, UPDATE_DELAY);
}
@Override
protected boolean mayUpdateData(boolean allowDialog) {
return true;
}
@Override
protected void updateDataFromModel(Object model, FileLock lock, boolean modify) {
if (model == null) {
return;
}
try {
Writer out = new StringWriter();
((BaseBean) model).write(out);
out.close();
getDataCache().setData(lock, out.toString(), modify);
} catch (IOException e) {
LOG.log(Level.INFO, null, e);
} catch (Schema2BeansException e) {
LOG.log(Level.INFO, null, e);
} finally {
if (lock != null){
lock.releaseLock();
}
}
}
@Override
protected Object getModel() {
try {
return getPersistence();
} catch (RuntimeException ex) { // must catch RTE (thrown by schema2beans when document is not valid)
LOG.log(Level.INFO, null, ex);
return null;
}
}
@Override
protected void reloadModelFromData() {
parseDocument();
}
}
}