/******************************************************************************* * Copyright (c) 2010, 2013 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 *******************************************************************************/ package org.eclipse.wst.xml.ui.internal.views.contentmodel; import java.util.ArrayList; import java.util.EmptyStackException; import java.util.List; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.ui.model.IWorkbenchAdapter; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.eclipse.wst.xml.core.internal.contentmodel.CMAttributeDeclaration; import org.eclipse.wst.xml.core.internal.contentmodel.CMDataType; import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration; import org.eclipse.wst.xml.core.internal.contentmodel.CMGroup; import org.eclipse.wst.xml.core.internal.contentmodel.CMNamedNodeMap; import org.eclipse.wst.xml.core.internal.contentmodel.CMNode; import org.eclipse.wst.xml.core.internal.contentmodel.util.CMVisitor; import org.eclipse.wst.xml.ui.internal.XMLUIMessages; import org.eclipse.wst.xml.ui.internal.XMLUIPlugin; import org.eclipse.wst.xml.ui.internal.editor.XMLEditorPluginImages; public class CMListWorkbenchAdapter extends CMVisitor implements IWorkbenchAdapter { private CMElementDeclaration fDeclaration; private String text; private ImageDescriptor image; private GroupStack fGroupStack; private CMDescriber fRoot; private static final Object[] EMPTY = new Object[0]; public CMListWorkbenchAdapter(CMElementDeclaration decl) { if (decl != null) { text = getFormattedLabel(decl.getNodeName(), decl.getMinOccur(), decl.getMaxOccur(), decl.getDataType()); fDeclaration = decl; image = AbstractUIPlugin.imageDescriptorFromPlugin(XMLUIPlugin.ID, XMLEditorPluginImages.IMG_OBJ_ELEMENT); fGroupStack = new GroupStack(); fRoot = new CMDescriber(decl); } else { text = XMLUIMessages.ContentModel_ElementNotDescribed; image = AbstractUIPlugin.imageDescriptorFromPlugin(XMLUIPlugin.ID, XMLEditorPluginImages.IMG_OBJ_WARNING_OBJ); } } public Object[] getChildren(Object o) { /* TODO Use the CMElementDeclaration and ModelQuery to get the available children of the root element */ return fRoot != null ? fRoot.getChildren(o) : EMPTY; } public ImageDescriptor getImageDescriptor(Object object) { return image; } public String getLabel(Object o) { return text; } public Object getParent(Object o) { return fDeclaration; } /** * Formats the label for an element declaration. The format will be: <b>name</b> [<i>min</i>..<i>max</i>], where <i>max</i> when unbounded is represented by <b>*</b> * @param name The name of the element declaration * @param min The minimum number of occurrences of the element * @param max The maximum number of occurrences of the element * @return The formatted label String */ static String getFormattedLabel(String name, int min, int max, CMDataType dataType) { StringBuffer buffer = new StringBuffer(name); buffer.append('['); buffer.append(min); buffer.append(".."); //$NON-NLS-1$ buffer.append((max >= 0 ? Integer.toString(max) : "*")); //$NON-NLS-1$ buffer.append(']'); if (dataType != null) { buffer.append(" {"); //$NON-NLS-1$ buffer.append(dataType.getDataTypeName()); buffer.append('}'); } return buffer.toString(); } /** * Workbench adapter that describes its CMNode. It calculates its children lazily using the CMVisitor * to identify immediate children (i.e., the content of child nodes is not visited). * */ private class CMDescriber extends CMVisitor implements IWorkbenchAdapter { List fChildren = null; ImageDescriptor fImage; String label; CMNode root; public CMDescriber(CMNode node) { root = node; } public Object[] getChildren(Object o) { if (fChildren == null) { fChildren = new ArrayList(); if (root.getNodeType() == CMNode.ELEMENT_DECLARATION) { CMElementDeclaration ed = (CMElementDeclaration) root; CMNamedNodeMap nodeMap = ed.getAttributes(); int size = nodeMap.getLength(); for (int i = 0; i < size; i++) { visitCMNode(nodeMap.item(i)); } visitCMNode(ed.getContent()); } } return fChildren.toArray(); } public ImageDescriptor getImageDescriptor(Object object) { return fImage; } public void setImage(String path) { fImage = AbstractUIPlugin.imageDescriptorFromPlugin(XMLUIPlugin.ID, path); } public String getLabel(Object o) { return label; } public Object getParent(Object o) { return root; } public void visitCMGroup(CMGroup e) { fGroupStack.push(e.getMaxOccur()); super.visitCMGroup(e); fGroupStack.pop(); } public void visitCMAttributeDeclaration(CMAttributeDeclaration ad) { CMDescriber describer = new CMDescriber(ad); describer.label = ad.getNodeName(); describer.setImage(XMLEditorPluginImages.IMG_OBJ_ATTRIBUTE); fChildren.add(describer); } public void visitCMElementDeclaration(CMElementDeclaration ed) { CMDescriber describer = new CMDescriber(ed); // If the parent group stack containing this element declaration is unbounded, the element within is unbounded as well describer.label = getFormattedLabel(ed.getNodeName(), ed.getMinOccur(), (!fGroupStack.isEmpty() && fGroupStack.peek() < 0) ? -1 : ed.getMaxOccur(), ed.getDataType()); describer.setImage(XMLEditorPluginImages.IMG_OBJ_ELEMENT); fChildren.add(describer); } } /** * A stack of integers used to determine if an element declaration is unbounded * */ private static class GroupStack { int[] stack; int size; public GroupStack() { stack = new int[5]; } public void push(int i) { if (size >= stack.length) { int[] tmp = stack; stack = new int[stack.length * 2]; System.arraycopy(tmp, 0, stack, 0, tmp.length); } stack[size++] = i; } public int pop() { if (isEmpty()) throw new EmptyStackException(); return stack[--size]; } public boolean isEmpty() { return size == 0; } public int peek() { if (isEmpty()) throw new EmptyStackException(); return stack[size - 1]; } } }