001: /*
002: * IzPack - Copyright 2001-2008 Julien Ponge, All Rights Reserved.
003: *
004: * http://izpack.org/
005: * http://izpack.codehaus.org/
006: *
007: * Copyright 2002 Marcus Wolschon
008: * Copyright 2002 Jan Blok
009: * Copyright 2004 Gaganis Giorgos
010: * Copyright 2006,2007 Dennis Reil
011: *
012: * Licensed under the Apache License, Version 2.0 (the "License");
013: * you may not use this file except in compliance with the License.
014: * You may obtain a copy of the License at
015: *
016: * http://www.apache.org/licenses/LICENSE-2.0
017: *
018: * Unless required by applicable law or agreed to in writing, software
019: * distributed under the License is distributed on an "AS IS" BASIS,
020: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
021: * See the License for the specific language governing permissions and
022: * limitations under the License.
023: */
024:
025: package com.izforge.izpack.panels;
026:
027: import java.io.File;
028: import java.io.FileInputStream;
029: import java.io.FileNotFoundException;
030: import java.io.IOException;
031: import java.io.ObjectInputStream;
032: import java.util.ArrayList;
033: import java.util.HashMap;
034: import java.util.Iterator;
035: import java.util.List;
036: import java.util.Map;
037: import java.util.Properties;
038:
039: import javax.swing.table.AbstractTableModel;
040:
041: import com.izforge.izpack.LocaleDatabase;
042: import com.izforge.izpack.Pack;
043: import com.izforge.izpack.installer.AutomatedInstallData;
044: import com.izforge.izpack.installer.InstallData;
045: import com.izforge.izpack.rules.RulesEngine;
046: import com.izforge.izpack.util.Debug;
047:
048: /**
049: * User: Gaganis Giorgos Date: Sep 17, 2004 Time: 8:33:21 AM
050: */
051: class PacksModel extends AbstractTableModel {
052:
053: /**
054: *
055: */
056: private static final long serialVersionUID = 3258128076746733110L;
057:
058: private static final String INITAL_PACKSELECTION = "initial.pack.selection";
059:
060: private List packs;
061:
062: private List packsToInstall;
063:
064: private Map installedpacks;
065: private boolean modifyinstallation;
066:
067: private PacksPanelInterface panel;
068:
069: private LocaleDatabase langpack;
070:
071: // This is used to represent the status of the checkbox
072: private int[] checkValues;
073:
074: // Map to hold the object name relationship
075: Map<String, Pack> namesObj;
076:
077: // Map to hold the object name relationship
078: Map<String, Integer> namesPos;
079:
080: // reference to the RulesEngine for validating conditions
081: private RulesEngine rules;
082:
083: // reference to the current variables, needed for condition validation
084: private Properties variables;
085:
086: public PacksModel(PacksPanelInterface panel, InstallData idata,
087: RulesEngine rules) {
088: modifyinstallation = Boolean.valueOf(idata
089: .getVariable(InstallData.MODIFY_INSTALLATION));
090: this .installedpacks = new HashMap();
091:
092: if (modifyinstallation) {
093: // installation shall be modified
094: // load installation information
095:
096: try {
097: FileInputStream fin = new FileInputStream(
098: new File(
099: idata.getInstallPath()
100: + File.separator
101: + AutomatedInstallData.INSTALLATION_INFORMATION));
102: ObjectInputStream oin = new ObjectInputStream(fin);
103: List packsinstalled = (List) oin.readObject();
104: for (Object aPacksinstalled : packsinstalled) {
105: Pack installedpack = (Pack) aPacksinstalled;
106: if ((installedpack.id != null)
107: && (installedpack.id.length() > 0)) {
108: this .installedpacks.put(installedpack.id,
109: installedpack);
110: } else {
111: this .installedpacks.put(installedpack.name,
112: installedpack);
113: }
114: }
115: this .removeAlreadyInstalledPacks(idata.selectedPacks);
116: Debug.trace("Found " + packsinstalled.size()
117: + " installed packs");
118:
119: Properties variables = (Properties) oin.readObject();
120:
121: Iterator iter = variables.keySet().iterator();
122: while (iter.hasNext()) {
123: Object key = iter.next();
124: idata.setVariable((String) key, (String) variables
125: .get(key));
126: }
127: fin.close();
128: } catch (FileNotFoundException e) {
129: // TODO Auto-generated catch block
130: e.printStackTrace();
131: } catch (IOException e) {
132: // TODO Auto-generated catch block
133: e.printStackTrace();
134: } catch (ClassNotFoundException e) {
135: // TODO Auto-generated catch block
136: e.printStackTrace();
137: }
138: }
139: this .rules = rules;
140: this .packs = idata.availablePacks;
141: this .packsToInstall = idata.selectedPacks;
142: this .panel = panel;
143: this .variables = idata.getVariables();
144: this .variables.setProperty(INITAL_PACKSELECTION, Boolean
145: .toString(true));
146: langpack = panel.getLangpack();
147: checkValues = new int[packs.size()];
148: reverseDeps();
149: initvalues();
150: this .updateConditions(true);
151: refreshPacksToInstall();
152: this .variables.setProperty(INITAL_PACKSELECTION, Boolean
153: .toString(false));
154: }
155:
156: private void removeAlreadyInstalledPacks(List selectedpacks) {
157: List<Pack> removepacks = new ArrayList<Pack>();
158:
159: for (Object selectedpack1 : selectedpacks) {
160: Pack selectedpack = (Pack) selectedpack1;
161: String key = "";
162: if ((selectedpack.id != null)
163: && (selectedpack.id.length() > 0)) {
164: key = selectedpack.id;
165: } else {
166: key = selectedpack.name;
167: }
168: if (installedpacks.containsKey(key)) {
169: // pack is already installed, remove it
170: removepacks.add(selectedpack);
171: }
172: }
173: for (Pack removepack : removepacks) {
174: selectedpacks.remove(removepack);
175: }
176: }
177:
178: public void updateConditions() {
179: this .updateConditions(false);
180: }
181:
182: private void updateConditions(boolean initial) {
183: boolean changes = true;
184:
185: while (changes) {
186: changes = false;
187: // look for packages,
188: for (Object pack1 : packs) {
189: Pack pack = (Pack) pack1;
190: int pos = getPos(pack.name);
191: Debug.trace("Conditions fulfilled for: " + pack.name
192: + "?");
193: if (!this .rules.canInstallPack(pack.id, this .variables)) {
194: Debug.trace("no");
195: if (this .rules.canInstallPackOptional(pack.id,
196: this .variables)) {
197: Debug.trace("optional");
198: Debug.trace(pack.id
199: + " can be installed optionally.");
200: if (initial) {
201: if (checkValues[pos] != 0) {
202: checkValues[pos] = 0;
203: changes = true;
204: // let the process start from the beginning
205: break;
206: }
207: } else {
208: // just do nothing
209: }
210: } else {
211: Debug.trace(pack.id + " can not be installed.");
212: if (checkValues[pos] != -2) {
213: checkValues[pos] = -2;
214: changes = true;
215: // let the process start from the beginning
216: break;
217: }
218: }
219: }
220: }
221: refreshPacksToInstall();
222: }
223: }
224:
225: /**
226: * Creates the reverse dependency graph
227: */
228: private void reverseDeps() {
229: // name to pack map
230: namesObj = new HashMap<String, Pack>();
231: for (Object pack2 : packs) {
232: Pack pack = (Pack) pack2;
233: namesObj.put(pack.name, pack);
234: }
235: // process each pack
236: for (Object pack1 : packs) {
237: Pack pack = (Pack) pack1;
238: List<String> deps = pack.dependencies;
239: for (int j = 0; deps != null && j < deps.size(); j++) {
240: String name = deps.get(j);
241: Pack parent = namesObj.get(name);
242: parent.addRevDep(pack.name);
243: }
244: }
245:
246: }
247:
248: private void initvalues() {
249: // name to pack position map
250: namesPos = new HashMap<String, Integer>();
251: for (int i = 0; i < packs.size(); i++) {
252: Pack pack = (Pack) packs.get(i);
253: namesPos.put(pack.name, i);
254: }
255: // Init to the first values
256: for (int i = 0; i < packs.size(); i++) {
257: Pack pack = (Pack) packs.get(i);
258: if (packsToInstall.contains(pack))
259: checkValues[i] = 1;
260: }
261:
262: // Check out and disable the ones that are excluded by non fullfiled
263: // deps
264: for (int i = 0; i < packs.size(); i++) {
265: Pack pack = (Pack) packs.get(i);
266: if (checkValues[i] == 0) {
267: List<String> deps = pack.revDependencies;
268: for (int j = 0; deps != null && j < deps.size(); j++) {
269: String name = deps.get(j);
270: int pos = getPos(name);
271: checkValues[pos] = -2;
272: }
273: }
274: // for mutual exclusion, uncheck uncompatible packs too
275: // (if available in the current installGroup)
276:
277: if (checkValues[i] > 0 && pack.excludeGroup != null) {
278: for (int q = 0; q < packs.size(); q++) {
279: if (q != i) {
280: Pack otherpack = (Pack) packs.get(q);
281: if (pack.excludeGroup
282: .equals(otherpack.excludeGroup)) {
283: if (checkValues[q] == 1)
284: checkValues[q] = 0;
285: }
286: }
287: }
288: }
289: }
290: // The required ones must propagate their required status to all the
291: // ones
292: // that they depend on
293: for (Object pack1 : packs) {
294: Pack pack = (Pack) pack1;
295:
296: if (pack.required) {
297: propRequirement(pack.name);
298: }
299: }
300:
301: refreshPacksToInstall();
302: }
303:
304: private void propRequirement(String name) {
305:
306: final int pos = getPos(name);
307: checkValues[pos] = -1;
308: List<String> deps = ((Pack) packs.get(pos)).dependencies;
309: for (int i = 0; deps != null && i < deps.size(); i++) {
310: String s = deps.get(i);
311: propRequirement(s);
312: }
313:
314: }
315:
316: /**
317: * Given a map of names and Integer for position and a name it return the position of this name
318: * as an int
319: *
320: * @return position of the name
321: */
322: private int getPos(String name) {
323: return namesPos.get(name);
324: }
325:
326: /*
327: * @see TableModel#getRowCount()
328: */
329: public int getRowCount() {
330: return packs.size();
331: }
332:
333: /*
334: * @see TableModel#getColumnCount()
335: */
336: public int getColumnCount() {
337: return 3;
338: }
339:
340: /*
341: * @see TableModel#getColumnClass(int)
342: */
343: public Class getColumnClass(int columnIndex) {
344: switch (columnIndex) {
345: case 0:
346: return Integer.class;
347:
348: default:
349: return String.class;
350: }
351: }
352:
353: /*
354: * @see TableModel#isCellEditable(int, int)
355: */
356: public boolean isCellEditable(int rowIndex, int columnIndex) {
357: if (checkValues[rowIndex] < 0) {
358: return false;
359: } else
360: return columnIndex == 0;
361: }
362:
363: /*
364: * @see TableModel#getValueAt(int, int)
365: */
366: public Object getValueAt(int rowIndex, int columnIndex) {
367: Pack pack = (Pack) packs.get(rowIndex);
368: switch (columnIndex) {
369: case 0:
370:
371: return checkValues[rowIndex];
372:
373: case 1:
374:
375: if (langpack == null || pack.id == null
376: || pack.id.equals("")) {
377: return pack.name;
378: } else {
379: String tmp = langpack.getString(pack.id);
380: if (pack.id.equals(tmp))
381: return pack.name;
382: else
383: return tmp;
384: }
385:
386: case 2:
387: return Pack.toByteUnitsString(pack.nbytes);
388:
389: default:
390: return null;
391: }
392: }
393:
394: /*
395: * @see TableModel#setValueAt(Object, int, int)
396: */
397: public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
398: if (columnIndex == 0) {
399: if (aValue instanceof Integer) {
400: Pack pack = (Pack) packs.get(rowIndex);
401: boolean packadded = false;
402: if ((Integer) aValue == 1) {
403: packadded = true;
404: String packid = pack.id;
405: if (packid != null) {
406: if (this .rules.canInstallPack(packid,
407: this .variables)
408: || this .rules.canInstallPackOptional(
409: packid, this .variables)) {
410: if (pack.required) {
411: checkValues[rowIndex] = -1;
412: } else {
413: checkValues[rowIndex] = 1;
414: }
415: }
416: } else {
417: if (pack.required) {
418: checkValues[rowIndex] = -1;
419: } else {
420: checkValues[rowIndex] = 1;
421: }
422: }
423: } else {
424: packadded = false;
425: checkValues[rowIndex] = 0;
426: }
427: updateExcludes(rowIndex);
428: updateDeps();
429:
430: if (packadded) {
431: if (panel.getDebugger() != null) {
432: panel.getDebugger().packSelectionChanged(
433: "after adding pack " + pack.id);
434: }
435: // temporarily add pack to packstoinstall
436: this .packsToInstall.add(pack);
437: } else {
438: if (panel.getDebugger() != null) {
439: panel.getDebugger().packSelectionChanged(
440: "after removing pack " + pack.id);
441: }
442: // temporarily remove pack from packstoinstall
443: this .packsToInstall.remove(pack);
444: }
445: updateConditions();
446: if (packadded) {
447: // redo
448: this .packsToInstall.remove(pack);
449: } else {
450: // redo
451: this .packsToInstall.add(pack);
452: }
453: updateBytes();
454: fireTableDataChanged();
455: refreshPacksToInstall();
456: panel.showSpaceRequired();
457: }
458: }
459: }
460:
461: private void refreshPacksToInstall() {
462:
463: packsToInstall.clear();
464: for (int i = 0; i < packs.size(); i++) {
465: Pack pack = (Pack) packs.get(i);
466: String key = "";
467: if ((pack.id != null) && (pack.id.length() > 0)) {
468: key = pack.id;
469: } else {
470: key = pack.name;
471: }
472: if ((Math.abs(checkValues[i]) == 1)
473: && (!installedpacks.containsKey(key))) {
474: packsToInstall.add(pack);
475: }
476:
477: }
478:
479: for (int i = 0; i < packs.size(); i++) {
480: Pack pack = (Pack) packs.get(i);
481:
482: String key = "";
483: if ((pack.id != null) && (pack.id.length() > 0)) {
484: key = pack.id;
485: } else {
486: key = pack.name;
487: }
488: if (installedpacks.containsKey(key)) {
489: checkValues[i] = -3;
490: }
491: }
492: }
493:
494: /**
495: * This function updates the checkboxes after a change by disabling packs that cannot be
496: * installed anymore and enabling those that can after the change. This is accomplished by
497: * running a search that pinpoints the packs that must be disabled by a non-fullfiled
498: * dependency.
499: */
500: private void updateDeps() {
501: int[] statusArray = new int[packs.size()];
502: for (int i = 0; i < statusArray.length; i++) {
503: statusArray[i] = 0;
504: }
505: dfs(statusArray);
506: for (int i = 0; i < statusArray.length; i++) {
507: if (statusArray[i] == 0 && checkValues[i] < 0)
508: checkValues[i] += 2;
509: if (statusArray[i] == 1 && checkValues[i] >= 0)
510: checkValues[i] = -2;
511:
512: }
513: // The required ones must propagate their required status to all the
514: // ones
515: // that they depend on
516: for (Object pack1 : packs) {
517: Pack pack = (Pack) pack1;
518: if (pack.required) {
519: String packid = pack.id;
520: if (packid != null) {
521: if (!(!this .rules.canInstallPack(packid,
522: this .variables) && this .rules
523: .canInstallPackOptional(packid,
524: this .variables))) {
525: propRequirement(pack.name);
526: }
527: } else {
528: propRequirement(pack.name);
529: }
530: }
531: }
532:
533: }
534:
535: /*
536: * Sees which packs (if any) should be unchecked and updates checkValues
537: */
538: private void updateExcludes(int rowindex) {
539: int value = checkValues[rowindex];
540: Pack pack = (Pack) packs.get(rowindex);
541: if (value > 0 && pack.excludeGroup != null) {
542: for (int q = 0; q < packs.size(); q++) {
543: if (rowindex != q) {
544: Pack otherpack = (Pack) packs.get(q);
545: String name1 = otherpack.excludeGroup;
546: String name2 = pack.excludeGroup;
547: if (name2.equals(name1)) {
548: if (checkValues[q] == 1)
549: checkValues[q] = 0;
550: }
551: }
552: }
553: }
554: }
555:
556: private void updateBytes() {
557: long bytes = 0;
558: for (int q = 0; q < packs.size(); q++) {
559: if (Math.abs(checkValues[q]) == 1) {
560: Pack pack = (Pack) packs.get(q);
561: bytes += pack.nbytes;
562: }
563: }
564: panel.setBytes(bytes);
565: }
566:
567: /**
568: * We use a modified dfs graph search algorithm as described in: Thomas H. Cormen, Charles
569: * Leiserson, Ronald Rivest and Clifford Stein. Introduction to algorithms 2nd Edition
570: * 540-549,MIT Press, 2001
571: */
572: private int dfs(int[] status) {
573: for (int i = 0; i < packs.size(); i++) {
574: for (Object pack1 : packs) {
575: ((Pack) pack1).color = Pack.WHITE;
576: }
577: Pack pack = (Pack) packs.get(i);
578: boolean wipe = false;
579:
580: if (dfsVisit(pack, status, wipe) != 0)
581: return -1;
582:
583: }
584: return 0;
585: }
586:
587: private int dfsVisit(Pack u, int[] status, boolean wipe) {
588: u.color = Pack.GREY;
589: int check = checkValues[getPos(u.name)];
590:
591: if (Math.abs(check) != 1) {
592: wipe = true;
593: }
594: List<String> deps = u.revDependencies;
595: if (deps != null) {
596: for (String name : deps) {
597: Pack v = namesObj.get(name);
598: if (wipe) {
599: status[getPos(v.name)] = 1;
600: }
601: if (v.color == Pack.WHITE) {
602:
603: final int result = dfsVisit(v, status, wipe);
604: if (result != 0) {
605: return result;
606: }
607: }
608: }
609: }
610: u.color = Pack.BLACK;
611: return 0;
612: }
613:
614: /**
615: * @return the installedpacks
616: */
617: public Map getInstalledpacks() {
618: return this .installedpacks;
619: }
620:
621: /**
622: * @return the modifyinstallation
623: */
624: public boolean isModifyinstallation() {
625: return this.modifyinstallation;
626: }
627: }
|