1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21package proguard.gui; 22 23import proguard.*; 24import proguard.util.ListUtil; 25 26import javax.swing.*; 27import java.awt.*; 28import java.awt.event.*; 29import java.io.*; 30import java.util.List; 31 32/** 33 * This <code>ListPanel</code> allows the user to add, edit, filter, move, and 34 * remove ClassPathEntry objects in a ClassPath object. 35 * 36 * @author Eric Lafortune 37 */ 38class ClassPathPanel extends ListPanel 39{ 40 private final JFrame owner; 41 private final boolean inputAndOutput; 42 private final JFileChooser chooser; 43 private final FilterDialog filterDialog; 44 45 46 public ClassPathPanel(JFrame owner, boolean inputAndOutput) 47 { 48 super(); 49 50 super.firstSelectionButton = inputAndOutput ? 3 : 2; 51 52 this.owner = owner; 53 this.inputAndOutput = inputAndOutput; 54 55 list.setCellRenderer(new MyListCellRenderer()); 56 57 chooser = new JFileChooser(""); 58 chooser.setMultiSelectionEnabled(true); 59 chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); 60 chooser.addChoosableFileFilter( 61 new ExtensionFileFilter(msg("jarWarEarZipExtensions"), 62 new String[] { ".jar", ".war", ".ear", ".zip" })); 63 chooser.setApproveButtonText(msg("ok")); 64 65 filterDialog = new FilterDialog(owner, msg("enterFilter")); 66 67 addAddButton(inputAndOutput, false); 68 if (inputAndOutput) 69 { 70 addAddButton(inputAndOutput, true); 71 } 72 addEditButton(); 73 addFilterButton(); 74 addRemoveButton(); 75 addUpButton(); 76 addDownButton(); 77 78 enableSelectionButtons(); 79 } 80 81 82 protected void addAddButton(boolean inputAndOutput, 83 final boolean isOutput) 84 { 85 JButton addButton = new JButton(msg(inputAndOutput ? 86 isOutput ? "addOutput" : 87 "addInput" : 88 "add")); 89 addButton.addActionListener(new ActionListener() 90 { 91 public void actionPerformed(ActionEvent e) 92 { 93 chooser.setDialogTitle(msg("addJars")); 94 chooser.setSelectedFile(null); 95 chooser.setSelectedFiles(null); 96 97 int returnValue = chooser.showOpenDialog(owner); 98 if (returnValue == JFileChooser.APPROVE_OPTION) 99 { 100 File[] selectedFiles = chooser.getSelectedFiles(); 101 ClassPathEntry[] entries = classPathEntries(selectedFiles, isOutput); 102 103 // Add the new elements. 104 addElements(entries); 105 } 106 } 107 }); 108 109 addButton(tip(addButton, inputAndOutput ? 110 isOutput ? "addOutputTip" : 111 "addInputTip" : 112 "addTip")); 113 } 114 115 116 protected void addEditButton() 117 { 118 JButton editButton = new JButton(msg("edit")); 119 editButton.addActionListener(new ActionListener() 120 { 121 public void actionPerformed(ActionEvent e) 122 { 123 boolean isOutput = false; 124 125 int[] selectedIndices = list.getSelectedIndices(); 126 127 // Copy the Object array into a File array. 128 File[] selectedFiles = new File[selectedIndices.length]; 129 for (int index = 0; index < selectedFiles.length; index++) 130 { 131 ClassPathEntry entry = 132 (ClassPathEntry)listModel.getElementAt(selectedIndices[index]); 133 134 isOutput = entry.isOutput(); 135 136 selectedFiles[index] = entry.getFile(); 137 } 138 139 chooser.setDialogTitle(msg("chooseJars")); 140 141 // Up to JDK 1.3.1, setSelectedFiles doesn't show in the file 142 // chooser, so we just use setSelectedFile first. It also sets 143 // the current directory. 144 chooser.setSelectedFile(selectedFiles[0].getAbsoluteFile()); 145 chooser.setSelectedFiles(selectedFiles); 146 147 int returnValue = chooser.showOpenDialog(owner); 148 if (returnValue == JFileChooser.APPROVE_OPTION) 149 { 150 selectedFiles = chooser.getSelectedFiles(); 151 ClassPathEntry[] entries = classPathEntries(selectedFiles, isOutput); 152 153 // If there are the same number of files selected now as 154 // there were before, we can just replace the old ones. 155 if (selectedIndices.length == selectedFiles.length) 156 { 157 // Replace the old elements. 158 setElementsAt(entries, selectedIndices); 159 } 160 else 161 { 162 // Remove the old elements. 163 removeElementsAt(selectedIndices); 164 165 // Add the new elements. 166 addElements(entries); 167 } 168 } 169 } 170 }); 171 172 addButton(tip(editButton, "editTip")); 173 } 174 175 176 protected void addFilterButton() 177 { 178 JButton filterButton = new JButton(msg("filter")); 179 filterButton.addActionListener(new ActionListener() 180 { 181 public void actionPerformed(ActionEvent e) 182 { 183 if (!list.isSelectionEmpty()) 184 { 185 int[] selectedIndices = list.getSelectedIndices(); 186 187 // Put the filters of the first selected entry in the dialog. 188 getFiltersFrom(selectedIndices[0]); 189 190 int returnValue = filterDialog.showDialog(); 191 if (returnValue == FilterDialog.APPROVE_OPTION) 192 { 193 // Apply the entered filters to all selected entries. 194 setFiltersAt(selectedIndices); 195 } 196 } 197 } 198 }); 199 200 addButton(tip(filterButton, "filterTip")); 201 } 202 203 204 /** 205 * Sets the ClassPath to be represented in this panel. 206 */ 207 public void setClassPath(ClassPath classPath) 208 { 209 listModel.clear(); 210 211 if (classPath != null) 212 { 213 for (int index = 0; index < classPath.size(); index++) 214 { 215 listModel.addElement(classPath.get(index)); 216 } 217 } 218 219 // Make sure the selection buttons are properly enabled, 220 // since the clear method doesn't seem to notify the listener. 221 enableSelectionButtons(); 222 } 223 224 225 /** 226 * Returns the ClassPath currently represented in this panel. 227 */ 228 public ClassPath getClassPath() 229 { 230 int size = listModel.size(); 231 if (size == 0) 232 { 233 return null; 234 } 235 236 ClassPath classPath = new ClassPath(); 237 for (int index = 0; index < size; index++) 238 { 239 classPath.add((ClassPathEntry)listModel.get(index)); 240 } 241 242 return classPath; 243 } 244 245 246 /** 247 * Converts the given array of File objects into a corresponding array of 248 * ClassPathEntry objects. 249 */ 250 private ClassPathEntry[] classPathEntries(File[] files, boolean isOutput) 251 { 252 ClassPathEntry[] entries = new ClassPathEntry[files.length]; 253 for (int index = 0; index < entries.length; index++) 254 { 255 entries[index] = new ClassPathEntry(files[index], isOutput); 256 } 257 return entries; 258 } 259 260 261 /** 262 * Sets up the filter dialog with the filters from the specified class path 263 * entry. 264 */ 265 private void getFiltersFrom(int index) 266 { 267 ClassPathEntry firstEntry = (ClassPathEntry)listModel.get(index); 268 269 filterDialog.setFilter(firstEntry.getFilter()); 270 filterDialog.setJarFilter(firstEntry.getJarFilter()); 271 filterDialog.setWarFilter(firstEntry.getWarFilter()); 272 filterDialog.setEarFilter(firstEntry.getEarFilter()); 273 filterDialog.setZipFilter(firstEntry.getZipFilter()); 274 } 275 276 277 /** 278 * Applies the entered filter to the specified class path entries. 279 * Any previously set filters are discarded. 280 */ 281 private void setFiltersAt(int[] indices) 282 { 283 for (int index = indices.length - 1; index >= 0; index--) 284 { 285 ClassPathEntry entry = (ClassPathEntry)listModel.get(indices[index]); 286 entry.setFilter(filterDialog.getFilter()); 287 entry.setJarFilter(filterDialog.getJarFilter()); 288 entry.setWarFilter(filterDialog.getWarFilter()); 289 entry.setEarFilter(filterDialog.getEarFilter()); 290 entry.setZipFilter(filterDialog.getZipFilter()); 291 } 292 293 // Make sure they are selected and thus repainted. 294 list.setSelectedIndices(indices); 295 } 296 297 298 /** 299 * Attaches the tool tip from the GUI resources that corresponds to the 300 * given key, to the given component. 301 */ 302 private static JComponent tip(JComponent component, String messageKey) 303 { 304 component.setToolTipText(msg(messageKey)); 305 306 return component; 307 } 308 309 310 /** 311 * Returns the message from the GUI resources that corresponds to the given 312 * key. 313 */ 314 private static String msg(String messageKey) 315 { 316 return GUIResources.getMessage(messageKey); 317 } 318 319 320 /** 321 * This ListCellRenderer renders ClassPathEntry objects. 322 */ 323 private class MyListCellRenderer implements ListCellRenderer 324 { 325 private static final String ARROW_IMAGE_FILE = "arrow.gif"; 326 327 private final JPanel cellPanel = new JPanel(new GridBagLayout()); 328 private final JLabel iconLabel = new JLabel("", JLabel.RIGHT); 329 private final JLabel jarNameLabel = new JLabel("", JLabel.RIGHT); 330 private final JLabel filterLabel = new JLabel("", JLabel.RIGHT); 331 332 private final Icon arrowIcon; 333 334 335 public MyListCellRenderer() 336 { 337 GridBagConstraints jarNameLabelConstraints = new GridBagConstraints(); 338 jarNameLabelConstraints.anchor = GridBagConstraints.WEST; 339 jarNameLabelConstraints.insets = new Insets(1, 2, 1, 2); 340 341 GridBagConstraints filterLabelConstraints = new GridBagConstraints(); 342 filterLabelConstraints.gridwidth = GridBagConstraints.REMAINDER; 343 filterLabelConstraints.fill = GridBagConstraints.HORIZONTAL; 344 filterLabelConstraints.weightx = 1.0; 345 filterLabelConstraints.anchor = GridBagConstraints.EAST; 346 filterLabelConstraints.insets = jarNameLabelConstraints.insets; 347 348 arrowIcon = new ImageIcon(Toolkit.getDefaultToolkit().getImage(this.getClass().getResource(ARROW_IMAGE_FILE))); 349 350 cellPanel.add(iconLabel, jarNameLabelConstraints); 351 cellPanel.add(jarNameLabel, jarNameLabelConstraints); 352 cellPanel.add(filterLabel, filterLabelConstraints); 353 } 354 355 356 // Implementations for ListCellRenderer. 357 358 public Component getListCellRendererComponent(JList list, 359 Object value, 360 int index, 361 boolean isSelected, 362 boolean cellHasFocus) 363 { 364 ClassPathEntry entry = (ClassPathEntry)value; 365 366 // Prepend an arrow to the output entries. 367 if (inputAndOutput && entry.isOutput()) 368 { 369 iconLabel.setIcon(arrowIcon); 370 } 371 else 372 { 373 iconLabel.setIcon(null); 374 } 375 376 // Set the entry name text. 377 jarNameLabel.setText(entry.getName()); 378 379 // Set the filter text. 380 StringBuffer filter = null; 381 filter = appendFilter(filter, entry.getZipFilter()); 382 filter = appendFilter(filter, entry.getEarFilter()); 383 filter = appendFilter(filter, entry.getWarFilter()); 384 filter = appendFilter(filter, entry.getJarFilter()); 385 filter = appendFilter(filter, entry.getFilter()); 386 387 if (filter != null) 388 { 389 filter.append(')'); 390 } 391 392 filterLabel.setText(filter != null ? filter.toString() : ""); 393 394 // Set the colors. 395 if (isSelected) 396 { 397 cellPanel.setBackground(list.getSelectionBackground()); 398 jarNameLabel.setForeground(list.getSelectionForeground()); 399 filterLabel.setForeground(list.getSelectionForeground()); 400 } 401 else 402 { 403 cellPanel.setBackground(list.getBackground()); 404 jarNameLabel.setForeground(list.getForeground()); 405 filterLabel.setForeground(list.getForeground()); 406 } 407 408 // Make the font color red if this is an input file that can't be read. 409 if (!(inputAndOutput && entry.isOutput()) && 410 !entry.getFile().canRead()) 411 { 412 jarNameLabel.setForeground(Color.red); 413 } 414 415 cellPanel.setOpaque(true); 416 417 return cellPanel; 418 } 419 420 421 private StringBuffer appendFilter(StringBuffer filter, List additionalFilter) 422 { 423 if (filter != null) 424 { 425 filter.append(';'); 426 } 427 428 if (additionalFilter != null) 429 { 430 if (filter == null) 431 { 432 filter = new StringBuffer().append('('); 433 } 434 435 filter.append(ListUtil.commaSeparatedString(additionalFilter, true)); 436 } 437 438 return filter; 439 } 440 } 441} 442