1 /*
   2  * Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.tools.serialver;
  27 
  28 import java.io.*;
  29 import java.awt.*;
  30 import java.applet.*;
  31 import java.io.ObjectStreamClass;
  32 import java.util.Properties;
  33 import java.text.MessageFormat;
  34 import java.util.ResourceBundle;
  35 import java.util.MissingResourceException;
  36 import java.net.URLClassLoader;
  37 import java.net.URL;
  38 import java.net.MalformedURLException;
  39 import java.util.StringTokenizer;
  40 import sun.net.www.ParseUtil;
  41 
  42 public class SerialVer extends Applet {
  43     GridBagLayout gb;
  44     TextField classname_t;
  45     Button show_b;
  46     TextField serialversion_t;
  47     Label footer_l;
  48 
  49     private static final long serialVersionUID = 7666909783837760853L;
  50 
  51     public synchronized void init() {
  52         gb = new GridBagLayout();
  53         setLayout(gb);
  54 
  55         GridBagConstraints c = new GridBagConstraints();
  56         c.fill = GridBagConstraints.BOTH;
  57 
  58         Label l1 = new Label(Res.getText("FullClassName"));
  59         l1.setAlignment(Label.RIGHT);
  60         gb.setConstraints(l1, c);
  61         add(l1);
  62 
  63         classname_t = new TextField(20);
  64         c.gridwidth = GridBagConstraints.RELATIVE;
  65         c.weightx = 1.0;
  66         gb.setConstraints(classname_t, c);
  67         add(classname_t);
  68 
  69         show_b = new Button(Res.getText("Show"));
  70         c.gridwidth = GridBagConstraints.REMAINDER;
  71         c.weightx = 0.0;        /* Don't grow the button */
  72         gb.setConstraints(show_b, c);
  73         add(show_b);
  74 
  75         Label l2 = new Label(Res.getText("SerialVersion"));
  76         l2.setAlignment(Label.RIGHT);
  77         c.gridwidth = 1;
  78         gb.setConstraints(l2, c);
  79         add(l2);
  80 
  81         serialversion_t = new TextField(50);
  82         serialversion_t.setEditable(false);
  83         c.gridwidth = GridBagConstraints.REMAINDER;
  84         gb.setConstraints(serialversion_t, c);
  85         add(serialversion_t);
  86 
  87         footer_l = new Label();
  88         c.gridwidth = GridBagConstraints.REMAINDER;
  89         gb.setConstraints(footer_l, c);
  90         add(footer_l);
  91 
  92         /* Give the focus to the type-in area */
  93         classname_t.requestFocus();
  94     }
  95 
  96     public void start() {
  97         /* Give the focus to the type-in area */
  98         classname_t.requestFocus();
  99     }
 100 
 101     @SuppressWarnings("deprecation")
 102     public boolean action(Event ev, Object obj) {
 103         if (ev.target == classname_t) {
 104             show((String)ev.arg);
 105             return true;
 106         } else if (ev.target == show_b) {
 107             show(classname_t.getText());
 108             return true;
 109         }
 110         return false;
 111     }
 112 
 113 
 114     @SuppressWarnings("deprecation")
 115     public boolean handleEvent(Event ev) {
 116         boolean rc = super.handleEvent(ev);
 117         return rc;
 118     }
 119 
 120     /**
 121      * Lookup the specified classname and display it.
 122      */
 123     void show(String classname) {
 124         try {
 125             footer_l.setText(""); // Clear the message
 126             serialversion_t.setText(""); // clear the last value
 127 
 128             if (classname.equals("")) {
 129                 return;
 130             }
 131 
 132             String s = serialSyntax(classname);
 133             if (s != null) {
 134                 serialversion_t.setText(s);
 135             } else {
 136                 footer_l.setText(Res.getText("NotSerializable", classname));
 137             }
 138         } catch (ClassNotFoundException cnf) {
 139             footer_l.setText(Res.getText("ClassNotFound", classname));
 140         }
 141     }
 142 
 143     /*
 144      * A class loader that will load from the CLASSPATH environment
 145      * variable set by the user.
 146      */
 147     static URLClassLoader loader = null;
 148 
 149     /*
 150      * Create a URL class loader that will load classes from the
 151      * specified classpath.
 152      */
 153     static void initializeLoader(String cp)
 154                                 throws MalformedURLException, IOException {
 155         URL[] urls;
 156         StringTokenizer st = new StringTokenizer(cp, File.pathSeparator);
 157         int count = st.countTokens();
 158         urls = new URL[count];
 159         for (int i = 0; i < count; i++) {
 160             urls[i] = ParseUtil.fileToEncodedURL(
 161                 new File(new File(st.nextToken()).getCanonicalPath()));
 162         }
 163         loader = new URLClassLoader(urls);
 164     }
 165 
 166     /*
 167      * From the classname find the serialVersionUID string formatted
 168      * for to be copied to a java class.
 169      */
 170     static String serialSyntax(String classname) throws ClassNotFoundException {
 171         String ret = null;
 172         boolean classFound = false;
 173 
 174         // If using old style of qualifyling inner classes with '$'s.
 175         if (classname.indexOf('$') != -1) {
 176             ret = resolveClass(classname);
 177         } else {
 178             /* Try to resolve the fully qualified name and if that fails, start
 179              * replacing the '.'s with '$'s starting from the last '.', until
 180              * the class is resolved.
 181              */
 182             try {
 183                 ret = resolveClass(classname);
 184                 classFound = true;
 185             } catch (ClassNotFoundException e) {
 186                 /* Class not found so far */
 187             }
 188             if (!classFound) {
 189                 StringBuffer workBuffer = new StringBuffer(classname);
 190                 String workName = workBuffer.toString();
 191                 int i;
 192                 while ((i = workName.lastIndexOf('.')) != -1 && !classFound) {
 193                     workBuffer.setCharAt(i, '$');
 194                     try {
 195                         workName = workBuffer.toString();
 196                         ret = resolveClass(workName);
 197                         classFound = true;
 198                     } catch (ClassNotFoundException e) {
 199                         /* Continue searching */
 200                     }
 201                 }
 202             }
 203             if (!classFound) {
 204                 throw new ClassNotFoundException();
 205             }
 206         }
 207         return ret;
 208     }
 209 
 210     static String resolveClass(String classname) throws ClassNotFoundException {
 211         Class<?> cl = Class.forName(classname, false, loader);
 212         ObjectStreamClass desc = ObjectStreamClass.lookup(cl);
 213         if (desc != null) {
 214             return "    private static final long serialVersionUID = " +
 215                 desc.getSerialVersionUID() + "L;";
 216         } else {
 217             return null;
 218         }
 219     }
 220 
 221     @SuppressWarnings("deprecation")
 222     private static void showWindow(Window w) {
 223         w.show();
 224     }
 225 
 226     public static void main(String[] args) {
 227         boolean show = false;
 228         String envcp = null;
 229         int i = 0;
 230 
 231         if (args.length == 0) {
 232             usage();
 233             System.exit(1);
 234         }
 235 
 236         for (i = 0; i < args.length; i++) {
 237             if (args[i].equals("-show")) {
 238                 show = true;
 239             } else if (args[i].equals("-classpath")) {
 240                 if ((i+1 == args.length) || args[i+1].startsWith("-")) {
 241                     System.err.println(Res.getText("error.missing.classpath"));
 242                     usage();
 243                     System.exit(1);
 244                 }
 245                 envcp = new String(args[i+1]);
 246                 i++;
 247             }  else if (args[i].startsWith("-")) {
 248                 System.err.println(Res.getText("invalid.flag", args[i]));
 249                 usage();
 250                 System.exit(1);
 251             } else {
 252                 break;          // drop into processing class names
 253             }
 254         }
 255 
 256 
 257         /*
 258          * Get user's CLASSPATH environment variable, if the -classpath option
 259          * is not defined, and make a loader that can read from that path.
 260          */
 261         if (envcp == null) {
 262             envcp = System.getProperty("env.class.path");
 263             /*
 264              * If environment variable not set, add current directory to path.
 265              */
 266             if (envcp == null) {
 267                 envcp = ".";
 268             }
 269         }
 270 
 271         try {
 272             initializeLoader(envcp);
 273         } catch (MalformedURLException mue) {
 274             System.err.println(Res.getText("error.parsing.classpath", envcp));
 275             System.exit(2);
 276         } catch (IOException ioe) {
 277             System.err.println(Res.getText("error.parsing.classpath", envcp));
 278             System.exit(3);
 279         }
 280 
 281         if (!show) {
 282             /*
 283              * Check if there are any class names specified, if it is not a
 284              * invocation with the -show option.
 285              */
 286             if (i == args.length) {
 287                 usage();
 288                 System.exit(1);
 289             }
 290 
 291             /*
 292              * The rest of the parameters are classnames.
 293              */
 294             boolean exitFlag = false;
 295             for (i = i; i < args.length; i++ ) {
 296                 try {
 297                     String syntax = serialSyntax(args[i]);
 298                     if (syntax != null)
 299                         System.out.println(args[i] + ":" + syntax);
 300                     else {
 301                         System.err.println(Res.getText("NotSerializable",
 302                             args[i]));
 303                         exitFlag = true;
 304                     }
 305                 } catch (ClassNotFoundException cnf) {
 306                     System.err.println(Res.getText("ClassNotFound", args[i]));
 307                     exitFlag = true;
 308                 }
 309             }
 310             if (exitFlag) {
 311                 System.exit(1);
 312             }
 313         } else {
 314             if (i < args.length) {
 315                 System.err.println(Res.getText("ignoring.classes"));
 316                 System.exit(1);
 317             }
 318             Frame f =  new SerialVerFrame();
 319             //  f.setLayout(new FlowLayout());
 320             SerialVer sv = new SerialVer();
 321             sv.init();
 322 
 323             f.add("Center", sv);
 324             f.pack();
 325             showWindow(f);
 326         }
 327     }
 328 
 329 
 330     /**
 331      * Usage
 332      */
 333     public static void usage() {
 334         System.err.println(Res.getText("usage"));
 335     }
 336 
 337 }
 338 
 339 /**
 340  * Top level frame so serialVer can be run as an main program
 341  * and have an exit menu item.
 342  */
 343 class SerialVerFrame extends Frame {
 344     MenuBar menu_mb;
 345     Menu file_m;
 346     MenuItem exit_i;
 347 
 348     private static final long serialVersionUID = -7248105987187532533L;
 349 
 350     /*
 351      * Construct a new Frame with title and menu.
 352      */
 353     SerialVerFrame() {
 354         super(Res.getText("SerialVersionInspector"));
 355 
 356         /* Create the file menu */
 357         file_m = new Menu(Res.getText("File"));
 358         file_m.add(exit_i = new MenuItem(Res.getText("Exit")));
 359 
 360         /* Now add the file menu to the menu bar  */
 361         menu_mb = new MenuBar();
 362         menu_mb.add(file_m);
 363 
 364         /* Add the menubar to the frame */
 365         // Bug in JDK1.1        setMenuBar(menu_mb);
 366     }
 367 
 368     /*
 369      * Handle a window destroy event by exiting.
 370      */
 371     @SuppressWarnings("deprecation")
 372     public boolean handleEvent(Event e) {
 373         if (e.id == Event.WINDOW_DESTROY) {
 374             exit(0);
 375         }
 376         return super.handleEvent(e);
 377     }
 378     /*
 379      * Handle an Exit event by exiting.
 380      */
 381     @SuppressWarnings("deprecation")
 382     public boolean action(Event ev, Object obj) {
 383         if (ev.target == exit_i) {
 384             exit(0);
 385         }
 386         return false;
 387     }
 388 
 389     /*
 390      * Cleanup and exit.
 391      */
 392     void exit(int ret) {
 393         System.exit(ret);
 394     }
 395 
 396 }
 397 
 398 /**
 399  * Utility for integrating with serialver and for localization.
 400  * Handle Resources. Access to error and warning counts.
 401  * Message formatting.
 402  *
 403  * @see java.util.ResourceBundle
 404  * @see java.text.MessageFormat
 405  */
 406 class Res {
 407 
 408     private static ResourceBundle messageRB;
 409 
 410     /**
 411      * Initialize ResourceBundle
 412      */
 413     static void initResource() {
 414         try {
 415             messageRB =
 416                 ResourceBundle.getBundle("sun.tools.serialver.resources.serialver");
 417         } catch (MissingResourceException e) {
 418             throw new Error("Fatal: Resource for serialver is missing");
 419         }
 420     }
 421 
 422     /**
 423      * get and format message string from resource
 424      *
 425      * @param key selects message from resource
 426      */
 427     static String getText(String key) {
 428         return getText(key, (String)null);
 429     }
 430 
 431     /**
 432      * get and format message string from resource
 433      *
 434      * @param key selects message from resource
 435      * @param a1 first argument
 436      */
 437     static String getText(String key, String a1) {
 438         return getText(key, a1, null);
 439     }
 440 
 441     /**
 442      * get and format message string from resource
 443      *
 444      * @param key selects message from resource
 445      * @param a1 first argument
 446      * @param a2 second argument
 447      */
 448     static String getText(String key, String a1, String a2) {
 449         return getText(key, a1, a2, null);
 450     }
 451 
 452     /**
 453      * get and format message string from resource
 454      *
 455      * @param key selects message from resource
 456      * @param a1 first argument
 457      * @param a2 second argument
 458      * @param a3 third argument
 459      */
 460     static String getText(String key, String a1, String a2, String a3) {
 461         if (messageRB == null) {
 462             initResource();
 463         }
 464         try {
 465             String message = messageRB.getString(key);
 466             return MessageFormat.format(message, a1, a2, a3);
 467         } catch (MissingResourceException e) {
 468             throw new Error("Fatal: Resource for serialver is broken. There is no " + key + " key in resource.");
 469         }
 470     }
 471 }