001 /* 002 * file Browser.java 003 * 004 * Licensed Materials - Property of IBM 005 * Restricted Materials of IBM - you are allowed to copy, modify and 006 * redistribute this file as part of any program that interfaces with 007 * IBM Rational CM API. 008 * 009 * com.ibm.rational.stp.client.samples.Browser 010 * 011 * (C) Copyright IBM Corporation 2004, 2008. All Rights Reserved. 012 * Note to U.S. Government Users Restricted Rights: Use, duplication or 013 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 014 */ 015 package com.ibm.rational.stp.client.samples; 016 017 import java.awt.BorderLayout; 018 import java.awt.Cursor; 019 import java.awt.FlowLayout; 020 import java.awt.event.ActionEvent; 021 import java.awt.event.ActionListener; 022 import java.io.File; 023 import java.io.FileOutputStream; 024 import java.util.Arrays; 025 import java.util.Comparator; 026 import java.util.List; 027 028 import javax.swing.DefaultComboBoxModel; 029 import javax.swing.JButton; 030 import javax.swing.JComboBox; 031 import javax.swing.JFrame; 032 import javax.swing.JLabel; 033 import javax.swing.JOptionPane; 034 import javax.swing.JPanel; 035 import javax.wvcm.PropertyRequestItem; 036 import javax.wvcm.Resource; 037 import javax.wvcm.ResourceList; 038 import javax.wvcm.WvcmException; 039 import javax.wvcm.PropertyNameList.PropertyName; 040 import javax.wvcm.PropertyRequestItem.NestedPropertyName; 041 import javax.wvcm.PropertyRequestItem.PropertyRequest; 042 043 import com.ibm.rational.stp.client.samples.BrowserDataModel.Operations; 044 import com.ibm.rational.wvcm.stp.StpException; 045 import com.ibm.rational.wvcm.stp.StpProperty; 046 import com.ibm.rational.wvcm.stp.StpPropertyException; 047 import com.ibm.rational.wvcm.stp.StpProvider; 048 import com.ibm.rational.wvcm.stp.StpResource; 049 import com.ibm.rational.wvcm.stp.StpException.StpReasonCode; 050 import com.ibm.rational.wvcm.stp.StpProperty.MetaPropertyName; 051 import com.ibm.rational.wvcm.stp.cq.CqDbSet; 052 import com.ibm.rational.wvcm.stp.cq.CqProvider; 053 import com.ibm.rational.wvcm.stp.cq.CqUserDb; 054 055 /** 056 * This is a SWING application that reads and displays the properties of a 057 * resource using the generic Resource/Property interfaces of the CM API. 058 * Starting from the name or type of a resource, a user can read and display 059 * resource properties, following references from one resource to the next. 060 * <p> 061 * The user can start browsing either by entering the object selector for the 062 * resource to be viewed or the user can select a resource type and ask the API 063 * for all known folders containing that resource type. 064 * <p> 065 * In the latter case, the list of folders is displayed by 066 * {@link Browser#showResourceList Browser.showResourceList}. By selecting an 067 * entry in the display for a Resource, ResourceList or Property.List value, the 068 * user can traverse to that object or list of objects and display them. 069 * <p> 070 * In the former case, the user types in the object selector for a specific 071 * resource he is interested in, such as a specific record. All properties of 072 * this record are then displayed by {@link Browser#showResource 073 * Browser.showResource} The Property.List property Record.ALL_FIELD_VALUES 074 * contains all the schema-defined fields of the record and could be selected to 075 * view those fields. 076 * <p> 077 * If a resource has content, that data can be displayed using 078 * {@link BrowserDataModel#showContent BrowserDataModel.showContent()}. 079 */ 080 public class Browser { 081 082 /** 083 * Displays a list of folders in which are found resources of a specified 084 * type or a resource at a given location. 085 * 086 * @param name 087 * Either a String specifying the type of resource for which a 088 * folder list is to be generated or a String containing the 089 * location of the resource to be displayed. Must not be null. 090 */ 091 static void showRoot(String name) { 092 // We've had to defer allocation of the provider until we're here on the 093 // event thread to satisfy the Apartment threading model used by COM. 094 if (g_provider == null) 095 try { 096 g_provider = Utilities.getProvider(); 097 } catch (Exception ex) { 098 Utilities.exception(null, "Provider", ex); 099 } 100 try { 101 if (name.equals(CLEAR_QUEST_DB_SETS)) { 102 showResourceList("ClearQuest Connections", 103 g_provider.cqProvider().doGetDbSetList(PROPS), 104 PROPS.toArray(), 105 true); 106 } else if (name.equals(CLEAR_CASE_WORKSPACES)) { 107 showResourceList("ClearCase Workspaces", 108 g_provider.ccProvider().getClientViewList(PROPS), 109 PROPS.toArray(), 110 true); 111 } else { 112 showResource("Resource " + name, (StpResource) g_provider 113 .resource(g_provider.location(name)), 114 META_PROPS.toArray(), true); 115 } 116 } catch (Throwable ex) { 117 Utilities.exception(null, "Show root", ex); 118 } 119 } 120 121 /** 122 * Displays the elements of a ResourceList in a table. 123 * 124 * @param title 125 * The title for the window in which the resources are displayed. 126 * @param resources 127 * The ResoureList containing the resources to be displayed. 128 * @param properties 129 * A PropertyName array containing the PropertyName's for the 130 * properties to be displayed for each Resource. These properties 131 * should be defined in the proxies in the ResourceList. 132 * @return The handle on the window in which the resource list is displayed. 133 */ 134 static JFrame showResourceList(String title, 135 final ResourceList<? extends StpResource> resources, 136 final NestedPropertyName<?>[] properties, 137 final boolean validOnly) { 138 // Generate the data model for displaying the resource list, 139 // one resource per row, one property per column 140 BrowserDataModel dataModel = new BrowserDataModel() { 141 public int getColumnCount() { 142 return 1+properties.length; 143 } 144 145 public int getRowCount() { 146 return resources.size(); 147 } 148 149 public Object getValueAt(int row, int col) { 150 if (col == 0) 151 return Utilities.resourceType(resources.get(row)); 152 153 try { 154 return resources.get(row).getProperty(properties[col - 1] 155 .getRoot()); 156 } catch (Throwable ex) { 157 // If an object name selector was to be displayed 158 // substitute the resource location if that property 159 // is unavailable. (Not all resources have them.) 160 return StpResource.USER_FRIENDLY_LOCATION 161 .equals(properties[col-1]) ? resources.get(row) 162 : exceptionImage(ex, resources.get(row)); 163 } 164 } 165 166 public String getColumnName(int col) { 167 return col==0? "resource-type": properties[col-1].getRoot().getName(); 168 } 169 170 // The only showable objects are Resource's, so use showResource() 171 void show(Object viewable) throws WvcmException { 172 StpResource res = (StpResource) viewable; 173 174 try { 175 m_frame.setCursor(Cursor 176 .getPredefinedCursor(Cursor.WAIT_CURSOR)); 177 showResource(Utilities.resourceType(res) + " " 178 + res.location().string(), res, 179 META_PROPS.toArray(), validOnly); 180 } finally { 181 m_frame.setCursor(Cursor 182 .getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 183 } 184 } 185 186 // Since each row represents a resource, each row is always 187 // showable 188 Object getViewable(int row) { 189 return resources.get(row); 190 } 191 192 private static final long serialVersionUID = 1L; 193 }; 194 195 return dataModel.showModel(title, false); 196 } 197 198 /** 199 * Retrieves all properties of a given resource and displays them in a 200 * table. 201 * 202 * @param title 203 * The title for the window in which the resource is displayed. 204 * @param res 205 * A Resource proxy for the resource to be displayed. 206 * @param metaProperties 207 * A MetaPropertyName[] containing the meta-properties to be 208 * displayed for each property of the resource. 209 * @return A handle for the window containing the display. 210 * @throws WvcmException 211 * if resource denoted by the given Resource proxy can not be 212 * read. 213 */ 214 static JFrame showResource(String title, 215 StpResource res, 216 final PropertyRequestItem.NestedPropertyName<?>[] metaProperties, 217 boolean validOnly) throws WvcmException { 218 return showPropertyList(title, 219 res, 220 StpResource.ALL_PROPERTIES, 221 metaProperties, 222 validOnly); 223 } 224 225 /** 226 * Retrieves the value of a Property.List-valued property from a given 227 * resource and displays the results in a table. The display includes an 228 * option to view the content of the resource as well as the resource or 229 * resource list referenced by any of the displayed properties. 230 * 231 * @param title 232 * The title for the window in which the property list is 233 * displayed 234 * @param resource 235 * A proxy for the Resource whose properties are to be displayed. 236 * @param property 237 * A PropertyName specifying the Property.List-valued property of 238 * the resource that is to be retrieved and displayed. 239 * @param metaProperties 240 * The meta-properties of each Property on the list that are to 241 * be retrieved and displayed. 242 * @return A handle for the window in which the Property.List is displayed. 243 * @throws WvcmException 244 * If the specified resource or property cannot be retrieved 245 * from the repository. 246 */ 247 static JFrame showPropertyList(final String title, 248 final StpResource resource, 249 final PropertyName<StpProperty.List<StpProperty<?>>> property, 250 final NestedPropertyName<?>[] metaProperties, 251 final boolean validOnly) 252 throws WvcmException { 253 PropertyRequest wantedProps = 254 new PropertyRequest(property.nest(StpProperty.VALUE 255 .nest(metaProperties))); 256 final StpResource res = (StpResource) resource.doReadProperties(wantedProps); 257 final StpProperty.List<?> total = res.getProperty(property); 258 final Operations operations = getOperations(res); 259 260 // Filter out properties that could not be read. 261 final StpProperty.List<StpProperty<?>> valid = 262 new StpProperty.List<StpProperty<?>>(); 263 264 for(StpProperty<?> prop: total) { 265 try { 266 // If getValue() doesn't blow up, we can display the property 267 prop.getValue(); 268 valid.add(prop); 269 } catch (WvcmException ex) { 270 } 271 } 272 273 // Sort the properties by their simple name 274 StpProperty<?>[] props; 275 276 if (validOnly) 277 props = (StpProperty[]) valid.toArray(new StpProperty[valid.size()]); 278 else 279 props = (StpProperty[]) total.toArray(new StpProperty[total.size()]); 280 281 Arrays.sort(props, new Comparator<StpProperty<?>>() { 282 public int compare(StpProperty<?> arg0, StpProperty<?> arg1) { 283 return arg0.getPropertyName().getName() 284 .compareTo(arg1.getPropertyName().getName()); 285 } 286 }); 287 288 final StpProperty.List<StpProperty<?>> properties = 289 new StpProperty.List<StpProperty<?>>(); 290 291 properties.addAll(Arrays.asList(props)); 292 293 // Build the data model for displaying one property per row, one 294 // meta-property per column. 295 BrowserDataModel dataModel = new BrowserDataModel() { 296 public int getColumnCount() { 297 return metaProperties.length; 298 } 299 300 public int getRowCount() { 301 return properties.size(); 302 } 303 304 public Object getValueAt(int row, int col) { 305 try { 306 return ((StpProperty<?>) properties.get(row)) 307 .getMetaProperty((MetaPropertyName<?>) 308 metaProperties[col].getRoot()); 309 } catch (Throwable ex) { 310 if (StpProperty.TYPE.equals(metaProperties[col])) 311 try { 312 return typeImage(((StpProperty<?>) properties.get(row)) 313 .getValue()); 314 } catch (WvcmException e) { 315 } 316 317 return exceptionImage(ex, properties.get(row)); 318 } 319 } 320 321 public String getColumnName(int col) { 322 return metaProperties[col].getRoot().getName(); 323 } 324 325 /** 326 * Properties whose values are resources or lists of resources, 327 * properties ChildBinding objects or ParentBinding objects are 328 * viewable. 329 */ 330 Object getViewable(int row) { 331 StpProperty<?> result = properties.get(row); 332 try { 333 Object val = result.getValue(); 334 335 if (val != null 336 && (val instanceof StpResource 337 || Utilities.isListOfResources(val) 338 || val instanceof StpProperty.List)) 339 return result; 340 } catch (WvcmException ex) { 341 } 342 343 return null; 344 } 345 346 /** 347 * Generates a new table window for a resource, resource list or 348 * property list. 349 */ 350 void show(Object viewable) throws WvcmException { 351 StpProperty<?> property = (StpProperty<?>) viewable; 352 Object val = property.getValue(); 353 354 try { 355 m_frame.setCursor(Cursor 356 .getPredefinedCursor(Cursor.WAIT_CURSOR)); 357 358 if (Utilities.isListOfResources(val)) { 359 showResourceList(property.getName() + " of " 360 + Utilities.resourceType(res) 361 + " " + res.location().string(), 362 Utilities.toResourceList(res.stpProvider(), 363 (List<?>)val), 364 PROPS.toArray(), 365 validOnly); 366 } else if (val instanceof StpResource) { 367 StpResource res = (StpResource) val; 368 showResource(Utilities.resourceType(res) + " " 369 + res.location().string(), 370 res, 371 metaProperties, 372 validOnly); 373 } else if (val instanceof StpProperty.List) { 374 PropertyName<StpProperty.List<StpProperty<?>>> prop = 375 StpException 376 .<PropertyName<StpProperty.List<StpProperty<?>>>> 377 unchecked_cast(property 378 .getPropertyName()); 379 380 showPropertyList(property.getName() + " of " 381 + Utilities.resourceType(res) 382 + " " + res.location().string(), 383 res, 384 prop, 385 metaProperties, 386 validOnly); 387 } 388 } finally { 389 m_frame.setCursor(Cursor 390 .getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 391 } 392 } 393 394 String toggleErrorsLabel() { 395 return m_validPropertyCount == m_totalPropertyCount ? "" 396 : properties.size() == m_validPropertyCount ? "Show Errors" 397 : "Hide Errors"; 398 } 399 400 void toggleErrors() throws WvcmException { 401 try { 402 m_frame.setCursor(Cursor 403 .getPredefinedCursor(Cursor.WAIT_CURSOR)); 404 showPropertyList(title, 405 resource, 406 property, 407 metaProperties, 408 !validOnly); 409 } finally { 410 m_frame.dispose(); 411 } 412 } 413 414 void redisplay() throws WvcmException { 415 try { 416 m_frame.setCursor(Cursor 417 .getPredefinedCursor(Cursor.WAIT_CURSOR)); 418 419 showPropertyList(title, 420 resource, 421 property, 422 metaProperties, 423 validOnly); 424 } finally { 425 m_frame.dispose(); 426 } 427 } 428 429 /** 430 * Generates a display for the content of this resource 431 */ 432 void showContent() throws Throwable { 433 File temp = File.createTempFile("browser", "tmp"); 434 FileOutputStream stream = new FileOutputStream(temp); 435 436 res.doReadContent(stream, null); 437 showFile("Content of " + Utilities.resourceType(res) + " " 438 + res.location().string(), temp); 439 } 440 441 /** 442 * Returns the Operations object associated with this window. 443 * @see CcBrowser 444 */ 445 Operations getOperationsObject() { 446 return operations; 447 } 448 449 private static final long serialVersionUID = 1L; 450 451 /** The total number of properties defined by the resource proxy */ 452 private int m_totalPropertyCount = total.size(); 453 454 /** The total number of valid properties defined by the proxy */ 455 private int m_validPropertyCount = valid.size(); 456 }; 457 458 return dataModel.showModel(title, true); 459 } 460 461 /** 462 * Starts browsing at a user-supplied resource location or in the folders 463 * designated for a user-supplied type of resource. 464 * 465 * @param args 466 * Not used in this application 467 */ 468 public static void main(String[] args) throws Exception { 469 /** 470 * Present the list to the user and marshal requests for folder lists 471 * and specific resources to the showRoot() method. 472 */ 473 String help = "Enter a Location or Select a resource category and Click 'Browse'"; 474 String prompt = "<type>.<namespace>:<name>@<repository>"; 475 476 final JFrame frame = new JFrame("Resource Browser"); 477 final JPanel panel = new JPanel(new BorderLayout()); 478 final JPanel subpanel = new JPanel(new FlowLayout()); 479 final DefaultComboBoxModel model = new DefaultComboBoxModel(); 480 481 // Add object selectors for sample databases. Edit this code to match 482 // installed resources as desired. 483 model.insertElementAt("cq.record:Defect/SAMPL00000005@7.0.0/SAMPL", 0); 484 model.insertElementAt(CLEAR_CASE_WORKSPACES, 0); 485 model.insertElementAt(CLEAR_QUEST_DB_SETS, 0); 486 model.insertElementAt(prompt, 0); 487 model.setSelectedItem(prompt); 488 489 final JComboBox box = new JComboBox(model); 490 491 panel.add(box, BorderLayout.CENTER); 492 box.setEditable(true); 493 494 final JButton browse = new JButton("Browse"); 495 496 subpanel.add(browse, BorderLayout.SOUTH); 497 browse.addActionListener(new ActionListener() { 498 public void actionPerformed(ActionEvent arg0) { 499 String selection = (String) box.getSelectedItem(); 500 501 if (model.getIndexOf(selection) < 0) 502 model.insertElementAt(selection, 0); 503 504 try { 505 frame.setCursor(Cursor 506 .getPredefinedCursor(Cursor.WAIT_CURSOR)); 507 508 showRoot(selection); 509 } finally { 510 frame.setCursor(Cursor 511 .getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 512 } 513 } 514 }); 515 516 JButton exit = new JButton("Exit"); 517 subpanel.add(exit); 518 exit.addActionListener(new ActionListener() { 519 public void actionPerformed(ActionEvent arg0) { 520 System.exit(0); 521 } 522 }); 523 524 final JButton cqurl = new JButton("URL..."); 525 subpanel.add(cqurl); 526 cqurl.addActionListener(new ActionListener() { 527 public void actionPerformed(ActionEvent arg0) { 528 getUrl("http://qwin115:9082/TeamWeb/services/Team"); 529 }}); 530 531 panel.add(new JLabel(help), BorderLayout.NORTH); 532 panel.add(subpanel, BorderLayout.SOUTH); 533 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 534 frame.setContentPane(panel); 535 frame.setBounds(200, 200, 500, 110); 536 frame.setVisible(true); 537 } 538 539 static boolean getUrl(String defaultURL) 540 { 541 try { 542 if (g_provider == null) 543 g_provider = Utilities.getProvider(); 544 545 String url = g_provider.getServerUrl(); 546 547 if (url == null || url.length() == 0) 548 url =defaultURL; 549 550 url = 551 JOptionPane.showInputDialog("Enter a URL for the server", url); 552 553 g_provider.setServerUrl(url); 554 return true; 555 } catch (Exception ex) { 556 Utilities.exception(null, "Provider", ex); 557 } 558 559 return false; 560 } 561 /** 562 * Generates a short string to identify a given throwable 563 * 564 * @param ex 565 * A Throwable that is to be identified. 566 * @param obj 567 * The object that promulgated the exception. 568 * @return A short String providing, in a concise format, identification of 569 * and relevant information from the throwable. 570 */ 571 static Object exceptionImage(Throwable ex, Object obj) { 572 StpReasonCode code = null; 573 String prefix = ""; 574 575 if (ex instanceof StpException) { 576 code = ((StpException) ex).getStpReasonCode(); 577 } else if (obj instanceof StpPropertyException) { 578 StpPropertyException pe = (StpPropertyException) obj; 579 PropertyName<?> name = pe.getPropertyName(); 580 581 code = pe.getStpReasonCode(); 582 583 if (name != null) 584 prefix = name.getName() + ": "; 585 } else { 586 String name = ex.getClass().getName(); 587 return name.substring(name.lastIndexOf(".") + 1); 588 } 589 590 // Look through the wrapper to display more information 591 if (code == StpReasonCode.PROPERTY_RETRIEVAL_FAILED) 592 code = ((StpException)ex.getCause()).getStpReasonCode(); 593 594 return prefix + code; 595 } 596 597 /** 598 * Generates the unqualified name of the type of a given object 599 * 600 * @param obj 601 * The object whose value is to be displayed. May be null. 602 * @return The leaf name of the type of the object. 603 */ 604 static Object typeImage(Object obj) { 605 if (obj == null) 606 return "(null)"; 607 608 String name = obj.getClass().getName(); 609 int dot = name.lastIndexOf('.'); 610 611 return dot < 0 ? name : name.substring(dot + 1); 612 } 613 614 /** 615 * If the g_operationsClass variable is set, returns a new instance of that 616 * class initialized with the given Resource proxy. 617 * 618 * @param res 619 * A Resource proxy. Must not be null. 620 * @return A new Operations object if the operations class has been assigned 621 * to g_operationsClass; otherwise null. 622 */ 623 private static Operations getOperations(StpResource res) { 624 if (g_operationsClass != null) 625 try { 626 return (Operations) g_operationsClass 627 .getConstructor(new Class[] { StpResource.class }) 628 .newInstance(new Object[] { res }); 629 } catch (Throwable t) { 630 Utilities.exception(null, "getOperations", t); 631 } 632 633 return null; 634 } 635 636 private static final MetaPropertyName<Object> VALUE_AS_OBJECT = 637 new MetaPropertyName<Object>(StpProperty.VALUE.getNamespace(), 638 StpProperty.VALUE.getName()); 639 640 /** 641 * Properties to be displayed for elements of a ResourceList Also the 642 * properties requested for resource-valued properties. 643 */ 644 private static final PropertyRequest PROPS = new PropertyRequest( 645 StpResource.USER_FRIENDLY_LOCATION /*, 646 StpResource.COMMENT*/ ); 647 648 /** 649 * Meta-properties to be displayed for each property Also the properties 650 * requested for property-valued properties 651 */ 652 static final PropertyRequest META_PROPS = 653 new PropertyRequest( new PropertyRequestItem[] { 654 StpProperty.NAME, 655 StpProperty.TYPE, 656 StpProperty.VALUE.nest(PROPS)}); 657 658 /** Drop-down entry for requesting ClearQuest connections */ 659 private static final String CLEAR_QUEST_DB_SETS = "<ClearQuest Connections>"; 660 661 /** Drop-down entry for requesting local ClearCase views */ 662 private static final String CLEAR_CASE_WORKSPACES = "<Local ClearCase Views>"; 663 664 /** 665 * The class to use for Operations if supported by the application using 666 * the Browser class. 667 * 668 * @see CcBrowser#main(String[]) 669 */ 670 static Class<?> g_operationsClass = null; 671 672 /** The Provider instance used by all instances of the Browser in a process */ 673 private static StpProvider g_provider = null; 674 }