1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved.
   5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   6  *
   7  * This code is free software; you can redistribute it and/or modify it
   8  * under the terms of the GNU General Public License version 2 only, as
   9  * published by the Free Software Foundation.  Oracle designates this
  10  * particular file as subject to the "Classpath" exception as provided
  11  * by Oracle in the LICENSE file that accompanied this code.
  12  *
  13  * This code is distributed in the hope that it will be useful, but WITHOUT
  14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16  * version 2 for more details (a copy is included in the LICENSE file that
  17  * accompanied this code).
  18  *
  19  * You should have received a copy of the GNU General Public License version
  20  * 2 along with this work; if not, write to the Free Software Foundation,
  21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  22  *
  23  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  24  * or visit www.oracle.com if you need additional information or have any
  25  * questions.
  26  */
  27 
  28 package com.sun.javatest.exec;
  29 
  30 import com.sun.javatest.exec.Session.Event;
  31 import java.awt.BorderLayout;
  32 import java.awt.EventQueue;
  33 import java.util.Collection;
  34 import java.util.List;
  35 import java.util.Map;
  36 import java.util.Set;
  37 import java.util.Vector;
  38 
  39 import javax.swing.Action;
  40 import javax.swing.JCheckBox;
  41 import javax.swing.JComponent;
  42 import javax.swing.JDialog;
  43 import javax.swing.JMenu;
  44 import javax.swing.JOptionPane;
  45 import javax.swing.JPanel;
  46 import javax.swing.JTextArea;
  47 
  48 import com.sun.javatest.AllTestsFilter;
  49 import com.sun.javatest.Harness;
  50 import com.sun.javatest.InterviewParameters;
  51 import com.sun.javatest.LastRunFilter;
  52 import com.sun.javatest.ParameterFilter;
  53 import com.sun.javatest.Parameters;
  54 import com.sun.javatest.TestFilter;
  55 import com.sun.javatest.TestResult;
  56 import com.sun.javatest.TestSuite;
  57 import com.sun.javatest.tool.Preferences;
  58 import com.sun.javatest.tool.UIFactory;
  59 import com.sun.javatest.util.PrefixMap;
  60 
  61 /**
  62  * This class handles all the special filter juggling that exec tool needs to do.
  63  */
  64 public class ET_FilterHandler implements ET_FilterControl, Session.Observer {
  65     ET_FilterHandler(JComponent parent, ExecModel model, Harness h, UIFactory uif,
  66                      Map map) {
  67         this(parent, model, uif);
  68         setHarness(h);
  69         restore(map);
  70     }
  71 
  72     protected ET_FilterHandler(JComponent parent, ExecModel model, UIFactory uif) {
  73         this.uif = uif;
  74         this.model = model;
  75         this.parentComponent = parent;
  76 
  77         allFilters = new Vector<>();
  78 
  79     }
  80 
  81     public void setHarness(Harness h) {
  82         h.addObserver(new Watcher());
  83     }
  84 
  85     FilterConfig loadFilters() {
  86         // this method may eventually do fancy things to scan the classpath or
  87         // preferences for custom plugin tools, for now it is hardcoded
  88 
  89         if (fConfig != null)
  90             return fConfig;
  91 
  92         fConfig = new FilterConfig(model, parentComponent, uif);
  93 
  94         fHandler = fConfig.createFilterSelectionHandler();
  95 
  96         // add observer here so that the menu gets the additions
  97         // also watches for user selection of new filter
  98         /*
  99         filterWatcher = new FilterWatcher();
 100         filterHandler.addObserver(filterWatcher);
 101         */
 102 
 103         // last run filter
 104         TestFilter filter = ltrFilter = new LastRunFilter();
 105         allFilters.add(filter);
 106         fConfig.add(filter);
 107 
 108         // current parameter filter
 109         filter = paramFilter = new ParameterFilter();
 110         allFilters.add(filter);
 111         fConfig.add(filter);
 112 
 113         List<TestFilter> usersFilters = getUsersFilters();
 114         if (usersFilters != null) {
 115             for (TestFilter tf: usersFilters) {
 116                 allFilters.add(tf);
 117                 fConfig.add(tf);
 118             }
 119         }
 120 /*
 121         if (model.getContextManager() != null &&
 122                 model.getContextManager().getFeatureManager() != null) {
 123             if (model.getContextManager().getFeatureManager().isEnabled(FeatureManager.TEMPLATE_USAGE)) {
 124                 tfilter = new TemplateParameterFilter();
 125                 allFilters.add(tfilter);
 126                 fConfig.add(tfilter);
 127             }
 128         }
 129  */
 130 
 131         // filter which accepts all tests
 132         allFilter = new AllTestsFilter();
 133         allFilters.add(allFilter);
 134         fConfig.add(allFilter);
 135 
 136         updateCustomFilter();
 137 
 138         // establish the default
 139         fHandler.setFilter(getDefaultFilter(map));
 140 
 141         return fConfig;
 142     }
 143 
 144     /**
 145      * Subclasses may override this method to insert filters
 146      * like TemplateFilter
 147      *
 148      * @return list of filters defined for the User's TestSuite, or null
 149      */
 150     protected List<TestFilter> getUsersFilters() {
 151         return null;
 152     }
 153 
 154     public JMenu getFilterMenu() {
 155         return getFilterSelectionHandler().getFilterMenu();
 156     }
 157 
 158     FilterSelectionHandler getFilterSelectionHandler() {
 159         loadFilters();
 160         return fHandler;
 161     }
 162 
 163     private TestFilter getDefaultFilter(Map map) {
 164         if (map != null) {
 165             String pref = (String)(map.get(ExecTool.ACTIVE_FILTER));
 166 
 167             // try to use filter indicated in preference
 168             for (int i = 0; i < allFilters.size(); i++) {
 169                 if (allFilters.elementAt(i).getClass().getName().equals(pref))
 170                     return (TestFilter)allFilters.elementAt(i);
 171             }   // for
 172         }
 173 
 174         // default filter
 175         return allFilter;
 176     }
 177 
 178     protected void updateFilters() {
 179         loadFilters();
 180 
 181         // special code for custom filter loading
 182         updateCustomFilter();
 183 
 184         // update Last Test Run filtered if needed
 185         if (!ltrFilter.isWorkDirectorySet())
 186             ltrFilter.setWorkDirectory(model.getWorkDirectory());
 187 
 188         InterviewParameters ips = model.getInterviewParameters();
 189         if (ips == null)        // this filter does not apply now
 190             return;
 191 
 192         paramFilter.update(ips);
 193 
 194         TestFilter newCertFilter = ips.getRelevantTestFilter();
 195         // check for filter behavior equality
 196         if (newCertFilter == null) {
 197             if (certFilter != null) {
 198                 // we had a certification filter earlier, now it is gone
 199                 if (fHandler.getActiveFilter() == certFilter) {
 200                     // switch to another filter before removing
 201                     // XXX may want to notify user!
 202                     fHandler.setFilter(paramFilter);
 203                 }
 204 
 205                 fConfig.remove(certFilter);
 206             }
 207             else {
 208                 // FilterConfig is clean
 209             }
 210         }   // outer if
 211         else if (!newCertFilter.equals(certFilter)) {
 212             // check for reference equality
 213             if (newCertFilter == certFilter) {
 214                 // this is ignored by fConfig if it is not relevant
 215                 fConfig.notifyUpdated(certFilter);
 216             }
 217             else {
 218                 // rm old one, put in new one
 219                 fConfig.add(newCertFilter);
 220 
 221                 if (fHandler.getActiveFilter() == certFilter) {
 222                     // switch to another filter before removing
 223                     // XXX may want to notify user!
 224                     fHandler.setFilter(newCertFilter);
 225                 }
 226 
 227                 fConfig.remove(certFilter);
 228                 certFilter = newCertFilter;
 229             }
 230         }
 231         else {
 232             // filter is the same
 233         }
 234     }
 235 
 236     public JMenu getMenu() {
 237         loadFilters();
 238         return null;
 239         //return fHandler.getFilterMenu();
 240     }
 241 
 242     public FilterConfig getFilterConfig() {
 243         return fConfig;
 244     }
 245 
 246     /**
 247      * Save internal state.
 248      */
 249     public void save(Map<String, String> m) {
 250         // -------- saved to given map (desktop) -------
 251         Preferences prefs = Preferences.access();
 252         TestFilter aFilter = fHandler.getActiveFilter();
 253         m.put(ExecTool.ACTIVE_FILTER, aFilter.getClass().getName());
 254 
 255         // -------- saved to global prefs -------
 256         TestSuite ts = model.getTestSuite();
 257         String tsId = null;
 258         String tsName = null;
 259         if (ts != null) {
 260             tsId = ts.getID();
 261             tsName = ts.getName();
 262         }
 263 
 264         int prefIndex = getPreferenceIndexForWrite(prefs, tsId);
 265 
 266         ConstrainedPreferenceMap cpm = new ConstrainedPreferenceMap(prefs);
 267         // using meta_ prefix for info not written by the filter itself
 268         PrefixMap<String> pm = new PrefixMap<>(cpm, FILTER_PREFIX + prefIndex);
 269 
 270         // it's really a special case to have a pref. entry which does not
 271         // have a tsId associated
 272         // it should really only be used (if at all) if a default set of
 273         // settings is being saved
 274         if (tsId != null) {
 275             pm.put(META_ID, tsId);
 276             pm.put(META_NAME, tsName);
 277         }
 278 
 279         pm.put(META_CLASS, bctf.getClass().getName());
 280         bctf.save(pm);
 281 
 282         prefs.save();
 283     }
 284     public void restore(Map m) {
 285         this.map = m;
 286         fHandler.setFilter(getDefaultFilter(m));
 287     }
 288     public void updateGUI() {
 289         // do nothing
 290     }
 291 
 292     public List<Action> getToolBarActionList() {
 293         return null;
 294     }
 295 
 296     public void dispose() {
 297         // do nothing
 298     }
 299 
 300 
 301     private void updateCustomFilter() {
 302         // we only go thru here once per init.
 303         if (lastTs != null)
 304             return;
 305 
 306         // load from prefs
 307         lastTs = model.getTestSuite();
 308         String tsId = null;
 309         String tsName = null;
 310 
 311         // may be null, meaning that the exec tool has no TS
 312         if (lastTs != null) {
 313             tsId = lastTs.getID();
 314             tsName = lastTs.getName();
 315         }
 316 
 317         Preferences prefs = Preferences.access();
 318         int prefIndex = getPreferenceIndexForRead(prefs, tsId);
 319 
 320         // using META_ prefix for info not written by the filter itself
 321         // XXX could check value of c in the future
 322         //String c = prefs.getPreference(FILTER_PREFIX + "." + prefIndex + META_CLASS);
 323 
 324         if (prefIndex >= 0) {
 325             // load previous settings
 326             ConstrainedPreferenceMap cpm = new ConstrainedPreferenceMap(prefs);
 327             PrefixMap<String> pm = new PrefixMap<>(cpm, FILTER_PREFIX + prefIndex);
 328 
 329             if (bctf == null) {     // init
 330                 bctf = new BasicCustomTestFilter(pm, model, uif);
 331                 allFilters.add(bctf);
 332                 fConfig.add(bctf);
 333             }
 334             else {                  // tell filter load settings
 335                 bctf.load(pm);
 336                 fHandler.updateFilterMetaInfo(bctf);
 337             }
 338         }
 339         else if (bctf == null) {
 340             // no previous settings to use
 341             bctf = new BasicCustomTestFilter(model, uif);
 342             allFilters.add(bctf);
 343             fConfig.add(bctf);
 344         }
 345 
 346     }
 347 
 348     /**
 349      * Find the index in the preferences which is appropriate for this filter
 350      * to save its info in.  Returns the next available one if there isn't
 351      * an existing one.
 352      * @param tsId May be null.
 353      */
 354     private int getPreferenceIndexForWrite(Preferences p, String tsId) {
 355         // pref. index 0 is the default when no tsId is available
 356         // pref. encoding is:
 357         // FILTER_PREFIX + <number> + <filter data>
 358         int index = 0;
 359         int numFilters = getPreferenceCount(p);
 360 
 361         if (tsId != null && numFilters != 0) {
 362             index = getPreferenceIndex(p, tsId, numFilters);
 363 
 364             if (index == -1) {      // not found
 365                 index = ++numFilters;
 366                 p.setPreference(FILTER_PREFIX + ".count",
 367                         Integer.toString(numFilters));
 368             }
 369         }
 370         else if (tsId != null && numFilters == 0) {
 371             index = 1;
 372             numFilters = 1;
 373             p.setPreference(FILTER_PREFIX + ".count",
 374                     Integer.toString(numFilters));
 375         }
 376         else {
 377             // index remains 0, the default
 378         }
 379         return index;
 380     }
 381 
 382     /**
 383      * Which pref index should be read for the given test suite.
 384      * @return -1 if there is no pref. to read.
 385      */
 386     private int getPreferenceIndexForRead(Preferences p, String tsId) {
 387         int numFilters = getPreferenceCount(p);
 388         int result = -1;
 389 
 390         if (numFilters == 0)
 391             result = -1;
 392         else {
 393             result = getPreferenceIndex(p, tsId, numFilters);
 394 
 395             // read default values from index 0 if a match for the given
 396             // TS was not found
 397             /*
 398             if (result == -1 && numFilters > 0)
 399                 result = 0;
 400             */
 401         }
 402 
 403         return result;
 404     }
 405 
 406     /**
 407      * Do not call this directly.
 408      * @param numFilters A number greater than zero.
 409      * @return -1 if not found.
 410      */
 411     private int getPreferenceIndex(Preferences p, String tsId, int numFilters) {
 412         int index = -1;
 413 
 414         for (int i = 1; i <= numFilters; i++) {
 415             String id = p.getPreference(FILTER_PREFIX + i + "." + META_ID);
 416             if (id.equals(tsId)) {
 417                 index = i;
 418                 break;
 419             }
 420         }   // for
 421 
 422         if (index > numFilters)
 423             return -1;
 424         else
 425             return index;
 426     }
 427 
 428     /**
 429      * How many indexes are we using for filters right now.
 430      * @return -1 for none.
 431      */
 432     private int getPreferenceCount(Preferences p) {
 433         int numFilters = Integer.parseInt(
 434                             p.getPreference(FILTER_PREFIX + ".count", "0"));
 435 
 436         return numFilters;
 437     }
 438 
 439     private FilterConfig fConfig;
 440     private FilterSelectionHandler fHandler;
 441     private ExecModel model;
 442     private UIFactory uif;
 443     private JComponent parentComponent;
 444     private Map map;        // saved desktop map to restore from
 445 
 446     // filters
 447     private LastRunFilter ltrFilter;        // last test run
 448     private ParameterFilter paramFilter;    // current param filter
 449     private BasicCustomTestFilter bctf;     // "custom" filter
 450     private AllTestsFilter allFilter;
 451     private TestFilter certFilter;          // "certification" filter
 452     protected Vector<TestFilter> allFilters;
 453 
 454     // custom filter info
 455     private TestSuite lastTs;
 456 
 457     // preferences constants
 458     private static final String FILTER_PREFIX = "exec.vfilters";
 459     private static final String BTF_PREFIX = FILTER_PREFIX + ".btf";
 460     private static final String META_ID = "meta_tsid";
 461     private static final String META_NAME = "meta_tsn";
 462     private static final String META_CLASS = "meta_class";
 463 
 464     public void updated(Event ev) {
 465         if (ev instanceof BasicSession.E_NewConfig) {
 466             paramFilter.update(((BasicSession.E_NewConfig)ev).ip);
 467         }
 468         updateFilters();
 469     }
 470 
 471     /**
 472      * This class is completely private and only implements what we
 473      * want to use here.
 474      */
 475     private static class ConstrainedPreferenceMap implements Map<String, String> {
 476         ConstrainedPreferenceMap(Preferences p) {
 477             prefs = p;
 478         }
 479 
 480         public void clear() {
 481             throw new UnsupportedOperationException();
 482         }
 483 
 484         public boolean containsKey(Object o) {
 485             throw new UnsupportedOperationException();
 486         }
 487 
 488         public boolean containsValue(Object v) {
 489             throw new UnsupportedOperationException();
 490         }
 491 
 492         public Set<Map.Entry<String, String>> entrySet() {
 493             throw new UnsupportedOperationException();
 494         }
 495 
 496         public String get(Object key) {
 497             if (!(key instanceof String))
 498                 throw new IllegalArgumentException("key must be a string");
 499 
 500             return prefs.getPreference((String)key);
 501         }
 502 
 503         public boolean isEmpty() {
 504             throw new UnsupportedOperationException();
 505         }
 506 
 507         public Set<String> keySet() {
 508             throw new UnsupportedOperationException();
 509         }
 510 
 511         public String put(String key, String value) {
 512             if (!(key instanceof String) ||
 513                 !(value instanceof String))
 514                 throw new IllegalArgumentException("both args must be strings");
 515 
 516             prefs.setPreference(key, value);
 517 
 518             return null;
 519         }
 520 
 521         public void putAll(Map t) {
 522             throw new UnsupportedOperationException();
 523         }
 524 
 525         public String remove(Object key) {
 526             throw new UnsupportedOperationException();
 527         }
 528 
 529         public int size() {
 530             throw new UnsupportedOperationException();
 531         }
 532 
 533         public Collection<String> values() {
 534             throw new UnsupportedOperationException();
 535         }
 536 
 537         public String get(String key) {
 538             return (String)(prefs.getPreference(key));
 539         }
 540 
 541         private Preferences prefs;
 542     }
 543 
 544     private class FilterWatcher implements FilterSelectionHandler.Observer {
 545         // NOTE: disconnected in loadFilters()
 546         // ---------- FilterConfig.Observer ----------
 547         public void filterUpdated(TestFilter f) {
 548             // ignore here
 549         }
 550 
 551         public void filterSelected(TestFilter f) {
 552             // change menu selection
 553             /* XXX not implemented yet
 554             int index = items.getValueIndex(f);
 555 
 556             if (index != -1) {
 557                 filterMenu.setSelected((Component)(items.getKeyAt(index)));
 558             }
 559             */
 560 
 561             // XXX avoid poking an uninitialized GUI what is a better check
 562             //if (testTreePanel != null)
 563         //      updateGUI();
 564         }
 565 
 566         public void filterAdded(TestFilter f) {
 567             // add to menu
 568             /* XXX not implemented yet
 569             JMenuItem mi = new JRadioButtonMenuItem(f.getName());
 570             mi.addActionListener(this);
 571             filterMenu.add(mi);
 572             items.put(mi, f);
 573             */
 574         }
 575 
 576         public void filterRemoved(TestFilter f) {
 577             // rm from menu
 578             /* XXX not implemented yet
 579             int index = items.getValueIndex(f);
 580             filterMenu.remove(index);
 581             items.remove(index);
 582             */
 583         }
 584     }
 585 
 586     class Watcher implements Harness.Observer {
 587         public void startingTestRun(Parameters params) {
 588             ltrFilter.setLastStartTime(System.currentTimeMillis());
 589             ltrFilter.clearTestURLs();
 590 
 591             if (fHandler.getActiveFilter() == allFilter) {
 592                 final Preferences p = Preferences.access();
 593                 if (p.getPreference(ExecTool.FILTER_WARN_PREF, "true").equals("true")) {
 594                     final JPanel pan = uif.createPanel("notagain", false);
 595                     final JCheckBox cb = uif.createCheckBox("exec.fltr.noShow",
 596                                                       false);
 597                     final JTextArea msg = uif.createMessageArea("exec.fltr.note");
 598                     EventQueue.invokeLater(new Runnable() {
 599                         public void run() {
 600                             pan.setLayout(new BorderLayout());
 601                             pan.add(cb, BorderLayout.SOUTH);
 602                             pan.add(msg, BorderLayout.CENTER);
 603 
 604                             JOptionPane pane = new JOptionPane(pan, JOptionPane.INFORMATION_MESSAGE,
 605                                 JOptionPane.DEFAULT_OPTION, null, null, null);
 606                             JDialog dialog = pane.createDialog(parentComponent, uif.getI18NString("exec.fltr.note.title"));
 607                             dialog.show();
 608 
 609                             // can't use this, it doesn't indicate if the user pressed
 610                             // OK or canceled the dialog some other way
 611                             //uif.showCustomOptionDialog("exec.fltr.note", pan);
 612 
 613                             Object selectedValue = pane.getValue();
 614                             if( (selectedValue instanceof Integer) &&
 615                                 ((Integer)selectedValue).intValue() >= 0 )
 616                                 p.setPreference(ExecTool.FILTER_WARN_PREF,
 617                                                 Boolean.toString(!cb.isSelected()));
 618                         }
 619                     });
 620                 }
 621             }   // if
 622         }
 623 
 624         public void startingTest(TestResult tr) {
 625             ltrFilter.addTestURL(tr.getTestName());
 626         }
 627 
 628         public void finishedTest(TestResult tr) { }
 629 
 630         public void stoppingTestRun() { }
 631 
 632         public void finishedTesting() { }
 633 
 634         public void finishedTestRun(boolean allOK) { }
 635 
 636         public void error(String msg) { }
 637     }
 638 }
 639