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}