001 /* 002 * $Id: McIDASVXmlUi.java,v 1.26 2012/03/12 18:16:23 jbeavers Exp $ 003 * 004 * This file is part of McIDAS-V 005 * 006 * Copyright 2007-2012 007 * Space Science and Engineering Center (SSEC) 008 * University of Wisconsin - Madison 009 * 1225 W. Dayton Street, Madison, WI 53706, USA 010 * https://www.ssec.wisc.edu/mcidas 011 * 012 * All Rights Reserved 013 * 014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 015 * some McIDAS-V source code is based on IDV and VisAD source code. 016 * 017 * McIDAS-V is free software; you can redistribute it and/or modify 018 * it under the terms of the GNU Lesser Public License as published by 019 * the Free Software Foundation; either version 3 of the License, or 020 * (at your option) any later version. 021 * 022 * McIDAS-V is distributed in the hope that it will be useful, 023 * but WITHOUT ANY WARRANTY; without even the implied warranty of 024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 025 * GNU Lesser Public License for more details. 026 * 027 * You should have received a copy of the GNU Lesser Public License 028 * along with this program. If not, see http://www.gnu.org/licenses. 029 */ 030 031 package edu.wisc.ssec.mcidasv.ui; 032 033 import java.awt.Component; 034 import java.awt.event.ActionEvent; 035 import java.util.HashMap; 036 import java.util.List; 037 import java.util.Map; 038 039 import javax.swing.ImageIcon; 040 import javax.swing.JComponent; 041 import javax.swing.event.HyperlinkEvent; 042 import javax.swing.event.HyperlinkListener; 043 import javax.swing.event.HyperlinkEvent.EventType; 044 045 import org.w3c.dom.Element; 046 import org.w3c.dom.NamedNodeMap; 047 import org.w3c.dom.Node; 048 import org.w3c.dom.NodeList; 049 050 import ucar.unidata.idv.IntegratedDataViewer; 051 import ucar.unidata.idv.ViewManager; 052 import ucar.unidata.idv.ui.IdvComponentGroup; 053 import ucar.unidata.idv.ui.IdvComponentHolder; 054 import ucar.unidata.idv.ui.IdvUIManager; 055 import ucar.unidata.idv.ui.IdvWindow; 056 import ucar.unidata.idv.ui.IdvXmlUi; 057 import ucar.unidata.ui.ComponentHolder; 058 import ucar.unidata.ui.HtmlComponent; 059 import ucar.unidata.util.GuiUtils; 060 import ucar.unidata.util.IOUtil; 061 import ucar.unidata.xml.XmlUtil; 062 063 import edu.wisc.ssec.mcidasv.util.TreePanel; 064 065 /** 066 * <p> 067 * McIDAS-V mostly extends this class to preempt the IDV. McIDAS-V needs to 068 * control some HTML processing, ensure that {@link McvComponentGroup}s 069 * and {@link McvComponentHolder}s are created, and handle some special 070 * problems that occur when attempting to load bundles that do not contain 071 * component groups. 072 * </p> 073 */ 074 @SuppressWarnings("unchecked") 075 public class McIDASVXmlUi extends IdvXmlUi { 076 077 /** 078 * Maps an ID to an {@link Element}. 079 */ 080 // private Hashtable<String, Element> idToElement; 081 private Map<String, Element> idToElement; 082 083 /** Avoids unneeded getIdv() calls. */ 084 private IntegratedDataViewer idv; 085 086 /** 087 * Keep around a reference to the window we were built for, useful for 088 * associated component groups with the appropriate window. 089 */ 090 private IdvWindow window; 091 092 public McIDASVXmlUi(IntegratedDataViewer idv, Element root) { 093 super(idv, root); 094 if (idToElement == null) { 095 idToElement = new HashMap<String, Element>(); 096 } 097 } 098 099 public McIDASVXmlUi(IdvWindow window, List viewManagers, 100 IntegratedDataViewer idv, Element root) 101 { 102 super(window, viewManagers, idv, root); 103 this.idv = idv; 104 this.window = window; 105 if (idToElement == null) { 106 idToElement = new HashMap<String, Element>(); 107 } 108 } 109 110 /** 111 * Convert the &gt; and &lt; entities to > and <. 112 * 113 * @param text The text you'd like to convert. 114 * 115 * @return The converted text! 116 */ 117 private static String decodeHtml(String text) { 118 return text.replace(">", ">").replace("<", ">"); 119 } 120 121 /** 122 * Add the component 123 * 124 * @param id id 125 * @param component component 126 */ 127 @Override public void addComponent(String id, Element component) { 128 // this needs to be here because even if you create idToElement in the 129 // constructor, this method will get called from 130 // ucar.unidata.xml.XmlUi#initialize(Element) before control has 131 // returned to the McIDASVXmlUi constructor! 132 if (idToElement == null) { 133 idToElement = new HashMap<String, Element>(); 134 } 135 super.addComponent(id, component); 136 idToElement.put(id, component); 137 } 138 139 /** 140 * <p> 141 * Overridden so that any attempts to generate 142 * {@link IdvComponentGroup}s or {@link IdvComponentHolder}s will return 143 * the respective McIDAS-V equivalents. 144 * </p> 145 * 146 * <p> 147 * It makes things like the draggable tabs possible. 148 * </p> 149 * 150 * @param node The XML representation of the desired component group. 151 * 152 * @return An honest-to-goodness McIDASVComponentGroup based upon the 153 * contents of <code>node</code>. 154 * 155 * @see ucar.unidata.idv.ui.IdvXmlUi#makeComponentGroup(Element) 156 */ 157 @Override protected IdvComponentGroup makeComponentGroup(Element node) { 158 McvComponentGroup group = new McvComponentGroup(idv, "", window); 159 group.initWith(node); 160 161 NodeList elements = XmlUtil.getElements(node); 162 for (int i = 0; i < elements.getLength(); i++) { 163 Element child = (Element)elements.item(i); 164 165 String tag = child.getTagName(); 166 167 if (tag.equals(IdvUIManager.COMP_MAPVIEW) 168 || tag.equals(IdvUIManager.COMP_VIEW)) 169 { 170 ViewManager viewManager = getViewManager(child); 171 group.addComponent(new McvComponentHolder(idv, viewManager)); 172 } 173 else if (tag.equals(IdvUIManager.COMP_COMPONENT_CHOOSERS)) { 174 IdvComponentHolder comp = 175 new McvComponentHolder(idv, "choosers"); 176 comp.setType(IdvComponentHolder.TYPE_CHOOSERS); 177 comp.setName(XmlUtil.getAttribute(child, "name", "Choosers")); 178 group.addComponent(comp); 179 } 180 else if (tag.equals(IdvUIManager.COMP_COMPONENT_SKIN)) { 181 IdvComponentHolder comp = new McvComponentHolder(idv, 182 XmlUtil.getAttribute(child, "url")); 183 184 comp.setType(IdvComponentHolder.TYPE_SKIN); 185 comp.setName(XmlUtil.getAttribute(child, "name", "UI")); 186 group.addComponent(comp); 187 } 188 else if (tag.equals(IdvUIManager.COMP_COMPONENT_HTML)) { 189 String text = XmlUtil.getChildText(child); 190 text = new String(XmlUtil.decodeBase64(text.trim())); 191 ComponentHolder comp = new HtmlComponent("Html Text", text); 192 comp.setShowHeader(false); 193 comp.setName(XmlUtil.getAttribute(child, "name", "HTML")); 194 group.addComponent(comp); 195 } 196 else if (tag.equals(IdvUIManager.COMP_DATASELECTOR)) { 197 group.addComponent(new McvComponentHolder(idv, 198 idv.getIdvUIManager().createDataSelector(false, false))); 199 } 200 else if (tag.equals(IdvUIManager.COMP_COMPONENT_GROUP)) { 201 group.addComponent(makeComponentGroup(child)); 202 } 203 else { 204 System.err.println("Unknown component element:" 205 + XmlUtil.toString(child)); 206 } 207 } 208 return group; 209 } 210 211 /** 212 * <p> 213 * McIDAS-V overrides this so that it can seize control of some HTML 214 * processing in addition to attempting to associate newly-created 215 * {@link ucar.unidata.idv.ViewManager}s with ViewManagers found in a 216 * bundle. 217 * </p> 218 * 219 * <p> 220 * The latter is done so that McIDAS-V can load bundles that do not use 221 * component groups. A "dynamic skin" is built with ViewManagers 222 * for each ViewManager in the bundle. The "viewid" attribute of 223 * the dynamic skin ViewManager is the name of the 224 * {@link ucar.unidata.idv.ViewDescriptor} from the bundled ViewManager. 225 * <tt>createViewManager()</tt> is used to actually associate the new 226 * ViewManager with its bundled ViewManager. 227 * </p> 228 * 229 * @param node The XML describing the component to be created. 230 * @param id <tt>node</tt>'s ID. 231 * 232 * @return The {@link java.awt.Component} described by <tt>node</tt>. 233 * 234 * @see ucar.unidata.idv.ui.IdvXmlUi#createComponent(Element, String) 235 * @see edu.wisc.ssec.mcidasv.ui.McIDASVXmlUi#createViewManager(Element) 236 */ 237 @Override public Component createComponent(Element node, String id) { 238 Component comp = null; 239 String tagName = node.getTagName(); 240 if (tagName.equals(TAG_HTML)) { 241 String text = getAttr(node, ATTR_TEXT, NULLSTRING); 242 text = decodeHtml(text); 243 if (text == null) { 244 String url = getAttr(node, ATTR_URL, NULLSTRING); 245 if (url != null) { 246 text = IOUtil.readContents(url, (String)null); 247 } 248 if (text == null) { 249 text = XmlUtil.getChildText(node); 250 } 251 } 252 HyperlinkListener linkListener = new HyperlinkListener() { 253 public void hyperlinkUpdate(HyperlinkEvent e) { 254 if (e.getEventType() != EventType.ACTIVATED) 255 return; 256 257 String url; 258 if (e.getURL() == null) { 259 url = e.getDescription(); 260 } else { 261 url = e.getURL().toString(); 262 } 263 actionPerformed(new ActionEvent(this, 0, url)); 264 } 265 }; 266 Component[] comps = 267 GuiUtils.getHtmlComponent(text, linkListener, getAttr(node, 268 ATTR_WIDTH, 200), getAttr(node, ATTR_HEIGHT, 200)); 269 comp = comps[1]; 270 } else if (tagName.equals(UIManager.COMP_MAPVIEW) 271 || tagName.equals(UIManager.COMP_VIEW)) { 272 273 // if we're creating a VM for a dynamic skin that was created for 274 // a bundle, createViewManager() will return the bundled VM. 275 ViewManager vm = createViewManager(node); 276 if (vm != null) { 277 comp = vm.getContents(); 278 } else { 279 comp = super.createComponent(node, id); 280 } 281 } else if (tagName.equals(TAG_TREEPANEL)) { 282 comp = createTreePanel(node, id); 283 } else { 284 comp = super.createComponent(node, id); 285 } 286 287 return comp; 288 } 289 290 /** 291 * <p> 292 * Attempts to build a {@link ucar.unidata.idv.ViewManager} based upon 293 * <tt>node</tt>. If the XML has a "viewid" attribute, the 294 * value will be used to search for a ViewManager that has been cached by 295 * the McIDAS-V {@link UIManager}. If the UIManager has a matching 296 * ViewManager, we'll use the cached ViewManager to initialize a 297 * "blank" ViewManager. The cached ViewManager is then removed 298 * from the cache and deleted. This method will return <code>null</code> if 299 * no cached ViewManager was found. 300 * </p> 301 * 302 * <p> 303 * The ViewManager "cache" will only contain bundled ViewManagers 304 * that were not held in a component holder. This means that any 305 * ViewManager returned was created for a dynamic skin, but initialized 306 * with the contents of the corresponding bundled ViewManager. 307 * </p> 308 * 309 * @param node The XML description of the ViewManager that needs building. 310 * 311 * @return Null if there was no cached ViewManager, otherwise a ViewManager 312 * that has been initialized with a bundled ViewManager. 313 */ 314 private ViewManager createViewManager(final Element node) { 315 final String viewId = getAttr(node, "viewid", NULLSTRING); 316 ViewManager vm = null; 317 if (viewId != null) { 318 ViewManager old = UIManager.savedViewManagers.remove(viewId); 319 if (old != null) { 320 vm = getViewManager(node); 321 vm.initWith(old); 322 old.destroy(); 323 } 324 } 325 return vm; 326 } 327 328 private TreePanel createTreePanel(final Element node, final String id) { 329 330 TreePanel treePanel = 331 new TreePanel(getAttr(node, ATTR_USESPLITPANE, false), 332 getAttr(node, ATTR_TREEWIDTH, -1)); 333 334 List<Element> kids = XmlUtil.getListOfElements(node); 335 336 for (Element kid : kids) { 337 Component comp = xmlToUi(kid); 338 if (comp == null) { 339 continue; 340 } 341 342 String label = getAttr(kid, ATTR_TITLE, ""); 343 344 ImageIcon icon = getAttr(kid, ATTR_ICON, (ImageIcon)null); 345 String cat = getAttr(kid, ATTR_CATEGORY, (String)null); 346 if (XmlUtil.getAttribute(kid, ATTR_CATEGORYCOMPONENT, false)) { 347 treePanel.addCategoryComponent(cat, (JComponent)comp); 348 } else { 349 treePanel.addComponent((JComponent)comp, cat, label, icon); 350 } 351 } 352 treePanel.closeAll(); 353 treePanel.showPersistedSelection(); 354 return treePanel; 355 } 356 357 /** 358 * The xml nodes can contain an idref field. If so this returns the 359 * node that that id defines 360 * 361 * @param node node 362 * 363 * @return The node or the referenced node 364 */ 365 private Element getReffedNode(Element node) { 366 String idRef = getAttr(node, ATTR_IDREF, NULLSTRING); 367 if (idRef == null) { 368 return node; 369 } 370 371 Element reffedNode = (Element)idToElement.get(idRef); 372 if (reffedNode == null) { 373 throw new IllegalStateException("Could not find idref=" + idRef); 374 } 375 376 // TODO(unidata): Make a new copy of the node 377 // reffedNode = reffedNode.copy (); 378 NamedNodeMap map = node.getAttributes(); 379 for (int i = 0; i < map.getLength(); i++) { 380 Node n = map.item(i); 381 if (!n.getNodeName().equals(ATTR_IDREF)) { 382 reffedNode.setAttribute(n.getNodeName(), n.getNodeValue()); 383 } 384 } 385 return reffedNode; 386 } 387 }