1 /*
   2  * Copyright (c) 2012, 2014, Oracle and/or its affiliates.
   3  * All rights reserved. Use is subject to license terms.
   4  *
   5  * This file is available and licensed under the following license:
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  *
  11  *  - Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  *  - Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in
  15  *    the documentation and/or other materials provided with the distribution.
  16  *  - Neither the name of Oracle Corporation nor the names of its
  17  *    contributors may be used to endorse or promote products derived
  18  *    from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 package com.oracle.javafx.scenebuilder.kit.editor.panel.library;
  33 
  34 import com.oracle.javafx.scenebuilder.kit.editor.i18n.I18N;
  35 import com.oracle.javafx.scenebuilder.kit.editor.panel.util.dialog.AbstractModalDialog;
  36 import com.oracle.javafx.scenebuilder.kit.editor.panel.util.dialog.ErrorDialog;
  37 import com.oracle.javafx.scenebuilder.kit.fxom.FXOMDocument;
  38 import com.oracle.javafx.scenebuilder.kit.library.user.UserLibrary;
  39 import com.oracle.javafx.scenebuilder.kit.library.util.JarExplorer;
  40 import com.oracle.javafx.scenebuilder.kit.library.util.JarReport;
  41 import com.oracle.javafx.scenebuilder.kit.library.util.JarReportEntry;
  42 
  43 import java.io.File;
  44 import java.io.IOException;
  45 import java.net.MalformedURLException;
  46 import java.net.URL;
  47 import java.net.URLClassLoader;
  48 import java.nio.file.Files;
  49 import java.nio.file.Path;
  50 import java.nio.file.Paths;
  51 import java.util.ArrayList;
  52 import java.util.Collections;
  53 import java.util.Iterator;
  54 import java.util.List;
  55 import java.util.concurrent.ExecutionException;
  56 import java.util.stream.Stream;
  57 
  58 import javafx.application.Platform;
  59 import javafx.beans.value.ChangeListener;
  60 import javafx.beans.value.ObservableValue;
  61 import javafx.concurrent.Task;
  62 import javafx.event.ActionEvent;
  63 import javafx.fxml.FXML;
  64 import javafx.geometry.Bounds;
  65 import javafx.scene.Group;
  66 import javafx.scene.Node;
  67 import javafx.scene.Scene;
  68 import javafx.scene.control.ChoiceBox;
  69 import javafx.scene.control.Label;
  70 import javafx.scene.control.ListView;
  71 import javafx.scene.control.ProgressIndicator;
  72 import javafx.scene.control.SplitPane;
  73 import javafx.scene.control.ToggleButton;
  74 import javafx.scene.control.cell.CheckBoxListCell;
  75 import javafx.scene.layout.Region;
  76 import javafx.scene.layout.VBox;
  77 import javafx.stage.Modality;
  78 import javafx.stage.Stage;
  79 import javafx.stage.Window;
  80 import javafx.stage.WindowEvent;
  81 import javafx.util.Callback;
  82 
  83 /**
  84  *
  85  */
  86 public class ImportWindowController extends AbstractModalDialog {
  87 
  88     final List<File> importFiles;
  89     private final LibraryPanelController libPanelController;
  90     Task<List<JarReport>> exploringTask = null;
  91     URLClassLoader importClassLoader;
  92     Node zeNode = new Label(I18N.getString("import.preview.unable"));
  93     double builtinPrefWidth;
  94     double builtinPrefHeight;
  95     private int numOfImportedJar;
  96 
  97     // At first we put in this collection the items which are already excluded,
  98     // basically all which are listed in the filter file.
  99     // When constructing the list of items discovered in new jar file being imported
 100     // we uncheck already excluded items and remove them from the collection.
 101     // When the user clicks the Import button the collection might contain the
 102     // items we retain from older import actions.
 103     private List<String> alreadyExcludedItems = new ArrayList<>();
 104 
 105     public enum PrefSize {
 106 
 107         DEFAULT, TWO_HUNDRED_BY_ONE_HUNDRED, TWO_HUNDRED_BY_TWO_HUNDRED
 108     };
 109 
 110     @FXML
 111     private VBox leftHandSidePart;
 112 
 113     @FXML
 114     private Label processingLabel;
 115 
 116     @FXML
 117     ProgressIndicator processingProgressIndicator;
 118 
 119     @FXML
 120     ListView<ImportRow> importList = new ListView<>();
 121 
 122     @FXML
 123     ChoiceBox<String> defSizeChoice;
 124 
 125     @FXML
 126     private Label sizeLabel;
 127 
 128     @FXML
 129     private SplitPane topSplitPane;
 130 
 131     @FXML
 132     Group previewGroup;
 133 
 134     @FXML
 135     Label numOfItemsLabel;
 136 
 137     @FXML
 138     Label classNameLabel;
 139 
 140     @FXML
 141     Label previewHintLabel;
 142 
 143     @FXML
 144     ToggleButton checkAllUncheckAllToggle;
 145 
 146     public ImportWindowController(LibraryPanelController lpc, List<File> files, Window owner) {
 147         super(ImportWindowController.class.getResource("ImportDialog.fxml"), I18N.getBundle(), owner); //NOI18N
 148         libPanelController = lpc;
 149         importFiles = new ArrayList<>(files);
 150     }
 151 
 152     /*
 153      * Event handlers
 154      */
 155     /* TO BE SOLVED
 156      We have an issue with the exploration of SOME jar files.
 157      If e.g. you use sa-jdi.jar (take it in the JRE or JDK tree) then a NPE as
 158      the one below will be printed but cannot be caught in the code of this class.
 159      And from there we won't be able to exit from SB, whatever the action we take
 160      on the import window (Cancel or Import).
 161      Yes the window goes away but some thread refuse to give up.
 162      I noticed two non daemon threads:
 163      AWT-EventQueue-0
 164      AWT-Shutdown
 165 
 166      java.lang.NullPointerException
 167      at java.util.StringTokenizer.<init>(StringTokenizer.java:199)
 168      at java.util.StringTokenizer.<init>(StringTokenizer.java:221)
 169      at sun.jvm.hotspot.tools.jcore.PackageNameFilter.<init>(PackageNameFilter.java:41)
 170      at sun.jvm.hotspot.tools.jcore.PackageNameFilter.<init>(PackageNameFilter.java:36)
 171      at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 172      at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
 173      at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
 174      at java.lang.reflect.Constructor.newInstance(Constructor.java:414)
 175      at java.lang.Class.newInstance(Class.java:444)
 176      at sun.reflect.misc.ReflectUtil.newInstance(ReflectUtil.java:47)
 177      at javafx.fxml.FXMLLoader$InstanceDeclarationElement.constructValue(FXMLLoader.java:883)
 178      at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:614)
 179      at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2491)
 180      at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2300)
 181      at com.oracle.javafx.scenebuilder.kit.library.util.JarExplorer.instantiateWithFXMLLoader(JarExplorer.java:83)
 182      at com.oracle.javafx.scenebuilder.kit.library.util.JarExplorer.exploreEntry(JarExplorer.java:117)
 183      at com.oracle.javafx.scenebuilder.kit.library.util.JarExplorer.explore(JarExplorer.java:43)
 184      at com.oracle.javafx.scenebuilder.kit.editor.panel.library.ImportWindowController$2.call(ImportWindowController.java:155)
 185      at com.oracle.javafx.scenebuilder.kit.editor.panel.library.ImportWindowController$2.call(ImportWindowController.java:138)
 186      at javafx.concurrent.Task$TaskCallable.call(Task.java:1376)
 187      at java.util.concurrent.FutureTask.run(FutureTask.java:262)
 188      at java.lang.Thread.run(Thread.java:724)
 189      */
 190     @Override
 191     protected void cancelButtonPressed(ActionEvent e) {
 192         if (exploringTask != null && exploringTask.isRunning()) {
 193             exploringTask.setOnCancelled(t -> getStage().close());
 194             exploringTask.cancel(true);
 195         } else {
 196             getStage().close();
 197         }
 198 
 199         exploringTask = null;
 200 
 201         try {
 202             closeClassLoader();
 203         } catch (IOException ex) {
 204             showErrorDialog(ex);
 205         }
 206     }
 207 
 208     @Override
 209     protected void okButtonPressed(ActionEvent e) {
 210         exploringTask = null;
 211         getStage().close();
 212 
 213         try {
 214             closeClassLoader();
 215             libPanelController.copyFilesToUserLibraryDir(importFiles);
 216             UserLibrary userLib = ((UserLibrary) libPanelController.getEditorController().getLibrary());
 217             userLib.setFilter(getExcludedItems());
 218         } catch (IOException ex) {
 219             showErrorDialog(ex);
 220         } finally {
 221             alreadyExcludedItems.clear();
 222         }
 223     }
 224 
 225     @Override
 226     protected void actionButtonPressed(ActionEvent e) {
 227         // NOTHING TO DO (no ACTION button)
 228     }
 229 
 230     /*
 231      * AbstractFxmlWindowController
 232      */
 233     @Override
 234     public void onCloseRequest(WindowEvent event) {
 235         cancelButtonPressed(null);
 236     }
 237 
 238     @Override
 239     public void controllerDidLoadContentFxml() {
 240         assert topSplitPane != null;
 241         // The SplitPane should not be visible from the beginning: only the progressing bar is initially visible.
 242         assert topSplitPane.isVisible() == false;
 243         assert processingLabel != null;
 244         assert processingProgressIndicator != null;
 245         assert sizeLabel != null;
 246         assert previewGroup != null;
 247         assert importList != null;
 248         assert defSizeChoice != null;
 249         assert numOfItemsLabel != null;
 250         assert leftHandSidePart != null;
 251         assert classNameLabel != null;
 252         assert previewHintLabel != null;
 253         assert checkAllUncheckAllToggle != null;
 254 
 255         // Setup dialog buttons
 256         setOKButtonVisible(true);
 257         setDefaultButtonID(ButtonID.OK);
 258         setShowDefaultButton(true);
 259 
 260         // Setup size choice box
 261         defSizeChoice.getItems().clear();
 262         // Care to have values in sync with definition of PrefSize
 263         defSizeChoice.getItems().addAll(I18N.getString("import.choice.builtin"),
 264                 "200 x 100", "200 x 200"); //NOI18N
 265         defSizeChoice.getSelectionModel().selectFirst();
 266         defSizeChoice.getSelectionModel().selectedIndexProperty().addListener((ChangeListener<Number>) (ov, t, t1) -> {
 267             assert t1 instanceof Integer;
 268             updateSize((Integer)t1);
 269         });
 270 
 271         // Setup Select All / Unselect All toggle
 272         // Initially all items are Selected.
 273         checkAllUncheckAllToggle.selectedProperty().addListener((ChangeListener<Boolean>) (ov, t, t1) -> {
 274             if (t1) {
 275                 for (ImportRow row1 : importList.getItems()) {
 276                     row1.setImportRequired(false);
 277                 }
 278                 checkAllUncheckAllToggle.setText(I18N.getString("import.toggle.checkall"));
 279             } else {
 280                 for (ImportRow row2 : importList.getItems()) {
 281                     row2.setImportRequired(true);
 282                 }
 283                 checkAllUncheckAllToggle.setText(I18N.getString("import.toggle.uncheckall"));
 284             }
 285         });
 286 
 287         setProcessing();
 288 
 289         // We do not want the list becomes larger when the window is made larger.
 290         // The way to make the list larger is to use the splitter.
 291         SplitPane.setResizableWithParent(leftHandSidePart, false);
 292 
 293         work();
 294     }
 295 
 296     /*
 297      * AbstractWindowController
 298      */
 299     @Override
 300     protected void controllerDidCreateStage() {
 301         getStage().setTitle(I18N.getString("import.window.title"));
 302         getStage().initModality(Modality.APPLICATION_MODAL);
 303     }
 304 
 305     /*
 306      * Private
 307      */
 308 
 309     private void closeClassLoader() throws IOException {
 310         if (importClassLoader != null) {
 311             importClassLoader.close();
 312         }
 313     }
 314 
 315     // This method returns a new list of File made of the union of the provided
 316     // one and jar files found in the user library dir.
 317     List<File> buildListOfAllFiles(List<File> importFiles) throws IOException {
 318         final List<File> res = new ArrayList<>(importFiles);
 319         String userLibraryDir = ((UserLibrary) libPanelController.getEditorController().getLibrary()).getPath();
 320         Path userLibraryPath = new File(userLibraryDir).toPath();
 321 
 322         try (Stream<Path> pathStream = Files.list(userLibraryPath)) {
 323             Iterator<Path> pathIterator = pathStream.iterator();
 324             while (pathIterator.hasNext()) {
 325                 Path element = pathIterator.next();
 326                 if (element.toString().endsWith(".jar")) { //NOI18N
 327 //                    System.out.println("ImportWindowController::buildListOfAllFiles: Adding " + element); //NOI18N
 328                     res.add(element.toFile());
 329                 }
 330             }
 331         }
 332 
 333         return res;
 334     }
 335 
 336     private void work() {
 337         exploringTask = new Task<List<JarReport>>() {
 338 
 339             @Override
 340             protected List<JarReport> call() throws Exception {
 341                 final List<JarReport> res = new ArrayList<>();
 342                 numOfImportedJar = importFiles.size();
 343                 // The classloader takes in addition all already existing
 344                 // jar files stored in the user lib dir.
 345                 final List<File> allFiles = buildListOfAllFiles(importFiles);
 346                 final URLClassLoader classLoader = getClassLoaderForFiles(allFiles);
 347                 int index = 1;
 348                 for (File file : importFiles) {
 349                     if (isCancelled()) {
 350                         updateMessage(I18N.getString("import.work.cancelled"));
 351                         break;
 352                     }
 353                     updateMessage(I18N.getString("import.work.exploring", file.getName()));
 354 //                    System.out.println("[" + index + "/" + max + "] Exploring file " + file.getName()); //NOI18N
 355                     final JarExplorer explorer = new JarExplorer(Paths.get(file.getAbsolutePath()));
 356                     final JarReport jarReport = explorer.explore(classLoader);
 357                     res.add(jarReport);
 358                     updateProgress(index, numOfImportedJar);
 359                     index++;
 360                 }
 361 
 362                 updateProgress(numOfImportedJar, numOfImportedJar);
 363                 updateImportClassLoader(classLoader);
 364                 return res;
 365             }
 366         };
 367 
 368         Thread th = new Thread(exploringTask);
 369         th.setDaemon(true);
 370         processingProgressIndicator.progressProperty().bind(exploringTask.progressProperty());
 371 
 372         // We typically enter this handler when dropping jar files such as
 373         // rt.jar from Java Runtime.
 374         exploringTask.setOnFailed(t -> {
 375             // See in setOnSucceeded the explanation for the toFront below.
 376             getStage().toFront();
 377             updateNumOfItemsLabelAndSelectionToggleState();
 378         });
 379 
 380         // We construct the import list only if exploration of jar files does well.
 381         // If Cancel is called during the construction of the list then the import
 382         // window is closed but the construction itself will continue up to the
 383         // end. Do we want to make it interruptible ?
 384         exploringTask.setOnSucceeded(t -> {
 385             assert Platform.isFxApplicationThread();
 386             // This toFront() might not be necessary because import window is modal
 387             // and is chained to the document window. Anyway experience showed
 388             // we need it (FX 8 b106). This is suspicious, to be investigated ...
 389             // But more tricky is why toFront() is called here. Mind that when toFront()
 390             // is called while isShowing() returns false isn't effective: that's
 391             // why toFront called at the end of controllerDidCreateStage() or
 392             // controllerDidLoadContentFxml() wasn't an option. Below is the
 393             // earliest place it has been proven effective, at least on my machine.
 394             getStage().toFront();
 395 
 396             try {
 397                 // We get the set of items which are already excluded prior to the current import.
 398                 UserLibrary userLib = ((UserLibrary) libPanelController.getEditorController().getLibrary());
 399                 alreadyExcludedItems = userLib.getFilter();
 400 
 401                 List<JarReport> jarReportList = exploringTask.get(); // blocking call
 402                 final Callback<ImportRow, ObservableValue<Boolean>> importRequired
 403                         = row -> row.importRequired();
 404                 importList.setCellFactory(CheckBoxListCell.forListView(importRequired));
 405 
 406                 for (JarReport jarReport : jarReportList) {
 407                     for (JarReportEntry e : jarReport.getEntries()) {
 408                         if ((e.getStatus() == JarReportEntry.Status.OK) && e.isNode()) {
 409                             boolean checked = true;
 410                             // If the class we import is already listed as an excluded one
 411                             // then it must appear unchecked in the list.
 412                             if (alreadyExcludedItems.contains(e.getKlass().getCanonicalName())) {
 413                                 checked = false;
 414                                 alreadyExcludedItems.remove(e.getKlass().getCanonicalName());
 415                             }
 416                             final ImportRow importRow = new ImportRow(checked, e, null);
 417                             importList.getItems().add(importRow);
 418                             importRow.importRequired().addListener((ChangeListener<Boolean>) (ov, oldValue,
 419                                     newValue) -> {
 420                                         final int numOfComponentToImport = getNumOfComponentToImport(importList);
 421                                         updateOKButtonTitle(numOfComponentToImport);
 422                                         updateSelectionToggleText(numOfComponentToImport);
 423                                     });
 424                         }
 425                     }
 426                 }
 427 
 428                 // Sort based on the simple class name.
 429                 Collections.sort(importList.getItems(), new ImportRowComparator());
 430 
 431                 final int numOfComponentToImport = getNumOfComponentToImport(importList);
 432                 updateOKButtonTitle(numOfComponentToImport);
 433                 updateSelectionToggleText(numOfComponentToImport);
 434                 updateNumOfItemsLabelAndSelectionToggleState();
 435             } catch (InterruptedException | ExecutionException | IOException ex) {
 436                 getStage().close();
 437                 showErrorDialog(ex);
 438             }
 439 
 440             unsetProcessing();
 441         });
 442 
 443         th.start();
 444     }
 445 
 446     private void showErrorDialog(Exception exception) {
 447         final ErrorDialog errorDialog = new ErrorDialog(null);
 448         errorDialog.setTitle(I18N.getString("import.error.title"));
 449         errorDialog.setMessage(I18N.getString("import.error.message"));
 450         errorDialog.setDetails(I18N.getString("import.error.details"));
 451         errorDialog.setDebugInfoWithThrowable(exception);
 452         errorDialog.showAndWait();
 453     }
 454 
 455     void updateImportClassLoader(URLClassLoader cl) {
 456         this.importClassLoader = cl;
 457     }
 458 
 459     void unsetProcessing() {
 460         processingProgressIndicator.setVisible(false);
 461         processingLabel.setVisible(false);
 462         topSplitPane.setVisible(true);
 463 
 464         importList.getSelectionModel().selectedItemProperty().addListener((ChangeListener<ImportRow>) (ov, t, t1) -> {
 465             previewGroup.getChildren().clear();
 466             final String fxmlText = JarExplorer.makeFxmlText(t1.getJarReportEntry().getKlass());
 467             try {
 468                 FXOMDocument fxomDoc = new FXOMDocument(fxmlText, null, importClassLoader, null);
 469                 zeNode = (Node) fxomDoc.getSceneGraphRoot();
 470             } catch (IOException ioe) {
 471                 showErrorDialog(ioe);
 472             }
 473 
 474             // In order to get valid bounds I need to put the node into a
 475             // scene and ask for full layout.
 476             try {
 477                 final Group visualGroup = new Group(zeNode);
 478                 final Scene hiddenScene = new Scene(visualGroup);
 479                 Stage hiddenStage = new Stage();
 480                 hiddenStage.setScene(hiddenScene);
 481                 visualGroup.applyCss();
 482                 visualGroup.layout();
 483                 final Bounds zeBounds = zeNode.getLayoutBounds();
 484                 builtinPrefWidth = zeBounds.getWidth();
 485                 builtinPrefHeight = zeBounds.getHeight();
 486                 // Detach the scene !
 487                 hiddenScene.setRoot(new Group());
 488                 hiddenStage.close();
 489             } catch (Error e) {
 490                 // Experience shows that with rogue jar files (a jar file
 491                 // unlikely to contain FX controls) we can enter here.
 492                 // Anything better to do than setting pref size to 0 ?
 493                 builtinPrefWidth = 0;
 494                 builtinPrefHeight = 0;
 495             }
 496 
 497             if (builtinPrefWidth == 0 || builtinPrefHeight == 0) {
 498                 ((Region) zeNode).setPrefSize(200, 200);
 499                 setSizeLabel(PrefSize.TWO_HUNDRED_BY_TWO_HUNDRED);
 500                 defSizeChoice.getSelectionModel().select(2);
 501             } else {
 502                 setSizeLabel(PrefSize.DEFAULT);
 503                 defSizeChoice.getSelectionModel().selectFirst();
 504             }
 505             previewGroup.getChildren().add(zeNode);
 506             defSizeChoice.setDisable(false);
 507             classNameLabel.setText(t1.getJarReportEntry().getKlass().getName());
 508         });
 509 
 510         // We avoid to get an empty Preview area at first.
 511         if (importList.getItems().size() > 0) {
 512             importList.getSelectionModel().selectFirst();
 513         }
 514     }
 515 
 516     private URLClassLoader getClassLoaderForFiles(List<File> files) {
 517         return new URLClassLoader(makeURLArrayFromFiles(files));
 518     }
 519 
 520     private URL[] makeURLArrayFromFiles(List<File> files) {
 521         final URL[] result = new URL[files.size()];
 522         try {
 523             int index = 0;
 524             for (File file : files) {
 525                 result[index] = file.toURI().toURL();
 526                 index++;
 527             }
 528         } catch (MalformedURLException x) {
 529             throw new RuntimeException("Bug in " + getClass().getSimpleName(), x); //NOI18N
 530         }
 531 
 532         return result;
 533     }
 534 
 535     private void setProcessing() {
 536         cancelButton.setDefaultButton(true);
 537     }
 538 
 539     private int getNumOfComponentToImport(final ListView<ImportRow> list) {
 540         int res = 0;
 541 
 542         for (final ImportRow row : list.getItems()) {
 543             if (row.isImportRequired()) {
 544                 res++;
 545             }
 546         }
 547 
 548         return res;
 549     }
 550 
 551     private List<String> getExcludedItems() {
 552         List<String> res = new ArrayList<>(alreadyExcludedItems);
 553 
 554         for (ImportRow row : importList.getItems()) {
 555             if (! row.isImportRequired()) {
 556                 res.add(row.getCanonicalClassName());
 557             }
 558         }
 559         return res;
 560     }
 561 
 562     // The title of the button is important in the sense it says to the user
 563     // what action will be taken.
 564     // In the most common case one or more component are selected in the list,
 565     // but it is also possible to get an empty list, in which case the user may
 566     // want to import the jar file anyway; it makes sense in ooder to resolve
 567     // dependencies other jars have onto it.
 568     // See DTL-6531 for details.
 569     private void updateOKButtonTitle(int numOfComponentToImport) {
 570         if (numOfComponentToImport == 0) {
 571             if (numOfImportedJar == 1) {
 572                 setOKButtonTitle(I18N.getString("import.button.import.jar"));
 573             } else {
 574                 setOKButtonTitle(I18N.getString("import.button.import.jars"));
 575             }
 576         } else if (numOfComponentToImport == 1) {
 577             setOKButtonTitle(I18N.getString("import.button.import.component"));
 578         } else {
 579             setOKButtonTitle(I18N.getString("import.button.import.components"));
 580         }
 581     }
 582 
 583     void updateNumOfItemsLabelAndSelectionToggleState() {
 584         final int num = importList.getItems().size();
 585         if (num == 0 || num == 1) {
 586             numOfItemsLabel.setText(num + " " //NOI18N
 587                     + I18N.getString("import.num.item"));
 588         } else {
 589             numOfItemsLabel.setText(num + " " //NOI18N
 590                     + I18N.getString("import.num.items"));
 591         }
 592 
 593         if (num >= 1) {
 594             checkAllUncheckAllToggle.setDisable(false);
 595         }
 596     }
 597 
 598     private void updateSelectionToggleText(int numOfComponentToImport) {
 599         if (numOfComponentToImport == 0) {
 600             checkAllUncheckAllToggle.setText(I18N.getString("import.toggle.checkall"));
 601         } else {
 602             checkAllUncheckAllToggle.setText(I18N.getString("import.toggle.uncheckall"));
 603         }
 604     }
 605 
 606     // NOTE At the end of the day some tooling in metadata will supersedes the
 607     // use of this method that is only able to deal with a Region, ignoring all
 608     // other cases.
 609     private void updateSize(Integer choice) {
 610         if (zeNode instanceof Region) {
 611             PrefSize prefSize = PrefSize.values()[choice];
 612             switch (prefSize) {
 613                 case DEFAULT:
 614                     ((Region) zeNode).setPrefSize(builtinPrefWidth, builtinPrefHeight);
 615                     setSizeLabel(prefSize);
 616                     break;
 617                 case TWO_HUNDRED_BY_ONE_HUNDRED:
 618                     ((Region) zeNode).setPrefSize(200, 100);
 619                     setSizeLabel(prefSize);
 620                     break;
 621                 case TWO_HUNDRED_BY_TWO_HUNDRED:
 622                     ((Region) zeNode).setPrefSize(200, 200);
 623                     setSizeLabel(prefSize);
 624                     break;
 625                 default:
 626                     break;
 627             }
 628 
 629             defSizeChoice.getSelectionModel().select(choice);
 630         }
 631     }
 632 
 633     private void setSizeLabel(PrefSize ps) {
 634         switch (ps) {
 635             case DEFAULT:
 636                 sizeLabel.setText(builtinPrefWidth + " x " + builtinPrefHeight); //NOI18N
 637                 break;
 638             case TWO_HUNDRED_BY_ONE_HUNDRED:
 639                 sizeLabel.setText("200 x 100"); //NOI18N
 640                 break;
 641             case TWO_HUNDRED_BY_TWO_HUNDRED:
 642                 sizeLabel.setText("200 x 200"); //NOI18N
 643                 break;
 644             default:
 645                 break;
 646         }
 647     }
 648 }