/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.xml.ui.wizards; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.emf.common.command.Command; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.dialogs.ProgressMonitorDialog; import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.window.Window; import org.eclipse.jface.wizard.IWizardContainer; import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.actions.WorkspaceModifyOperation; import org.eclipse.xsd.XSDSchema; import org.eclipse.xsd.XSDSchemaDirective; import org.eclipse.xsd.XSDSimpleTypeDefinition; import org.eclipse.xsd.util.XSDResourceImpl; import org.osgi.service.prefs.BackingStoreException; import org.teiid.core.designer.ModelerCoreException; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.designer.core.ModelEditor; import org.teiid.designer.core.ModelEditorImpl; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.container.ResourceFinder; import org.teiid.designer.core.resource.xmi.MtkXmiResourceImpl; import org.teiid.designer.core.types.DatatypeManager; import org.teiid.designer.core.types.EnterpriseDatatypeInfo; import org.teiid.designer.core.workspace.ModelResource; import org.teiid.designer.core.workspace.ModelUtil; import org.teiid.designer.core.workspace.ModelWorkspaceException; import org.teiid.designer.metamodels.xml.XmlFragment; import org.teiid.designer.ui.common.wizard.AbstractWizard; import org.teiid.designer.ui.viewsupport.ModelUtilities; import org.teiid.designer.ui.wizards.INewModelObjectWizard; import org.teiid.designer.xml.IVirtualDocumentFragmentSource; import org.teiid.designer.xml.PluginConstants; import org.teiid.designer.xml.factory.IDocumentsAndFragmentsPopulator; import org.teiid.designer.xml.factory.VirtualDocumentModelPopulator; import org.teiid.designer.xml.ui.ModelerXmlUiConstants; import org.teiid.designer.xml.ui.ModelerXmlUiPlugin; import org.teiid.designer.xml.ui.dialogs.ConvertSimpleTypesToEnteriseTypesDialog; /** * XMLDocumentWizard * * @since 8.0 */ public class XMLDocumentWizard extends AbstractWizard implements INewModelObjectWizard, ModelerXmlUiConstants { static final String DOC_ERROR_MSG = Util.getString("XMLDocumentWizard.documentErrorMessage"); //$NON-NLS-1$ private ModelResource modelResource; private NewVirtualDocumentWizardPage docPage; private VirtualDocumentStatisticsWizardPage statsPage; private PreviewVirtualDocumentWizardPage previewPage; private IWizardPage priorPage; private boolean completedOperation = false; private NewDocumentWizardModel model; // Transaction related objects boolean txnStarted = false; boolean txnSucceeded = false; boolean txnCancelled = false; public XMLDocumentWizard() { super(ModelerXmlUiPlugin.getDefault(), Util.getString("XMLDocumentWizard.title"), null); //$NON-NLS-1$ } @Override public void init( IWorkbench workbench, IStructuredSelection selection ) { setNeedsProgressMonitor(true); model = new NewDocumentWizardModel(); startTransaction(); } // Added the following transaction methods to help encapsulate the creation of XML Documents (via new child) // in a single transaction (so UNDO works) // Defect 18433 - BML 8/31/05 private void startTransaction() { // Start UoW if neccessary txnStarted = ModelerCore.startTxn(true, true, "Build XML Document", this); //$NON-NLS-1$ txnSucceeded = false; txnCancelled = false; } private void commitTransaction() { // Commit txn if we started it. if (txnStarted) { if (txnSucceeded) { ModelerCore.commitTxn(); } else { ModelerCore.rollbackTxn(); } } } private void cancelTransaction() { // Commit txn if we started it. if (txnStarted) { ModelerCore.rollbackTxn(); } } @Override public void setModel( ModelResource model ) { this.modelResource = model; } // public boolean canFinish() { // return getContainer().getCurrentPage() != docPage; // } @Override public void addPages() { docPage = new NewVirtualDocumentWizardPage(model, null); addPage(docPage); model.setSource(docPage); statsPage = new VirtualDocumentStatisticsWizardPage(model); addPage(statsPage); previewPage = new PreviewVirtualDocumentWizardPage(model); addPage(previewPage); } /** * Overrides super's getNextPage to prepopulate the preview page as needed. */ @Override public IWizardPage getNextPage( IWizardPage page ) { // make sure this is for the right page, and is a consequence // of hitting 'next', not 'back' IWizardPage currP = getContainer().getCurrentPage(); try { DocSrcUpdater dsi = null; if (priorPage == docPage && currP == statsPage) { dsi = new DocSrcUpdater(statsPage, true, model.getSelectedFragmentCount()); } else if (currP == previewPage) { dsi = new DocSrcUpdater(previewPage, true, model.getEstimatedNodeCount()); } // endif if (dsi != null) { // Defect 18433 - BML 8/31/05 - Changed the "fork" argument to FALSE. Forking loses the scope of a transaction // breaking the work of building this document into multiple UNDO's getContainer().run(false, true, dsi); } // endif } catch (Exception ex) { Util.log(ex); } // endtry priorPage = currP; return super.getNextPage(page); } @Override public boolean finish() { completedOperation = finishWizard(docPage, previewPage, modelResource, getContainer(), model); if (completedOperation) { txnSucceeded = true; commitTransaction(); } return completedOperation; } /** * Perform the finishing work of a wizard. * * @param docPage The page specifying schema information * @param previewPage The page that shows a preview * @param modelResource The modelResource to write to * @param container The IWizardContainer running the process * @return true if the operation completed successfully, false otherwise. */ public static boolean finishWizard( final NewVirtualDocumentWizardPage docPage, final PreviewVirtualDocumentWizardPage previewPage, final ModelResource modelResource, final IWizardContainer container, final NewDocumentWizardModel wizModel ) { // save pref: final boolean useXsdTypes = wizModel.getUseSchemaTypes(); IEclipsePreferences prefs = ModelerCore.getPreferences(PLUGIN_ID); prefs.putBoolean(PluginConstants.PreferenceKeys.MAPPING_TYPE_FROM_XSD, useXsdTypes); try { ModelerCore.savePreferences(PLUGIN_ID); } catch (BackingStoreException e) { Util.log(e); } // Get handle to DatatypeManager final DatatypeManager dtMgr = ModelerCore.getWorkspaceDatatypeManager(); // simple hack to let me change a variable in the runnable: final boolean[] completedOperation = new boolean[1]; // build the documents: IRunnableWithProgress op = new IRunnableWithProgress() { @Override public void run( IProgressMonitor monitor ) { try { IDocumentsAndFragmentsPopulator populator = docPage.getPopulator(); // the pages will generate the documents we need, though we have to // add the data to the modelResource and build mapping classes // ourselves. IWizardPage currentPage = container.getCurrentPage(); boolean buildMappingClasses = wizModel.getBuildMappingClasses(); Collection fragments = new ArrayList(); if (currentPage != previewPage) { // We are skipping the preview, just add the roots: // This is a new path for Large Model performance. Don't // update the fragments via the previewPage logic as it forces // creation of an entire UI Component. fragments = previewPage.getRoots(modelResource, monitor); final Resource res = modelResource.getEmfResource(); if (res instanceof MtkXmiResourceImpl && !buildMappingClasses) { // Most optimized path, but will not create notifications and requires // user to reopen the editor to see the changes. ((MtkXmiResourceImpl)res).addMany(fragments); } else { final EList contents = modelResource.getEmfResource().getContents(); // Check if any/all fragments have been already added to this resource or not. Don't want to add // them twice. Set missingFragments = new HashSet(); for (Iterator iter = fragments.iterator(); iter.hasNext();) { Object obj = iter.next(); if (!contents.contains(obj)) { missingFragments.add(obj); } } if (!missingFragments.isEmpty()) { ModelerCore.getModelEditor().addValue(res, missingFragments, contents); } } } else { // This forces preview page logic to ensure unchecked entites get removed. final XmlFragment[] xfs = previewPage.getFragments(modelResource, monitor); fragments = Arrays.asList(xfs); final Resource res = modelResource.getEmfResource(); final EList contents = modelResource.getEmfResource().getContents(); ModelerCore.getModelEditor().addValue(res, fragments, contents); } if (buildMappingClasses && populator != null) { final Iterator roots = fragments.iterator(); while (roots.hasNext()) { final XmlFragment fragment = (XmlFragment)roots.next(); populator.buildMappingClasses(fragment, wizModel.getMappingClassBuilderStrategy()); } // end while } // force GC System.gc(); Thread.yield(); // If the user selected "Use XML types from the document" we will check // to see if theier are any Simple Type elements selected that are not // Enterprise Types. If we find any, we will ask the user if they would // like to convert the Simple Types to Enterprise Datatypes. If yes, // we load the xsd resource and update the Simple Types that meet our criteria: // 1.) They were selected // 2.) They are Simple Types that are not Enterprise Datatypes. if (useXsdTypes && populator != null) { // Determine if any of the accumulated datatypes are SimpleTypes and not Enterprise Datatypes HashMap simpleTypeMap = new HashMap(); final Iterator accumulatedDatatypes = ((VirtualDocumentModelPopulator)populator).getAccumulatedDatatypes().iterator(); while (accumulatedDatatypes.hasNext()) { Object type = accumulatedDatatypes.next(); // Add to hash if SimpleType and not Enterprise Datatype if (type instanceof XSDSimpleTypeDefinition && !dtMgr.isEnterpriseDatatype((EObject)type)) { simpleTypeMap.put(((XSDSimpleTypeDefinition)type).getName(), ((XSDSimpleTypeDefinition)type).getSchema()); } } // If we found selected SimpleTypes that are not Enterprise Datatypes, prompt the user // to let us convert them. if (simpleTypeMap.size() > 0) { Set resources = new HashSet(); ModelResource modelResrc = ModelUtil.getModelResource((IFile)populator.getItem(), true); if (modelResrc != null) { Resource xsdResource = modelResrc.getEmfResource(); resources.add(xsdResource); loadReferencedXsdResources(xsdResource, resources, xsdResource.getResourceSet()); Set affectedSchemas = getAffectedSchemas(resources, simpleTypeMap); // Case 5229 - if referenced schemas are not in workspace, skip them. removeExternalSchemas(affectedSchemas); if (!affectedSchemas.isEmpty() && displayDialog(affectedSchemas)) { // check whether all the affected schemas are writable Iterator iter = affectedSchemas.iterator(); while (iter.hasNext()) { Resource resource = (Resource)iter.next(); if (ModelUtilities.getModelResource(resource, false).isReadOnly()) { throw new IOException(Util.getString("XMLDocumentWizard.readOnly", resource.getURI())); //$NON-NLS-1$ // readOnlySchemas.add(resource); } } convertToEnterpriseType(affectedSchemas, simpleTypeMap, monitor); } } } } // force GC System.gc(); Thread.yield(); // save: modelResource.save(monitor, true); modelResource.getEmfResource().setModified(false); completedOperation[0] = true; } catch (ModelWorkspaceException ex) { Util.log(IStatus.ERROR, ex, DOC_ERROR_MSG); } catch (ModelerCoreException ex) { Util.log(IStatus.ERROR, ex, DOC_ERROR_MSG); } catch (Exception ex) { Util.log(IStatus.ERROR, ex, DOC_ERROR_MSG); } finally { monitor.done(); } } private boolean displayDialog( Set resources ) { ConvertSimpleTypesToEnteriseTypesDialog dialog = null; dialog = new ConvertSimpleTypesToEnteriseTypesDialog(null, resources); dialog.open(); if (dialog.getReturnCode() == Window.OK) { return true; } return false; } /** * Helper method to remove any schemas from the supplied set which cannot be found in the workspace * * @param schemas the supplied set of schemas */ private void removeExternalSchemas( Set schemas ) { Iterator iter = schemas.iterator(); while (iter.hasNext()) { Resource resource = (Resource)iter.next(); ModelResource modelRsrc = null; modelRsrc = ModelUtilities.getModelResource(resource, false); if (modelRsrc == null) { iter.remove(); } } } /** * Load the xsd resource, find the Simple Types that have been selected to use in the generated xml document and * convert them to Enterprise Datatypes. * * @param affectedSchemas * @param simpleTypeSet * @param monitor * @throws ModelWorkspaceException */ private void convertToEnterpriseType( final Set resources, final HashMap simpleTypeMap, final IProgressMonitor monitor ) throws Exception { WorkspaceModifyOperation operation = new WorkspaceModifyOperation() { @Override protected void execute( IProgressMonitor monitor ) { XSDSchema schema = null; final ModelEditor me = ModelerCore.getModelEditor(); final Iterator xsdIter = resources.iterator(); while (xsdIter.hasNext()) { boolean updated = false; final XSDResourceImpl xsdResource = (XSDResourceImpl)xsdIter.next(); final Iterator eObjects = xsdResource.getContents().iterator(); while (eObjects.hasNext()) { Object next = eObjects.next(); if (next instanceof XSDSchema) { // turn off notifications for Schema until // we are done schema = (XSDSchema)next; schema.setIncrementalUpdate(false); final Iterator children = ((XSDSchema)next).eContents().iterator(); while (children.hasNext()) { final Object child = children.next(); // If this is a Simple Type and one of // the selected fragments for the document, // convert it to an Enterprise Datatype. if (child instanceof XSDSimpleTypeDefinition) { Object value = simpleTypeMap.get(((XSDSimpleTypeDefinition)child).getName()); if (value != null && ((XSDSchema)value).getSchemaLocation().equals(((XSDSchema)next).getSchemaLocation())) { EnterpriseDatatypeInfo edi = getEDIForType((XSDSimpleTypeDefinition)child); me.setEnterpriseDatatypePropertyValue((XSDSimpleTypeDefinition)child, edi); updated = true; } } } } } try { if (updated) { xsdResource.save(new HashMap()); IResource iResource = ModelUtilities.getModelResource(xsdResource, true).getResource(); iResource.refreshLocal(IResource.DEPTH_ZERO, null); } } catch (Exception err) { final String msg = Util.getString("XMLDocumentWizard.convertToEnterpriseTypesSaveError"); //$NON-NLS-1$ Util.log(IStatus.ERROR, err, msg); } } } }; try { new ProgressMonitorDialog(null).run(false, false, operation); } catch (InterruptedException e) { Util.log(IStatus.ERROR, e, DOC_ERROR_MSG); } catch (InvocationTargetException e) { Util.log(IStatus.ERROR, e, DOC_ERROR_MSG); } } /** * Determine which schema files will be affected by changing selected Simple Types to Enterprise types and return the * files in a Set. * * @param resources the root schema and all dependents * @param simpleTypeSet */ private Set getAffectedSchemas( Set resources, final HashMap simpleTypeMap ) { Set affectedSchemas = new HashSet(); final Iterator xsdIter = resources.iterator(); while (xsdIter.hasNext()) { final XSDResourceImpl xsdResource = (XSDResourceImpl)xsdIter.next(); final Iterator eObjects = xsdResource.getContents().iterator(); while (eObjects.hasNext()) { Object next = eObjects.next(); final Iterator children = ((XSDSchema)next).eContents().iterator(); while (children.hasNext()) { final Object child = children.next(); // If this is a Simple Type and one of the selected fragments for the document, // convert it to an Enterprise Datatype. if (child instanceof XSDSimpleTypeDefinition) { Object value = simpleTypeMap.get(((XSDSimpleTypeDefinition)child).getName()); if (value != null && ((XSDSchema)value).getSchemaLocation().equals(((XSDSchema)next).getSchemaLocation())) { affectedSchemas.add(xsdResource); } } } } } return affectedSchemas; } /** * Recursively loads a set of dependent schema resources * * @param xsdSource - the root schema * @param resources - the set of dependent resources * @param container - the resource set from the current schema */ private void loadReferencedXsdResources( Resource xsdSource, final Set resources, final ResourceSet container ) throws Exception { // Get a ResourceFinder to use when resolving dependent resource references CoreArgCheck.isNotNull(ModelerCore.getContainer(xsdSource)); ResourceFinder finder = ModelerCore.getContainer(xsdSource).getResourceFinder(); if (!xsdSource.isLoaded()) { ResourceSet ctnr = xsdSource.getResourceSet(); if (ctnr == null) { ctnr = ModelerCore.getModelContainer(); } final Map options = (ctnr == null ? null : ctnr.getLoadOptions()); xsdSource.load(options); } XSDSchema schema = ((XSDResourceImpl)xsdSource).getSchema(); for (final Iterator contents = schema.getContents().iterator(); contents.hasNext();) { final Object obj = contents.next(); if (obj instanceof XSDSchemaDirective) { xsdSource = finder.findByImport((XSDSchemaDirective)obj, false); if (xsdSource != null && xsdSource.getResourceSet() == container) { // Load referenced XSD resources if (resources.add(xsdSource)) { // Recurse to get the referenced resource loadReferencedXsdResources(xsdSource, resources, container); } } } } } /** * Returns the <code>EnterpriseDatatypeInfo</code> object for a Simple Type * * @param type * @return edi EnterpriseDatatypeInfo */ EnterpriseDatatypeInfo getEDIForType( final XSDSimpleTypeDefinition type ) { final EnterpriseDatatypeInfo edi = new EnterpriseDatatypeInfo(); XSDSimpleTypeDefinition superType = type; XSDSimpleTypeDefinition enterpriseParent = null; while (superType != null && enterpriseParent == null) { if (dtMgr.isEnterpriseDatatype(superType)) { enterpriseParent = superType; } else { XSDSimpleTypeDefinition tmp = superType.getBaseTypeDefinition(); if (tmp != superType) { superType = superType.getBaseTypeDefinition(); } else { superType = null; } } } if (enterpriseParent != null) { edi.setRuntimeTypeFixed(dtMgr.getRuntimeTypeFixed(enterpriseParent)); edi.setRuntimeType(dtMgr.getRuntimeTypeName(enterpriseParent)); } ModelEditorImpl.fillWithDefaultValues(edi, type); return edi; } }; try { container.run(false, true, op); } catch (InterruptedException e) { return false; } catch (InvocationTargetException e) { Throwable realException = e.getTargetException(); String message = realException.getMessage(); if (message == null) { message = realException.getClass().getName(); } MessageDialog.openError(container.getShell(), Util.getString("XMLDocumentWizard.error"), //$NON-NLS-1$ message); ModelerXmlUiConstants.Util.log(realException); return false; } return completedOperation[0]; } @Override public boolean completedOperation() { return completedOperation; } /** * Overrode super method so we could coordinate a "cancel" functionality on the transaction. * * @see org.eclipse.jface.wizard.Wizard#performCancel() * @since 4.3 */ @Override public boolean performCancel() { cancelTransaction(); return super.performCancel(); } /** * @see org.teiid.designer.ui.wizards.INewModelObjectWizard#setCommand(org.eclipse.emf.common.command.Command) */ @Override public void setCommand( Command descriptor ) { } public static class DocSrcUpdater implements IRunnableWithProgress { // Instance variables: private int units; private IVirtualDocumentFragmentSource srcToUpdate; private boolean isVis; // Constructors: public DocSrcUpdater( IVirtualDocumentFragmentSource thingToUpdate, boolean isVisible, int nodes ) { srcToUpdate = thingToUpdate; isVis = isVisible; units = nodes * 2; } // Implementation of the IRunnableWithProgress interface: @Override public void run( IProgressMonitor monitor ) { if (monitor == null) { monitor = new NullProgressMonitor(); } // endif monitor.beginTask(Util.getString("XMLDocumentWizard.taskGathering"), units); //$NON-NLS-1$ srcToUpdate.updateSourceFragments(isVis, monitor); monitor.done(); } } }