001/* 002 * IzPack - Copyright 2001-2005 Julien Ponge, All Rights Reserved. 003 * 004 * http://www.izforge.com/izpack/ 005 * http://developer.berlios.de/projects/izpack/ 006 * 007 * Copyright 2002 Marcus Wolschon 008 * Copyright 2002 Jan Blok 009 * Copyright 2004 Gaganis Giorgos 010 * 011 * Licensed under the Apache License, Version 2.0 (the "License"); 012 * you may not use this file except in compliance with the License. 013 * You may obtain a copy of the License at 014 * 015 * http://www.apache.org/licenses/LICENSE-2.0 016 * 017 * Unless required by applicable law or agreed to in writing, software 018 * distributed under the License is distributed on an "AS IS" BASIS, 019 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 020 * See the License for the specific language governing permissions and 021 * limitations under the License. 022 */ 023 024package com.izforge.izpack.panels; 025 026import java.util.HashMap; 027import java.util.List; 028import java.util.Map; 029 030import javax.swing.table.AbstractTableModel; 031 032import com.izforge.izpack.LocaleDatabase; 033import com.izforge.izpack.Pack; 034 035/** 036 * User: Gaganis Giorgos Date: Sep 17, 2004 Time: 8:33:21 AM 037 */ 038class PacksModel extends AbstractTableModel 039{ 040 041 /** 042 * 043 */ 044 private static final long serialVersionUID = 3258128076746733110L; 045 046 private List packs; 047 048 private List packsToInstall; 049 050 private PacksPanelInterface panel; 051 052 private LocaleDatabase langpack; 053 054 // This is used to represent the status of the checkbox 055 private int[] checkValues; 056 057 // Map to hold the object name relationship 058 Map namesObj; 059 060 // Map to hold the object name relationship 061 Map namesPos; 062 063 public PacksModel(List packs, List packsToInstall, PacksPanelInterface panel) 064 { 065 066 this.packs = packs; 067 this.packsToInstall = packsToInstall; 068 this.panel = panel; 069 langpack = panel.getLangpack(); 070 checkValues = new int[packs.size()]; 071 reverseDeps(); 072 initvalues(); 073 } 074 075 /** 076 * Creates the reverse dependency graph 077 */ 078 private void reverseDeps() 079 { 080 // name to pack map 081 namesObj = new HashMap(); 082 for (int i = 0; i < packs.size(); i++) 083 { 084 Pack pack = (Pack) packs.get(i); 085 namesObj.put(pack.name, pack); 086 } 087 // process each pack 088 for (int i = 0; i < packs.size(); i++) 089 { 090 Pack pack = (Pack) packs.get(i); 091 List deps = pack.dependencies; 092 for (int j = 0; deps != null && j < deps.size(); j++) 093 { 094 String name = (String) deps.get(j); 095 Pack parent = (Pack) namesObj.get(name); 096 parent.addRevDep(pack.name); 097 } 098 } 099 100 } 101 102 private void initvalues() 103 { 104 // name to pack position map 105 namesPos = new HashMap(); 106 for (int i = 0; i < packs.size(); i++) 107 { 108 Pack pack = (Pack) packs.get(i); 109 namesPos.put(pack.name, new Integer(i)); 110 } 111 // Init to the first values 112 for (int i = 0; i < packs.size(); i++) 113 { 114 Pack pack = (Pack) packs.get(i); 115 if (packsToInstall.contains(pack)) checkValues[i] = 1; 116 } 117 // Check out and disable the ones that are excluded by non fullfiled 118 // deps 119 for (int i = 0; i < packs.size(); i++) 120 { 121 Pack pack = (Pack) packs.get(i); 122 if (checkValues[i] == 0) 123 { 124 List deps = pack.revDependencies; 125 for (int j = 0; deps != null && j < deps.size(); j++) 126 { 127 String name = (String) deps.get(j); 128 int pos = getPos(name); 129 checkValues[pos] = -2; 130 } 131 } 132 } 133 // The required ones must propagate their required status to all the 134 // ones 135 // that they depend on 136 for (int i = 0; i < packs.size(); i++) 137 { 138 Pack pack = (Pack) packs.get(i); 139 if (pack.required == true) propRequirement(pack.name); 140 } 141 } 142 143 private void propRequirement(String name) 144 { 145 146 final int pos = getPos(name); 147 checkValues[pos] = -1; 148 List deps = ((Pack) packs.get(pos)).dependencies; 149 for (int i = 0; deps != null && i < deps.size(); i++) 150 { 151 String s = (String) deps.get(i); 152 propRequirement(s); 153 } 154 155 } 156 157 /** 158 * Given a map of names and Integer for position and a name it return the position of this name 159 * as an int 160 * 161 * @return position of the name 162 */ 163 private int getPos(String name) 164 { 165 return ((Integer) namesPos.get(name)).intValue(); 166 } 167 168 /* 169 * @see TableModel#getRowCount() 170 */ 171 public int getRowCount() 172 { 173 return packs.size(); 174 } 175 176 /* 177 * @see TableModel#getColumnCount() 178 */ 179 public int getColumnCount() 180 { 181 return 3; 182 } 183 184 /* 185 * @see TableModel#getColumnClass(int) 186 */ 187 public Class getColumnClass(int columnIndex) 188 { 189 switch (columnIndex) 190 { 191 case 0: 192 return Integer.class; 193 194 default: 195 return String.class; 196 } 197 } 198 199 /* 200 * @see TableModel#isCellEditable(int, int) 201 */ 202 public boolean isCellEditable(int rowIndex, int columnIndex) 203 { 204 if (checkValues[rowIndex] < 0) 205 { 206 return false; 207 } 208 else if (columnIndex == 0) 209 { 210 return true; 211 } 212 else 213 { 214 return false; 215 } 216 } 217 218 /* 219 * @see TableModel#getValueAt(int, int) 220 */ 221 public Object getValueAt(int rowIndex, int columnIndex) 222 { 223 224 Pack pack = (Pack) packs.get(rowIndex); 225 switch (columnIndex) 226 { 227 case 0: 228 229 return new Integer(checkValues[rowIndex]); 230 231 case 1: 232 233 if (langpack == null || pack.id == null || pack.id.equals("")) 234 { 235 return pack.name; 236 } 237 else 238 { 239 return langpack.getString(pack.id); 240 } 241 242 case 2: 243 return Pack.toByteUnitsString((int) pack.nbytes); 244 245 default: 246 return null; 247 } 248 } 249 250 /* 251 * @see TableModel#setValueAt(Object, int, int) 252 */ 253 public void setValueAt(Object aValue, int rowIndex, int columnIndex) 254 { 255 if (columnIndex == 0) 256 { 257 if (aValue instanceof Integer) 258 { 259 Pack pack = (Pack) packs.get(rowIndex); 260 if (((Integer) aValue).intValue() == 1) 261 { 262 checkValues[rowIndex] = 1; 263 updateDeps(); 264 265 int bytes = panel.getBytes(); 266 bytes += pack.nbytes; 267 panel.setBytes(bytes); 268 } 269 else 270 { 271 checkValues[rowIndex] = 0; 272 updateDeps(); 273 274 int bytes = panel.getBytes(); 275 bytes -= pack.nbytes; 276 panel.setBytes(bytes); 277 } 278 fireTableDataChanged(); 279 refreshPacksToInstall(); 280 panel.showSpaceRequired(); 281 } 282 } 283 } 284 285 private void refreshPacksToInstall() 286 { 287 288 packsToInstall.clear(); 289 for (int i = 0; i < packs.size(); i++) 290 { 291 Object pack = packs.get(i); 292 if (Math.abs(checkValues[i]) == 1) packsToInstall.add(pack); 293 294 } 295 296 } 297 298 /** 299 * This function updates the checkboxes after a change by disabling packs that cannot be 300 * installed anymore and enabling those that can after the change. This is accomplished by 301 * running a search that pinpoints the packs that must be disabled by a non-fullfiled 302 * dependency. 303 */ 304 private void updateDeps() 305 { 306 int[] statusArray = new int[packs.size()]; 307 for (int i = 0; i < statusArray.length; i++) 308 { 309 statusArray[i] = 0; 310 } 311 dfs(statusArray); 312 for (int i = 0; i < statusArray.length; i++) 313 { 314 if (statusArray[i] == 0 && checkValues[i] < 0) checkValues[i] += 2; 315 if (statusArray[i] == 1 && checkValues[i] >= 0) checkValues[i] = -2; 316 317 } 318 // The required ones must propagate their required status to all the 319 // ones 320 // that they depend on 321 for (int i = 0; i < packs.size(); i++) 322 { 323 Pack pack = (Pack) packs.get(i); 324 if (pack.required == true) propRequirement(pack.name); 325 } 326 327 } 328 329 /** 330 * We use a modified dfs graph search algorithm as described in: Thomas H. Cormen, Charles 331 * Leiserson, Ronald Rivest and Clifford Stein. Introduction to algorithms 2nd Edition 332 * 540-549,MIT Press, 2001 333 */ 334 private int dfs(int[] status) 335 { 336 for (int i = 0; i < packs.size(); i++) 337 { 338 for (int j = 0; j < packs.size(); j++) 339 { 340 ((Pack) packs.get(j)).color = Pack.WHITE; 341 } 342 Pack pack = (Pack) packs.get(i); 343 boolean wipe = false; 344 345 if (dfsVisit(pack, status, wipe) != 0) return -1; 346 347 } 348 return 0; 349 } 350 351 private int dfsVisit(Pack u, int[] status, boolean wipe) 352 { 353 u.color = Pack.GREY; 354 int check = checkValues[getPos(u.name)]; 355 356 if (Math.abs(check) != 1) 357 { 358 wipe = true; 359 } 360 List deps = u.revDependencies; 361 if (deps != null) 362 { 363 for (int i = 0; i < deps.size(); i++) 364 { 365 String name = (String) deps.get(i); 366 Pack v = (Pack) namesObj.get(name); 367 if (wipe) 368 { 369 status[getPos(v.name)] = 1; 370 } 371 if (v.color == Pack.WHITE) 372 { 373 374 final int result = dfsVisit(v, status, wipe); 375 if (result != 0) return result; 376 } 377 } 378 } 379 u.color = Pack.BLACK; 380 return 0; 381 } 382}