/* * 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.editor; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.emf.common.notify.AdapterFactory; import org.eclipse.emf.edit.provider.IItemLabelProvider; import org.eclipse.jface.viewers.CheckboxTreeViewer; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeExpansionEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.xsd.XSDComponent; import org.eclipse.xsd.XSDTypeDefinition; import org.teiid.core.designer.ModelerCoreException; import org.teiid.designer.core.ModelerCore; import org.teiid.designer.core.workspace.ModelResource; import org.teiid.designer.diagram.ui.DiagramUiConstants; import org.teiid.designer.metamodels.xml.XmlAttribute; import org.teiid.designer.metamodels.xml.XmlContainerNode; import org.teiid.designer.metamodels.xml.XmlDocumentBuilderImpl; import org.teiid.designer.metamodels.xml.XmlDocumentEntity; import org.teiid.designer.metamodels.xml.XmlDocumentNode; import org.teiid.designer.metamodels.xml.XmlElement; import org.teiid.designer.metamodels.xml.XmlFragment; import org.teiid.designer.metamodels.xml.XmlRoot; import org.teiid.designer.metamodels.xml.util.XmlDocumentUtil; import org.teiid.designer.ui.OverlayImageIcon; import org.teiid.designer.ui.common.eventsupport.SelectionUtilities; import org.teiid.designer.ui.common.tree.AbstractTreeContentProvider; import org.teiid.designer.ui.common.util.WidgetFactory; import org.teiid.designer.ui.common.widget.DefaultTreeViewerController; import org.teiid.designer.ui.editors.ModelEditorManager; import org.teiid.designer.ui.explorer.ModelExplorerLabelProvider; import org.teiid.designer.ui.viewsupport.ModelUtilities; import org.teiid.designer.xml.IVirtualDocumentFragmentSource; import org.teiid.designer.xml.ui.ModelerXmlUiConstants; import org.teiid.designer.xml.ui.ModelerXmlUiPlugin; /** * @author PForhan * * @since 8.0 */ public class EditVirtualDocumentsPanel extends Composite implements IVirtualDocumentFragmentSource { static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; private static final XmlFragment[] EMPTY_FRAGMENT_ARRAY = new XmlFragment[0]; CheckboxTreeViewer viewer; VDocContentProvider content; VDocTreeViewerController controller; XmlFragment[] roots; Set wasStopped = new HashSet(); Set wasRecursive = new HashSet(); int stopExpansion; public EditVirtualDocumentsPanel( Composite parent ) { super(parent, SWT.NULL); initializeGUI(); } private void initializeGUI() { GridLayout layout = new GridLayout(); layout.marginHeight = 0; layout.marginWidth = 0; setLayout(layout); Composite useParent = this; // set up tree: controller = new VDocTreeViewerController(); content = new VDocContentProvider(); viewer = (CheckboxTreeViewer)WidgetFactory.createTreeViewer(useParent, SWT.CHECK, controller); // Create and set content provider for tree: viewer.setContentProvider(content); // Create and set label provider for tree: VDocLabelProvider vdocLabelProvider = new VDocLabelProvider(); viewer.setLabelProvider(vdocLabelProvider); viewer.getTree().addDisposeListener(vdocLabelProvider); viewer.setInput(roots); viewer.addSelectionChangedListener(new VDocSelectionListener()); // a key or mouse event would come in later than the tree events: // These two listeners are a nasty hack to prevent rampant // expansion via the '*' (expand tree) key . Control tree = viewer.getControl(); tree.addKeyListener(new KeyListener() { @Override public void keyPressed( KeyEvent e ) { stopExpansion = 0; } @Override public void keyReleased( KeyEvent e ) { stopExpansion = 0; } }); // see note above. tree.addMouseListener(new MouseListener() { @Override public void mouseDoubleClick( MouseEvent e ) { stopExpansion = 0; } @Override public void mouseDown( MouseEvent e ) { stopExpansion = 0; } @Override public void mouseUp( MouseEvent e ) { stopExpansion = 0; } }); } static void buildNextLevelFromSchema( XmlElement element ) { // open and activate editor first so that the editor is dirty after building: // Note: this is not so important for new documents, but for existing ones, // we need to make sure the editor is open. ModelResource mr = ModelUtilities.getModelResourceForModelObject(element); if (mr != null && !mr.isOpen()) { ModelEditorManager.activate(mr, true); } try { XmlDocumentBuilderImpl builder = new XmlDocumentBuilderImpl(1); builder.buildDocument(element, null); } catch (final Exception theException) { final String msg = ModelerXmlUiConstants.Util.getString("NumberOfLevelsWizard.buildErrorMessage"); //$NON-NLS-1$ ModelerXmlUiConstants.Util.log(IStatus.ERROR, theException, msg); } // endtry XmlDocumentUtil.setAllExcluded(element, true); } /** * Note that calling this method sets in motion final processing; The fragments the editor is working with will be changed and * things not checked in the editor will be deleted. */ @Override public XmlFragment[] getFragments( ModelResource modelResource, IProgressMonitor monitor ) { // TODO this method should make use of a progress monitor! List rv = new ArrayList(roots.length); for (int i = 0; i < roots.length; i++) { XmlFragment root = roots[i]; try { boolean panelHidden = !isVisible(); if (viewer.getChecked(root) || panelHidden) { // this root was checked, or the panel is not showing, so assume it is: pruneChildren(root, wasStopped, wasRecursive); rv.add(root); } else { // we are not including this root; delete: ModelerCore.getModelEditor().delete(root, false); } // endif } catch (ModelerCoreException ex) { ModelerXmlUiConstants.Util.log(ex); } // endtry } // endfor return (XmlFragment[])rv.toArray(EMPTY_FRAGMENT_ARRAY); } /** * Return the set of root XML Elements */ public Collection getRoots( IProgressMonitor monitor ) { if (roots == null) { return Collections.EMPTY_LIST; } return Arrays.asList(roots); } public boolean haveFragmentsChanged() { return true; } /** Implemented to do nothing for now; the Preview WizardPage handles this */ @Override public void updateSourceFragments( boolean isVisible, IProgressMonitor monitor ) { } private static void pruneChildren( XmlDocumentEntity node, Set wasStopped, Set wasRecursive ) throws ModelerCoreException { List kids = new ArrayList(node.eContents()); // wrap in an AL to prevent outside interference for (int i = 0; i < kids.size(); i++) { XmlDocumentEntity kid = (XmlDocumentEntity)kids.get(i); if (XmlDocumentUtil.isExcluded(kid, false)) { // no need for any excluded objects (or their kids): // false below because we don't want to check the resource, // since there may be none (new documents). ModelerCore.getModelEditor().delete(kid, false); } else { // not excluded (that would be included, folks), so process further: if (!XmlDocumentUtil.isIncomplete(kid)) { // build didn't stop on this child, so dig in further: pruneChildren(kid, wasStopped, wasRecursive); } // endif // now that any unneeded kids have been removed, see if I still // belong or if I need to revert any of my flags: boolean hasElements = XmlDocumentUtil.hasElementChildren(kid); if (!hasElements) { // no elements below, may need to do something more: if (kid instanceof XmlContainerNode) { // delete any empty XmlContainers: ModelerCore.getModelEditor().delete(kid); } else { // restore attributes: if (wasStopped.contains(kid)) { XmlDocumentUtil.setIncomplete(kid, true); } // endif if (wasRecursive.contains(kid)) { XmlDocumentUtil.setRecursive(kid, true); } // endif -- was recursive } // endif -- was container node } // endif -- no element children } // endif -- was excluded } // endfor -- children } public XmlFragment[] getStartingFragments() { return roots; } public void setFragments( final XmlFragment[] docRoots ) { roots = docRoots; } /** * Set the initial documents and fragments to edit. * * @param docRoots the fragments and documents to edit. These should be safe for EditVirtualDocumentsPanel to change. */ public void setStartingFragments( final XmlFragment[] docRoots, final boolean isVisible, final IProgressMonitor monitor ) { roots = docRoots; if (viewer != null) { // do some init in the display thread: Display.getDefault().syncExec(new Runnable() { @Override public void run() { if (isVisible) viewer.setInput(roots); } }); // eliminate a little overhead by going through the array here: for (int i = 0; i < docRoots.length; i++) { walkTreeAndCheck(content, docRoots[i], isVisible ? viewer : null, monitor); } // endfor monitor.subTask(ModelerXmlUiConstants.Util.getString("EditVirtualDocumentsPanel.subtaskDisplaying")); //$NON-NLS-1$ // do the clean up in the display thread: // Defect 18433 - Changed thread to SYNC because we want all work to be done on the SAME thread so the transaction // boundary is maintained and the creation of a new XML DOcument(s) is Undoable Display.getDefault().syncExec(new Runnable() { @Override public void run() { if (roots.length > 0) { viewer.setSelection(new StructuredSelection(roots[0])); // make sure all roots are checked, regardless of this panel's isVisible status: for (int i = 0; i < roots.length; i++) { XmlFragment fragment = roots[i]; viewer.setChecked(fragment, true); // make sure required children are checked: controller.checkRequiredChildren(fragment); if (isVisible) monitor.worked(1); // indicate progress } // endfor } // endif } }); } // endif } /** * Check and expand all leaf nodes that should be initially checked. this method is safe to run from a non-gui thread. * * @param fragment */ private static void walkTreeAndCheck( ITreeContentProvider tcp, final Object current, final CheckboxTreeViewer viewer, IProgressMonitor monitor ) { // abort if necessary: if (monitor.isCanceled()) return; boolean onlyAttributeKids = true; Object[] kids = null; if (!XmlDocumentUtil.isIncomplete(current)) { // mark included: if (viewer != null) { Display.getDefault().syncExec(new Runnable() { @Override public void run() { viewer.setChecked(current, true); // this creates TreeItems... } }); } // endif // it's OK to call getChildren without hasChildren here because // we checked the stopped attribute above. kids = tcp.getChildren(current); // note that this does *not* create TreeItems for (int i = 0; i < kids.length; i++) { // abort if necessary: if (monitor.isCanceled()) return; // process each child: Object kid = kids[i]; walkTreeAndCheck(tcp, kid, viewer, monitor); if (!(kid instanceof XmlAttribute)) { onlyAttributeKids = false; } // endif } // endfor } else { // was stopped, and was not a root, mark as excluded (to get the model in sync with the tree): setExcluded(current, true); } // endif // abort if necessary: if (monitor.isCanceled()) return; if (viewer != null && kids != null && kids.length > 0 && !onlyAttributeKids) { // parent of a non-attribute leaf node; make sure I am expanded: Display.getDefault().asyncExec(new Runnable() { @Override public void run() { viewer.expandToLevel(current, 1); // this creates TreeItems... } }); } // endif monitor.worked(1); } static void setExcluded( final Object element, final boolean exclude ) { boolean startedTxn = ModelerCore.startTxn(false, false, "Set Excluded", null); //$NON-NLS-1$ boolean succeeded = false; try { XmlDocumentUtil.setExcluded(element, exclude); succeeded = true; } finally { if (startedTxn) { if (succeeded) { ModelerCore.commitTxn(); } else { ModelerCore.rollbackTxn(); } } } } class VDocContentProvider extends AbstractTreeContentProvider { @Override public boolean hasChildren( Object element ) { // prevent rampant expansion: if (stopExpansion > 1) return false; if (element == roots && roots.length > 0) { return true; } // endif if (XmlDocumentUtil.isIncomplete(element)) { // only process if we are not programattically unchecking // double-check the node to make sure it really is expandable: if (element instanceof XmlDocumentNode) { XmlDocumentNode dnode = (XmlDocumentNode)element; boolean isComplex = XmlDocumentUtil.hasComplexType(dnode); if (!isComplex) { // a simpleType is not really stopped... just a consequence of building // only one level of a document. XmlDocumentUtil.setIncomplete(element, false); } // endif return isComplex; } // endif // not a documentNode, just return true to allow expansion: return true; } // endif return !((XmlDocumentEntity)element).eContents().isEmpty(); } @Override public Object[] getChildren( final Object element ) { // prevent rampant expansion: if (stopExpansion > 1) return EMPTY_OBJECT_ARRAY; if (element == roots) { return roots; } // endif if (XmlDocumentUtil.isIncomplete(element)) { // track states for later, in case user changes their mind: wasStopped.add(element); if (XmlDocumentUtil.isRecursive(element)) { wasRecursive.add(element); } // endif // do the work: buildNextLevelFromSchema((XmlElement)element); // make sure the check state is synchronized: if (viewer.getChecked(element)) { setExcluded(element, false); } // endif -- item checked } // endif -- was stopped return ((XmlDocumentEntity)element).eContents().toArray(); } @Override public Object getParent( final Object element ) { if (element == roots) { return null; } // endif if (element instanceof XmlFragment) { return roots; } // endif return ((XmlDocumentEntity)element).eContainer(); } } // endclass VDocContentProvider class VDocTreeViewerController extends DefaultTreeViewerController { @Override public void checkedStateToggled( final TreeItem item ) { boolean shouldExclude = !item.getChecked(); // modify the value of shouldExclude: Object nodeData = item.getData(); setExcluded(nodeData, shouldExclude); if (shouldExclude) { // uncheck kids if unchecked: excludeNodeAndChildren(content, nodeData); } else { // make sure ancestors are checked: checkRequiredAncestors(nodeData); checkDefaultChildren(nodeData); } // endif } @Override public boolean isItemCheckable( final TreeItem item ) { Object d = item.getData(); return isUserCheckable(d); } @Override public void update( TreeItem item, boolean selected ) { // force non-elements and non-fragments to keep their checked state: Object d = item.getData(); if (!isUserCheckable(d) && item.getChecked()) { // force ancestors to be checked, too: checkRequiredAncestors(d); viewer.setChecked(d, true); setExcluded(d, false); } else if (viewer.getChecked(d)) { // we are an element or fragment, check kids and ancestors: checkRequiredAncestors(d); checkRequiredChildren(d); } // endif } @Override public void itemDoubleClicked( DoubleClickEvent event ) { treeExpanded(SelectionUtilities.getSelectedObject(event.getSelection())); } @Override public void itemExpanded( TreeExpansionEvent event ) { // try to prevent rampant expansion: stopExpansion++; treeExpanded(event.getElement()); } private void treeExpanded( final Object nodeData ) { if (viewer.getChecked(nodeData)) { checkRequiredChildren(nodeData); } // endif if (wasStopped.contains(nodeData)) { Display.getCurrent().asyncExec(new Runnable() { @Override public void run() { viewer.refresh(nodeData); } }); } // endif } private boolean isUserCheckable( Object d ) { return !(d instanceof XmlRoot) // defect 17530 users cannot uncheck the root element. && (d instanceof XmlElement || d instanceof XmlFragment || d instanceof XmlContainerNode || d instanceof XmlAttribute); } private void excludeNodeAndChildren( ITreeContentProvider tcp, Object nodeData ) { Object[] kids; // only scan children of an object if that object is complete, and has children. if (!XmlDocumentUtil.isIncomplete(nodeData) && tcp.hasChildren(nodeData)) { kids = tcp.getChildren(nodeData); } else { // no kids of interest: kids = EMPTY_OBJECT_ARRAY; } // endif for (int i = 0; i < kids.length; i++) { Object kid = kids[i]; // only scan an object if it is checked: if (viewer.getChecked(kid)) excludeNodeAndChildren(tcp, kid); } // endfor setExcluded(nodeData, true); viewer.setChecked(nodeData, false); } public void checkRequiredChildren( Object nodeData ) { // I am checked, make sure my non-clickable children are checked: Object[] kids; if (!XmlDocumentUtil.isIncomplete(nodeData) && content.hasChildren(nodeData)) { kids = content.getChildren(nodeData); } else { kids = EMPTY_OBJECT_ARRAY; } // endif for (int i = 0; i < kids.length; i++) { Object kid = kids[i]; // need to check any included children that are not checkable: if (!isUserCheckable(kid)) { // this is safe, because these entries don't have an excluded property: viewer.setChecked(kid, true); setExcluded(kid, false); } // endif } // endfor } private void checkDefaultChildren( Object nodeData ) { // I am checked, make sure my some of my children are checked: Object[] kids; if (!XmlDocumentUtil.isIncomplete(nodeData) && content.hasChildren(nodeData)) { kids = content.getChildren(nodeData); } else { kids = EMPTY_OBJECT_ARRAY; } // endif for (int i = 0; i < kids.length; i++) { Object kid = kids[i]; // need to check any included children that are "simple": if (kid instanceof XmlAttribute || (kid instanceof XmlElement && !XmlDocumentUtil.hasComplexType(kid))) { // this is safe, because these entries don't have an excluded property: viewer.setChecked(kid, true); setExcluded(kid, false); } // endif } // endfor } private void checkRequiredAncestors( Object nodeData ) { // I am checked, make sure all of my ancestors are checked: // (this for things like XmlContainerNodes) Object parent = content.getParent(nodeData); // walk ancestors to the top or until I hit a checked // one (since its ancestors are already checked): while (parent != null && !viewer.getChecked(parent)) { // need to check any ancestor that is not checked: viewer.setChecked(parent, true); setExcluded(parent, false); checkRequiredChildren(parent); parent = content.getParent(parent); } // endwhile } } // endclass VDocTreeViewerController static class VDocLabelProvider extends ModelExplorerLabelProvider implements DisposeListener { // Instance variables: Map classToLabelProvider = new HashMap(); private HashMap decoratedImageRegistry = new HashMap(); private List temporaryImages = new ArrayList(); private List newDecoratedImages = new ArrayList(); // Utility methods: private String getDatatypeText( XSDComponent xsdc ) { if (xsdc == null) { return ""; //$NON-NLS-1$ } // endif Class xclass = xsdc.getClass(); IItemLabelProvider provider = (IItemLabelProvider)classToLabelProvider.get(xclass); if (provider == null) { AdapterFactory adapterFactory = ModelerCore.getMetamodelRegistry().getAdapterFactory(); provider = (IItemLabelProvider)adapterFactory.adapt(xsdc, IItemLabelProvider.class); classToLabelProvider.put(xclass, provider); } // endif return provider.getText(xsdc); } // Overrides: @Override public String getText( Object element ) { if (element instanceof XmlDocumentNode) { XmlDocumentNode xdn = (XmlDocumentNode)element; String rv = getDatatypeText(xdn.getXsdComponent()); if (rv == null || rv.length() == 0) { return super.getText(element); } // endif return rv; } // endif return super.getText(element); } /** * @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object) */ @Override public Image getImage( Object element ) { Image originalImage = super.getImage(element); boolean recursive = XmlDocumentUtil.isRecursive(element); boolean incomplete = XmlDocumentUtil.hasComplexType(element) && XmlDocumentUtil.isIncomplete(element); int cardinality = XmlDocumentUtil.getCardinality(element); ImageKey imageKey = new ImageKey(originalImage, recursive, incomplete, cardinality); return getDecoratedImage(imageKey); } private Image getDecoratedImage( ImageKey key ) { Image decoratedImage = (Image)decoratedImageRegistry.get(key); if (decoratedImage == null) { Image originalImage = key.getImage(); decoratedImage = originalImage; if (key.isRecursive()) { decoratedImage = decorateWithTopLeftImage(decoratedImage, ModelerXmlUiPlugin.getDefault().getImage(ModelerXmlUiConstants.Images.RECURSIVE)); } if (key.isIncomplete()) { if (decoratedImage != originalImage) { temporaryImages.add(decoratedImage); } decoratedImage = decorateWithTopRightImage(decoratedImage, ModelerXmlUiPlugin.getDefault().getImage(ModelerXmlUiConstants.Images.INCOMPLETE)); } Image cardinalityImage = getCardinalityImage(key.getCardinality()); if (cardinalityImage != null) { if (decoratedImage != originalImage) { temporaryImages.add(decoratedImage); } decoratedImage = decorateWithBottomLeftImage(decoratedImage, cardinalityImage); } disposeTemporaryImages(); if (decoratedImage != originalImage) { newDecoratedImages.add(decoratedImage); } decoratedImageRegistry.put(key, decoratedImage); } return decoratedImage; } private Image getCardinalityImage( int cardinality ) { Image cardinalityImage = null; switch (cardinality) { case XmlDocumentUtil.XSD_OCCURRENCE_N: cardinalityImage = ModelerXmlUiPlugin.getDefault().getImage(ModelerXmlUiConstants.Images.XSD_OCCURRENCE_N); break; case XmlDocumentUtil.XSD_OCCURRENCE_NToM: cardinalityImage = ModelerXmlUiPlugin.getDefault().getImage(ModelerXmlUiConstants.Images.XSD_OCCURRENCE_NToM); break; case XmlDocumentUtil.XSD_OCCURRENCE_NToUnbounded: cardinalityImage = ModelerXmlUiPlugin.getDefault().getImage(ModelerXmlUiConstants.Images.XSD_OCCURRENCE_NToUnbounded); break; case XmlDocumentUtil.XSD_OCCURRENCE_Zero: cardinalityImage = ModelerXmlUiPlugin.getDefault().getImage(ModelerXmlUiConstants.Images.XSD_OCCURRENCE_Zero); break; case XmlDocumentUtil.XSD_OCCURRENCE_ZeroToOne: cardinalityImage = ModelerXmlUiPlugin.getDefault().getImage(ModelerXmlUiConstants.Images.XSD_OCCURRENCE_ZeroToOne); break; case XmlDocumentUtil.XSD_OCCURRENCE_ZeroToN: cardinalityImage = ModelerXmlUiPlugin.getDefault().getImage(ModelerXmlUiConstants.Images.XSD_OCCURRENCE_ZeroToN); break; case XmlDocumentUtil.XSD_OCCURRENCE_ZeroToUnbounded: cardinalityImage = ModelerXmlUiPlugin.getDefault().getImage(ModelerXmlUiConstants.Images.XSD_OCCURRENCE_ZeroToUnbounded); break; case XmlDocumentUtil.XSD_OCCURRENCE_One: cardinalityImage = ModelerXmlUiPlugin.getDefault().getImage(ModelerXmlUiConstants.Images.XSD_OCCURRENCE_One); break; case XmlDocumentUtil.XSD_OCCURRENCE_OneToN: cardinalityImage = ModelerXmlUiPlugin.getDefault().getImage(ModelerXmlUiConstants.Images.XSD_OCCURRENCE_OneToN); break; case XmlDocumentUtil.XSD_OCCURRENCE_OneToUnbounded: cardinalityImage = ModelerXmlUiPlugin.getDefault().getImage(ModelerXmlUiConstants.Images.XSD_OCCURRENCE_OneToUnbounded); break; default: } return cardinalityImage; } private Image decorateWithTopLeftImage( Image originalImage, Image overlayImage ) { OverlayImageIcon overlayIcon = new OverlayImageIcon(originalImage, overlayImage, OverlayImageIcon.TOP_LEFT); return overlayIcon.getImage(); } private Image decorateWithTopRightImage( Image originalImage, Image overlayImage ) { OverlayImageIcon overlayIcon = new OverlayImageIcon(originalImage, overlayImage, OverlayImageIcon.TOP_RIGHT); return overlayIcon.getImage(); } private Image decorateWithBottomLeftImage( Image originalImage, Image overlayImage ) { OverlayImageIcon overlayIcon = new OverlayImageIcon(originalImage, overlayImage, OverlayImageIcon.BOTTOM_LEFT); return overlayIcon.getImage(); } private void disposeTemporaryImages() { while (temporaryImages.size() > 0) { Image tempImage = (Image)temporaryImages.remove(temporaryImages.size() - 1); tempImage.dispose(); } } private void disposeNewDecoratedImages() { while (newDecoratedImages.size() > 0) { Image tempImage = (Image)newDecoratedImages.remove(newDecoratedImages.size() - 1); tempImage.dispose(); } } /** * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent) */ @Override public void widgetDisposed( DisposeEvent e ) { disposeNewDecoratedImages(); } } private static class ImageKey { private Image image; private boolean recursive; private boolean incomplete; private int cardinality; /** * @param image * @param recursive * @param incomplete * @param cardinality */ public ImageKey( Image image, boolean recursive, boolean incomplete, int cardinality ) { super(); this.image = image; this.recursive = recursive; this.incomplete = incomplete; this.cardinality = cardinality; } /** * @return Returns the cardinality. */ public int getCardinality() { return cardinality; } /** * @return Returns the complete. */ public boolean isIncomplete() { return incomplete; } /** * @return Returns the image. */ public Image getImage() { return image; } /** * @return Returns the recursive. */ public boolean isRecursive() { return recursive; } /** * Returns <code>true</code> if this <code>ImageKey</code> is the same as the o argument. * * @return <code>true</code> if this <code>ImageKey</code> is the same as the o argument. */ @Override public boolean equals( Object o ) { if (this == o) { return true; } if (o == null) { return false; } if (o.getClass() != getClass()) { return false; } ImageKey castedObj = (ImageKey)o; return ((this.image == null ? castedObj.image == null : this.image.equals(castedObj.image)) && (this.recursive == castedObj.recursive) && (this.incomplete == castedObj.incomplete) && (this.cardinality == castedObj.cardinality)); } /** * Override hashCode. * * @return the Objects hashcode. */ @Override public int hashCode() { int hashCode = 1; hashCode = 31 * hashCode + (image == null ? 0 : image.hashCode()); hashCode = 31 * hashCode + (recursive ? 1231 : 1237); hashCode = 31 * hashCode + cardinality; return hashCode; } } class VDocSelectionListener implements ISelectionChangedListener { private TreeItem lastColored; @Override public void selectionChanged( SelectionChangedEvent event ) { // unhighlight previous: if (lastColored != null && !lastColored.isDisposed()) { lastColored.setBackground(null); } // endif // highlight recursive root if needed: Object nodeData = SelectionUtilities.getSelectedObject(event.getSelection()); if (XmlDocumentUtil.isRecursive(nodeData)) { // save the type of the selected thing. Note that we can safely // cast because only XmlElements can be recursive: XSDTypeDefinition sType = XmlDocumentUtil.findXSDType(nodeData); // should be only one selected: TreeItem parent = viewer.getTree().getSelection()[0].getParentItem(); while (parent != null) { // need to color any ancestor that is of the same type: Object parentData = parent.getData(); if (parentData instanceof XmlElement) { // only check elements... nothing else could have a recursive nature: XmlElement parentElement = (XmlElement)parentData; XSDTypeDefinition pType = XmlDocumentUtil.findXSDType(parentElement); if (pType.equals(sType)) { lastColored = parent; parent.setBackground(DiagramUiConstants.Colors.VIRTUAL_GROUP_BKGRND); break; // remove to highlight all roots sharing this type. } // endif } // endif parent = parent.getParentItem(); } // endwhile } // endif } } // endclass VDocSelectionListener }