001: /*
002: * IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved.
003: *
004: * http://izpack.org/
005: * http://izpack.codehaus.org/
006: *
007: * Licensed under the Apache License, Version 2.0 (the "License");
008: * you may not use this file except in compliance with the License.
009: * You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: */
019: package com.izforge.izpack.panels;
020:
021: import java.awt.Component;
022: import java.awt.Dimension;
023: import java.awt.GridBagConstraints;
024: import java.awt.GridBagLayout;
025: import java.awt.Insets;
026: import java.io.UnsupportedEncodingException;
027: import java.net.URLDecoder;
028: import java.util.ArrayList;
029: import java.util.Collections;
030: import java.util.Comparator;
031: import java.util.HashMap;
032: import java.util.HashSet;
033: import java.util.Iterator;
034: import java.util.List;
035: import java.util.Set;
036:
037: import javax.swing.BorderFactory;
038: import javax.swing.ButtonGroup;
039: import javax.swing.JRadioButton;
040: import javax.swing.JScrollPane;
041: import javax.swing.JTable;
042: import javax.swing.JTextPane;
043: import javax.swing.ListSelectionModel;
044: import javax.swing.border.EmptyBorder;
045: import javax.swing.border.TitledBorder;
046: import javax.swing.event.ListSelectionEvent;
047: import javax.swing.event.ListSelectionListener;
048: import javax.swing.table.DefaultTableModel;
049: import javax.swing.table.TableCellRenderer;
050: import javax.swing.table.TableColumnModel;
051: import javax.swing.table.TableModel;
052:
053: import net.n3.nanoxml.XMLElement;
054:
055: import com.izforge.izpack.Pack;
056: import com.izforge.izpack.installer.InstallData;
057: import com.izforge.izpack.installer.InstallerFrame;
058: import com.izforge.izpack.installer.IzPanel;
059: import com.izforge.izpack.util.AbstractUIHandler;
060: import com.izforge.izpack.util.Debug;
061: import com.izforge.izpack.util.OsConstraint;
062: import javax.swing.SwingConstants;
063: import javax.swing.table.DefaultTableCellRenderer;
064:
065: /**
066: * A panel which displays the available installGroups found on the packs to
067: * allow the user to select a subset of the packs based on the pack
068: * installGroups attribute. This panel will be skipped if there are no
069: * pack elements with an installGroups attribute.
070: *
071: * @author Scott.Stark@jboss.org
072: * @version $Revision: 1.1.1.1 $
073: */
074: public class InstallationGroupPanel extends IzPanel implements
075: ListSelectionListener {
076: private static final long serialVersionUID = 1L;
077:
078: /** HashMap<String, Pack> of the InstallData.availablePacks */
079: private HashMap<String, Pack> packsByName;
080: private TableModel groupTableModel;
081: private JTextPane descriptionField;
082: private JScrollPane groupScrollPane;
083: private JTable groupsTable;
084: private GroupData[] rows;
085: private int selectedGroup = -1;
086:
087: public InstallationGroupPanel(InstallerFrame parent,
088: InstallData idata) {
089: super (parent, idata);
090: buildLayout();
091: }
092:
093: /**
094: * If there are no packs with an installGroups attribute, this panel is
095: * skipped. Otherwise, the unique installGroups are displayed in a table.
096: */
097: public void panelActivate() {
098: // Set/restore availablePacks from allPacks; consider OS constraints
099: idata.availablePacks = new ArrayList();
100: for (Pack p : idata.allPacks) {
101: if (OsConstraint.oneMatchesCurrentSystem(p.osConstraints)) {
102: idata.availablePacks.add(p);
103: }
104: }
105:
106: Debug
107: .trace("InstallationGroupPanel.panelActivate, selectedGroup="
108: + selectedGroup);
109: // If there are no groups, skip this panel
110: HashMap installGroups = getInstallGroups(idata);
111: if (installGroups.size() == 0) {
112: super .askQuestion("Skip InstallGroup selection",
113: "Skip InstallGroup selection",
114: AbstractUIHandler.CHOICES_YES_NO);
115: parent.skipPanel();
116: return;
117: }
118:
119: // Build the table model from the unique groups
120: groupTableModel = getModel(installGroups);
121: groupsTable.setModel(groupTableModel);
122: TableColumnModel tcm = groupsTable.getColumnModel();
123:
124: // renders the radio buttons and adjusts their state
125: TableCellRenderer radioButtonRenderer = new TableCellRenderer() {
126: public Component getTableCellRendererComponent(
127: JTable table, Object value, boolean isSelected,
128: boolean hasFocus, int row, int column) {
129: if (value == null)
130: return null;
131:
132: int selectedRow = table.getSelectedRow();
133:
134: if (selectedRow != -1) {
135: JRadioButton selectedButton = (JRadioButton) table
136: .getValueAt(selectedRow, 0);
137: if (!selectedButton.isSelected()) {
138: selectedButton.doClick();
139: }
140: }
141:
142: JRadioButton button = (JRadioButton) value;
143: button.setForeground(isSelected ? table
144: .getSelectionForeground() : table
145: .getForeground());
146: button.setBackground(isSelected ? table
147: .getSelectionBackground() : table
148: .getBackground());
149:
150: // long millis = System.currentTimeMillis() % 100000;
151: // System.out.printf("%1$5d: row: %2$d; isSelected: %3$5b; buttonSelected: %4$5b; selectedRow: %5$d%n", millis, row, isSelected, button.isSelected(), selectedRow);
152:
153: return button;
154: }
155: };
156: tcm.getColumn(0).setCellRenderer(radioButtonRenderer);
157:
158: DefaultTableCellRenderer renderer = new DefaultTableCellRenderer();
159: renderer.setHorizontalAlignment(SwingConstants.RIGHT);
160: tcm.getColumn(1).setCellRenderer(renderer);
161:
162: //groupsTable.setColumnSelectionAllowed(false);
163: //groupsTable.setRowSelectionAllowed(true);
164: groupsTable
165: .setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
166: groupsTable.getSelectionModel().addListSelectionListener(this );
167: groupsTable.setBorder(BorderFactory.createEmptyBorder(0, 2, 0,
168: 2));
169: groupsTable.setIntercellSpacing(new Dimension(0, 0));
170: groupsTable.setShowGrid(false);
171: if (selectedGroup >= 0) {
172: groupsTable.getSelectionModel().setSelectionInterval(
173: selectedGroup, selectedGroup);
174: descriptionField.setText(rows[selectedGroup].description);
175: } else {
176: descriptionField.setText(rows[0].description);
177: }
178: }
179:
180: /**
181: * Remove all packs from the InstallData availablePacks and selectedPacks
182: * that do not list the selected installation group. Packs without any
183: * installGroups are always included.
184: */
185: public void panelDeactivate() {
186:
187: Debug
188: .trace("InstallationGroupPanel.panelDeactivate, selectedGroup="
189: + selectedGroup);
190: if (selectedGroup >= 0) {
191: removeUnusedPacks();
192: GroupData group = this .rows[selectedGroup];
193: idata.setVariable("INSTALL_GROUP", group.name);
194: Debug.trace("Added variable INSTALL_GROUP=" + group.name);
195: }
196: }
197:
198: /**
199: * There needs to be a valid selectedGroup to go to the next panel
200: * @return true if selectedGroup >= 0, false otherwise
201: */
202: public boolean isValidated() {
203: Debug
204: .trace("InstallationGroupPanel.isValidated, selectedGroup="
205: + selectedGroup);
206: return selectedGroup >= 0;
207: }
208:
209: /**
210: * Update the current selected install group index.
211: * @param e
212: */
213: public void valueChanged(ListSelectionEvent e) {
214: Debug.trace("valueChanged: " + e);
215: if (!e.getValueIsAdjusting()) {
216: ListSelectionModel lsm = (ListSelectionModel) e.getSource();
217: if (lsm.isSelectionEmpty()) {
218: descriptionField.setText("");
219: } else {
220: selectedGroup = lsm.getMinSelectionIndex();
221: if (selectedGroup >= 0) {
222: GroupData data = rows[selectedGroup];
223: descriptionField.setText(data.description);
224: ((JRadioButton) groupTableModel.getValueAt(
225: selectedGroup, 0)).setSelected(true);
226: }
227: Debug.trace("selectedGroup set to: " + selectedGroup);
228: }
229: }
230: }
231:
232: /* Add the installation group to pack mappings
233: * @see com.izforge.izpack.installer.IzPanel#makeXMLData(net.n3.nanoxml.XMLElement)
234: */
235: public void makeXMLData(XMLElement panelRoot) {
236: InstallationGroupPanelAutomationHelper helper = new InstallationGroupPanelAutomationHelper();
237: idata.setAttribute("GroupData", rows);
238: idata.setAttribute("packsByName", packsByName);
239: helper.makeXMLData(idata, panelRoot);
240: }
241:
242: /**
243: * Create the panel ui.
244: */
245: protected void buildLayout() {
246: GridBagConstraints gridBagConstraints;
247:
248: descriptionField = new JTextPane();
249: groupScrollPane = new JScrollPane();
250: groupsTable = new JTable();
251:
252: setLayout(new GridBagLayout());
253:
254: descriptionField.setMargin(new Insets(2, 2, 2, 2));
255: descriptionField.setAlignmentX(LEFT_ALIGNMENT);
256: descriptionField.setCaretPosition(0);
257: descriptionField.setEditable(false);
258: descriptionField.setOpaque(false);
259: descriptionField
260: .setText("<b>Install group description text</b>");
261: descriptionField.setContentType("text/html");
262: descriptionField.setBorder(new TitledBorder(idata.langpack
263: .getString("PacksPanel.description")));
264: gridBagConstraints = new java.awt.GridBagConstraints();
265: gridBagConstraints.gridy = 2;
266: gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
267: gridBagConstraints.weightx = 1.0;
268: gridBagConstraints.weighty = 0.3;
269: add(descriptionField, gridBagConstraints);
270:
271: groupScrollPane.setBorder(new EmptyBorder(1, 1, 1, 1));
272: groupScrollPane.setViewportView(groupsTable);
273:
274: gridBagConstraints = new java.awt.GridBagConstraints();
275: gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
276: gridBagConstraints.weightx = 1.0;
277: gridBagConstraints.weighty = 1.0;
278: add(groupScrollPane, gridBagConstraints);
279: }
280:
281: protected void removeUnusedPacks() {
282: GroupData data = rows[selectedGroup];
283: Debug
284: .trace("InstallationGroupPanel.removeUnusedPacks, GroupData="
285: + data.name);
286:
287: // Now remove the packs not in groupPackNames
288: Iterator iter = idata.availablePacks.iterator();
289: while (iter.hasNext()) {
290: Pack p = (Pack) iter.next();
291:
292: //reverse dependencies must be reset in case the user is going
293: //back and forth between the group selection panel and the packs selection panel
294: p.revDependencies = null;
295:
296: if (!data.packNames.contains(p.name)) {
297: iter.remove();
298: Debug.trace("Removed AvailablePack: " + p.name);
299: }
300: }
301:
302: idata.selectedPacks.clear();
303: if (!"no".equals(idata
304: .getVariable("InstallationGroupPanel.selectPacks"))) {
305: idata.selectedPacks.addAll(idata.availablePacks);
306: } else {
307: for (Object availablePack : idata.availablePacks) {
308: Pack p = (Pack) availablePack;
309: if (p.preselected) {
310: idata.selectedPacks.add(p);
311: }
312: }
313: }
314: }
315:
316: protected void addDependents(Pack p,
317: HashMap<String, Pack> packsByName, GroupData data) {
318: data.packNames.add(p.name);
319: data.size += p.nbytes;
320: Debug.trace("addDependents, added pack: " + p.name);
321: if (p.dependencies == null || p.dependencies.size() == 0)
322: return;
323:
324: Iterator<String> iter = p.dependencies.iterator();
325: Debug.trace(p.name + " dependencies: " + p.dependencies);
326: while (iter.hasNext()) {
327: String dependent = iter.next();
328: if (!data.packNames.contains(dependent)) {
329: Debug.trace("Need dependent: " + dependent);
330: Pack dependentPack = packsByName.get(dependent);
331: addDependents(dependentPack, packsByName, data);
332: }
333: }
334: }
335:
336: /**
337: * Build the set of unique installGroups data. The GroupData description
338: * is taken from the InstallationGroupPanel.description.[name] property
339: * where [name] is the installGroup name. The GroupData size is built
340: * from the Pack.nbytes sum.
341: *
342: * @param idata - the panel install data
343: * @return HashMap<String, GroupData> of unique install group names
344: */
345: protected HashMap getInstallGroups(InstallData idata) {
346: /* First create a packsByName<String, Pack> of all packs and identify
347: the unique install group names.
348: */
349: packsByName = new HashMap<String, Pack>();
350: HashMap installGroups = new HashMap();
351: for (int n = 0; n < idata.availablePacks.size(); n++) {
352: Pack p = (Pack) idata.availablePacks.get(n);
353: packsByName.put(p.name, p);
354: Set<String> groups = p.installGroups;
355: Iterator<String> iter = groups.iterator();
356: Debug.trace("Pack: " + p.name + ", installGroups: "
357: + groups);
358: while (iter.hasNext()) {
359: String group = iter.next();
360: GroupData data = (GroupData) installGroups.get(group);
361: if (data == null) {
362: String description = getGroupDescription(group);
363: String sortKey = getGroupSortKey(group);
364: data = new GroupData(group, description, sortKey);
365: installGroups.put(group, data);
366: }
367: }
368: }
369: Debug.trace("Found installGroups: " + installGroups.keySet());
370:
371: /* Build up a set of the packs to include in the installation by finding
372: all packs in the selected group, and then include their dependencies.
373: */
374: Iterator gditer = installGroups.values().iterator();
375: while (gditer.hasNext()) {
376: GroupData data = (GroupData) gditer.next();
377: Debug.trace("Adding dependents for: " + data.name);
378: Iterator iter = idata.availablePacks.iterator();
379: while (iter.hasNext()) {
380: Pack p = (Pack) iter.next();
381: Set<String> groups = p.installGroups;
382: if (groups.size() == 0 || groups.contains(data.name)) {
383: // The pack may have already been added while traversing dependencies
384: if (!data.packNames.contains(p.name))
385: addDependents(p, packsByName, data);
386: }
387: }
388: Debug.trace("Completed dependents for: " + data);
389: if (Debug.tracing())
390: Debug.trace(data);
391: }
392:
393: return installGroups;
394: }
395:
396: /**
397: * Look for a key = InstallationGroupPanel.description.[group] entry:
398: * first using idata.langpack.getString(key+".html")
399: * next using idata.langpack.getString(key)
400: * next using idata.getVariable(key)
401: * lastly, defaulting to group + " installation"
402: * @param group - the installation group name
403: * @return the group description
404: */
405: protected String getGroupDescription(String group) {
406: String description = null;
407: String key = "InstallationGroupPanel.description." + group;
408: if (idata.langpack != null) {
409: String htmlKey = key + ".html";
410: String html = idata.langpack.getString(htmlKey);
411: // This will equal the key if there is no entry
412: if (htmlKey.equalsIgnoreCase(html))
413: description = idata.langpack.getString(key);
414: else
415: description = html;
416: }
417: if (description == null || key.equalsIgnoreCase(description))
418: description = idata.getVariable(key);
419: if (description == null)
420: description = group + " installation";
421: try {
422: description = URLDecoder.decode(description, "UTF-8");
423: } catch (UnsupportedEncodingException e) {
424: emitWarning("Failed to convert description", e.getMessage());
425: }
426:
427: return description;
428: }
429:
430: /**
431: * Look for a key = InstallationGroupPanel.sortKey.[group] entry:
432: * by using idata.getVariable(key)
433: * if this variable is not defined, defaults to group
434: * @param group - the installation group name
435: * @return the group sortkey
436: */
437: protected String getGroupSortKey(String group) {
438: String key = "InstallationGroupPanel.sortKey." + group;
439: String sortKey = idata.getVariable(key);
440: if (sortKey == null)
441: sortKey = group;
442: try {
443: sortKey = URLDecoder.decode(sortKey, "UTF-8");
444: } catch (UnsupportedEncodingException e) {
445: emitWarning("Failed to convert sortKey", e.getMessage());
446: }
447:
448: return sortKey;
449: }
450:
451: /**
452: * Look for a key = InstallationGroupPanel.group.[group] entry:
453: * first using idata.langpackgetString(key+".html")
454: * next using idata.langpack.getString(key)
455: * next using idata.getVariable(key)
456: * lastly, defaulting to group
457: * @param group - the installation group name
458: * @return the localized group name
459: */
460: protected String getLocalizedGroupName(String group) {
461: String gname = null;
462: String key = "InstallationGroupPanel.group." + group;
463: if (idata.langpack != null) {
464: String htmlKey = key + ".html";
465: String html = idata.langpack.getString(htmlKey);
466: // This will equal the key if there is no entry
467: if (htmlKey.equalsIgnoreCase(html))
468: gname = idata.langpack.getString(key);
469: else
470: gname = html;
471: }
472: if (gname == null || key.equalsIgnoreCase(gname))
473: gname = idata.getVariable(key);
474: if (gname == null)
475: gname = group;
476: try {
477: gname = URLDecoder.decode(gname, "UTF-8");
478: } catch (UnsupportedEncodingException e) {
479: emitWarning("Failed to convert localized group name", e
480: .getMessage());
481: }
482:
483: return gname;
484: }
485:
486: protected TableModel getModel(HashMap groupData) {
487: String c1 = parent.langpack
488: .getString("InstallationGroupPanel.colNameSelected");
489: //String c2 = parent.langpack.getString("InstallationGroupPanel.colNameInstallType");
490: String c3 = parent.langpack
491: .getString("InstallationGroupPanel.colNameSize");
492: String[] columns = { c1, c3 };
493: DefaultTableModel model = new DefaultTableModel(columns, 0) {
494: public boolean isCellEditable(int row, int column) {
495: return false;
496: }
497: };
498: rows = new GroupData[groupData.size()];
499: // The name of the group to select if there is no current selection
500: String defaultGroup = idata
501: .getVariable("InstallationGroupPanel.defaultGroup");
502: Debug.trace("InstallationGroupPanel.defaultGroup="
503: + defaultGroup + ", selectedGroup=" + selectedGroup);
504: List values = new ArrayList(groupData.values());
505: Collections.sort(values, new Comparator() {
506: public int compare(Object o1, Object o2) {
507: GroupData g1 = (GroupData) o1;
508: GroupData g2 = (GroupData) o2;
509:
510: if (g1.sortKey == null || g2.sortKey == null) {
511: return 0;
512: }
513:
514: return g1.sortKey.compareTo(g2.sortKey);
515: }
516: });
517:
518: Iterator iter = values.iterator();
519: ButtonGroup buttonGroup = new ButtonGroup();
520: boolean madeSelection = false;
521: int count = 0;
522: while (iter.hasNext()) {
523: GroupData gd = (GroupData) iter.next();
524: rows[count] = gd;
525: Debug.trace("Creating button#" + count + ", group="
526: + gd.name);
527: JRadioButton btn = new JRadioButton(
528: getLocalizedGroupName(gd.name));
529: if (selectedGroup == count) {
530: btn.setSelected(true);
531: Debug.trace("Selected button#" + count);
532: } else if (selectedGroup < 0 && !madeSelection) {
533: if (defaultGroup != null) {
534: if (defaultGroup.equals(gd.name))
535: madeSelection = true;
536: } else if (count == 0)
537: madeSelection = true;
538: if (madeSelection) {
539: btn.setSelected(true);
540: Debug.trace("Selected button#" + count);
541: selectedGroup = count;
542: }
543: } else {
544: btn.setSelected(false);
545: }
546: buttonGroup.add(btn);
547: String sizeText = gd.getSizeString();
548: //Object[] data = { btn, gd.description, sizeText};
549: Object[] data = { btn, sizeText };
550: model.addRow(data);
551: count++;
552: }
553: return model;
554: }
555:
556: protected static class GroupData {
557: static final long ONEK = 1024;
558: static final long ONEM = 1024 * 1024;
559: static final long ONEG = 1024 * 1024 * 1024;
560:
561: String name;
562: String description;
563: String sortKey;
564: long size;
565: HashSet<String> packNames = new HashSet<String>();
566:
567: GroupData(String name, String description, String sortKey) {
568: this .name = name;
569: this .description = description;
570: this .sortKey = sortKey;
571: }
572:
573: String getSizeString() {
574: String s;
575: if (size < ONEK) {
576: s = size + " bytes";
577: } else if (size < ONEM) {
578: s = size / ONEK + " KB";
579: } else if (size < ONEG) {
580: s = size / ONEM + " MB";
581: } else {
582: s = size / ONEG + " GB";
583: }
584: return s;
585: }
586:
587: public String toString() {
588: StringBuffer tmp = new StringBuffer("GroupData(");
589: tmp.append(name);
590: tmp.append("){description=");
591: tmp.append(description);
592: tmp.append(", sortKey=");
593: tmp.append(sortKey);
594: tmp.append(", size=");
595: tmp.append(size);
596: tmp.append(", sizeString=");
597: tmp.append(getSizeString());
598: tmp.append(", packNames=");
599: tmp.append(packNames);
600: tmp.append("}");
601: return tmp.toString();
602: }
603: }
604:
605: }
|