/* * � Copyright IBM Corp. 2011 * * Licensed 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 com.ibm.xsp.extlib.designer.tooling.propeditor; import java.util.ArrayList; import java.util.Iterator; import java.util.StringTokenizer; import org.eclipse.swt.widgets.Control; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.ibm.commons.iloader.node.lookups.api.ILookup; import com.ibm.commons.iloader.node.lookups.api.StringLookup; import com.ibm.commons.swt.data.editors.api.AbstractComboEditor; import com.ibm.commons.swt.data.editors.api.CompositeEditor; import com.ibm.commons.util.StringUtil; import com.ibm.commons.xml.DOMUtil; import com.ibm.designer.domino.constants.XSPAttributeNames; import com.ibm.designer.domino.xsp.api.util.XPagesDOMUtil; /** * * This property editor will search through the DOM of an XPage and get the id's for all the controls on the page. * These id's will be added to a comboBox so that a user can select a control id. The id of the control that this * editor is bound to will not appear in the list. (So if you are setting the for attribute on a label, the id of * label itself will not be present in the comboBox). * * You can optionally specify parameters for this property editor. The parameters must be a list a controls that * you want to show id's for. So if you specify the button and inputText tags, then the comboBox will only be populated * with id's from button and inputText tags found on the XPage. * * The parameters can be specified either separated by line delimiters or commas. Like this * <editor-parameter> * <control-namespace>|<control-tagName>,<control-namespace>|<control-tagName>,..... * </editor-parameter> * or * <editor-parameter> * <control-namespace>|<control-tagName> * <control-namespace>|<control-tagName> * ..... * </editor-parameter> * * Here is a real example * <editor-parameter> * http://www.ibm.com/xsp/core|button * http://www.ibm.com/xsp/core|inputText * </editor-parameter> */ public class XPageControlIDPicker extends AbstractComboEditor { //The parameters defined for this picker private String _parameters; //The separator we use to separate the namespace and tagName in the parameter private static String SEPARATOR = "|"; /** * Default Constructor. */ public XPageControlIDPicker() { super(); } /** * Constructor called with parameters specified for the property editor. * @param parameters */ public XPageControlIDPicker(String parameters) { super(); _parameters = parameters; } /* * (non-Javadoc) * @see com.ibm.commons.swt.data.editors.api.AbstractComboEditor#createControl(com.ibm.commons.swt.data.editors.api.CompositeEditor) */ @Override public Control createControl(CompositeEditor parent) { return super.createControl(parent); } /* * (non-Javadoc) * @see com.ibm.commons.swt.data.editors.api.AbstractComboEditor#initControlValue(com.ibm.commons.swt.data.editors.api.CompositeEditor, java.lang.String) */ @Override public void initControlValue(CompositeEditor parent, String value) { super.initControlValue(parent, value); //create the lookup of control IDs on the XPage ILookup controlPickerLookkup = createControlPickerLookup(parent); //if we manager to find IDs to add to the comboBox, then set that as the lookup for the ComboBox. if(null != controlPickerLookkup){ setLookup(parent.getEditorControl(), controlPickerLookkup); } } /** * This method will create the lookup to populate the comboBox with control ID's * @param compEditor * @return */ public ILookup createControlPickerLookup(CompositeEditor compEditor) { Node controlNode = getNodeForEditor(compEditor); if(null != controlNode){ //if no parameters are specified then we can just use XPagesDOMUtil to get a list of controlIds on the page if(StringUtil.isEmpty(_parameters)){ String[] controlIds = XPagesDOMUtil.getIds(controlNode.getOwnerDocument(), controlNode); if(null != controlIds && controlIds.length>0){ return new StringLookup(controlIds); } } //if we have parameters then we need a filtered list of controlIds else{ return getFilteredControlLookup(controlNode); } } //we failed to create the control lookup. return null; } /** * If parameters have been specified, this method will get the filtered lookup of controls, containing only id's of controls * specified in the parameters. * @param controlNode * @return */ private ILookup getFilteredControlLookup(Node controlNode){ ArrayList<String> controlIdsList = new ArrayList<String>(); if(StringUtil.isNotEmpty(_parameters)){ //parse the parameters to get a usable control list ArrayList<NodeInfo> parameters = getParameters(); if(parameters.size()>0){ //get the document level node for the XPage Document ownerDoc = controlNode.getOwnerDocument(); if(null != ownerDoc){ //get every node and child node on the XPage. NodeList childNodes = DOMUtil.getChildNodesToNLevels(ownerDoc); for(int i=0; i<childNodes.getLength(); i++){ Node childNode = childNodes.item(i); //if this child node not the node for this editor and its namespace and tagmName are specified in //the parameters, and it has an id, then add its id to the list if(childNode != controlNode && isNodeImportant(childNode, parameters)){ String id = XPagesDOMUtil.getAttribute((Element)childNode, XSPAttributeNames.XSP_ATTR_ID); if(StringUtil.isNotEmpty(id)){ controlIdsList.add(id); } } } //we have found ids to display in the comboBox, return them if(controlIdsList.size()>0){ String[] controlIds = controlIdsList.toArray(new String[controlIdsList.size()]); if(null != controlIds && controlIds.length>0){ return new StringLookup(controlIds); } } } } } //we failed to find control ids to put in the comboBox return null; } /** * This method checks to see if a given Nodes namespace and tagName are specified in the parameters for this property editor. * @param aNode * @param parameters * @return */ private boolean isNodeImportant(Node aNode, ArrayList<NodeInfo> parameters){ if(aNode.getNodeType() == Node.ELEMENT_NODE){ String namespace = aNode.getNamespaceURI(); String tagName = aNode.getLocalName(); if(StringUtil.isNotEmpty(namespace) && StringUtil.isNotEmpty(tagName)){ Iterator<NodeInfo> paramIter = parameters.iterator(); while(paramIter.hasNext()){ NodeInfo importantNode = paramIter.next(); String importantNodeNamespace = importantNode.getNamespace(); String importantNodeTagName = importantNode.getTagName(); if(StringUtil.isNotEmpty(importantNodeNamespace) && StringUtil.isNotEmpty(importantNodeTagName) && StringUtil.equalsIgnoreCase(importantNodeNamespace, namespace) && StringUtil.equalsIgnoreCase(importantNodeTagName, tagName)){ return true; } } } } return false; } /** * This method parses the parameters string into a usable list of control info. * @return */ private ArrayList<NodeInfo> getParameters(){ ArrayList<NodeInfo> controlsToInclude = new ArrayList<NodeInfo>(); //we allow parameters separated by commas or carriage returns, so those are our tokens. String tokens = ",\r\n"; // $NON-NLS-1$ StringTokenizer st = new StringTokenizer(_parameters, tokens); while (st.hasMoreTokens()) { String line = st.nextToken().trim(); if(StringUtil.isNotEmpty(line)) { String namespace; String tagName; //namespace and tagName are separated by colons. int pos = line.indexOf(SEPARATOR); if(pos>=0) { namespace = line.substring(0,pos); tagName = line.substring(pos+1); controlsToInclude.add(new NodeInfo(namespace, tagName)); } } } if(controlsToInclude.size()>0){ return controlsToInclude; } return null; } /** * This class is just used to store information about controls specified in the parameters of this property editor. */ private class NodeInfo{ String _namespace; String _tagName; public NodeInfo( String namespace, String tagName){ _namespace = namespace; _tagName = tagName; } public String getNamespace() { return _namespace; } public String getTagName() { return _tagName; } } /** * This method will get the Node that this editor is associated with. So if you are using this editor to set the for * attribute on a label, then this method will return the node in the DOM for that label control. * @param compEditor * @return */ private Node getNodeForEditor(CompositeEditor compEditor){ Object contextObject = getContextObject(); if(contextObject instanceof Node){ return (Node)contextObject; } return null; } /* * (non-Javadoc) * @see com.ibm.commons.swt.data.editors.api.AbstractComboEditor#isEditable() */ @Override public boolean isEditable() { return true; } /* * (non-Javadoc) * @see com.ibm.commons.swt.data.editors.api.AbstractComboEditor#isFirstBlankLine() */ @Override public boolean isFirstBlankLine() { return true; } }