/* * FindBugs - Find Bugs in Java programs * Copyright (C) 2006, University of Maryland * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307, USA */ package edu.umd.cs.findbugs.gui2; import java.awt.Rectangle; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; import edu.umd.cs.findbugs.SystemProperties; /** * Saves all the stuff that should be saved for each run, * like recent projects, previous comments, the current docking layout * and the sort order * * For project related things, look in ProjectSettings * @author Dan * */ /*GUISaveState uses the Preferences API, dont look for a file anywhere, there isn't one, well... there might be, but its all system dependent where it is and how its stored*/ public class GUISaveState{ private static GUISaveState instance; // private static final String PREVCOMMENTS="Previous Comments"; private static final String SORTERTABLELENGTH="Sorter Length"; private static final String PREVCOMMENTSSIZE="Previous Comments Size"; private static final String PREFERENCESDIRECTORY="Preference Directory"; private static final String DOCKINGLAYOUT="Docking Layout"; private static final String FRAME_BOUNDS="Frame Bounds"; private static final int MAXNUMRECENTPROJECTS= 5; private static final int MAXNUMRECENTANALYSES= MAXNUMRECENTPROJECTS; private static final Sortables[] DEFAULT_COLUMN_HEADERS = new Sortables[] { Sortables.CATEGORY, Sortables.BUGCODE, Sortables.TYPE, Sortables.DIVIDER, Sortables.BUG_RANK, Sortables.FIRST_SEEN, Sortables.DESIGNATION}; private static final String[] RECENTPROJECTKEYS=new String[MAXNUMRECENTPROJECTS];//{"Project1","Project2","Project3","Project4","Project5"};//Make MAXNUMRECENTPROJECTS of these private static final String[] RECENTANALYSISKEYS=new String[MAXNUMRECENTPROJECTS]; static { for (int x=0; x<RECENTPROJECTKEYS.length;x++) { RECENTPROJECTKEYS[x]="Project"+x; RECENTANALYSISKEYS[x]="Analysis"+x; } } private static final int MAXNUMPREVCOMMENTS= 10; private static final String[] COMMENTKEYS= new String[MAXNUMPREVCOMMENTS]; static { for (int x=0; x<COMMENTKEYS.length;x++) { COMMENTKEYS[x]="Comment"+x; } } private static final String NUMPROJECTS= "NumberOfProjectsToLoad"; private static final String NUMANALYSES= "NumberOfAnalysesToLoad"; private static final String STARTERDIRECTORY= "Starter Directory"; private static final String SPLIT_MAIN = "MainSplit"; private static final String SPLIT_TREE_COMMENTS = "TreeCommentsSplit"; private static final String SPLIT_TOP = "TopSplit"; private static final String SPLIT_SUMMARY = "SummarySplit"; private int splitMain; private int splitTreeComments; private int splitTop; private int splitSummary; private File starterDirectoryForLoadBugs; /** * List of previous comments by the user. */ private LinkedList<String> previousComments; private boolean useDefault=false; private SorterTableColumnModel starterTable; private ArrayList<File> recentFiles; //private ArrayList<File> recentAnalyses; private byte[] dockingLayout; private Rectangle frameBounds; private static final String TAB_SIZE = "TabSize"; private int tabSize; //Tab size in the source code display. private static final String FONT_SIZE = "FontSize"; private float fontSize; //Font size of entire GUI. private int packagePrefixSegments; private static final String PACKAGE_PREFIX_SEGEMENTS = "PackagePrefixSegments"; public int getTabSize() { return tabSize; } public void setTabSize(int tabSize) { this.tabSize = tabSize; } public int getPackagePrefixSegments() { return packagePrefixSegments; } public void setPackagePrefixSegments(int packagePrefixSegments) { this.packagePrefixSegments = packagePrefixSegments; } public byte[] getDockingLayout() { return dockingLayout; } public void setDockingLayout(byte[] dockingLayout) { this.dockingLayout = dockingLayout; } private static String[] generateSorterKeys(int numSorters) { String[] result= new String[numSorters]; for (int x=0; x< result.length;x++) { result[x]="Sorter"+x; } return result; } SorterTableColumnModel getStarterTable() { if (useDefault || (starterTable == null)) starterTable=new SorterTableColumnModel(GUISaveState.DEFAULT_COLUMN_HEADERS); return starterTable; } private GUISaveState() { recentFiles=new ArrayList<File>(); previousComments=new LinkedList<String>(); } public static synchronized GUISaveState getInstance() { if (instance==null) instance=new GUISaveState(); return instance; } /** * This should be the method called to add a reused file for the recent menu. */ public void fileReused(File f){ if (!recentFiles.contains(f)) { throw new IllegalStateException("Selected a recent project that doesn't exist?"); } else { recentFiles.remove(f); recentFiles.add(f); } } /** * This should be the method used to add a file for the recent menu. * @param f */ public void addRecentFile(File f){ if (null != f) recentFiles.add(f); } /** * Returns the list of recent files. * @return the list of recent files */ public ArrayList<File> getRecentFiles() { return recentFiles; } /** * Call to remove a file from the list. * @param f */ public void fileNotFound(File f) { if (!recentFiles.contains(f)) { throw new IllegalStateException("Well no wonder it wasn't found, its not in the list."); } else recentFiles.remove(f); } /** * The file to start the loading of Bugs from. * @return Returns the starterDirectoryForLoadBugs. */ public File getStarterDirectoryForLoadBugs() { return starterDirectoryForLoadBugs; } /** * @param f The starterDirectoryForLoadBugs to set. */ public void setStarterDirectoryForLoadBugs(File f) { this.starterDirectoryForLoadBugs = f; } public static void loadInstance() { GUISaveState newInstance=new GUISaveState(); newInstance.recentFiles=new ArrayList<File>(); Preferences p=Preferences.userNodeForPackage(GUISaveState.class); newInstance.tabSize = p.getInt(TAB_SIZE, 4); newInstance.fontSize = p.getFloat(FONT_SIZE, 12.0f); newInstance.starterDirectoryForLoadBugs=new File(p.get(GUISaveState.STARTERDIRECTORY, SystemProperties.getProperty("user.dir"))); int prevCommentsSize=p.getInt(GUISaveState.PREVCOMMENTSSIZE, 0); for (int x=0;x<prevCommentsSize;x++) { String comment=p.get(GUISaveState.COMMENTKEYS[x], ""); newInstance.previousComments.add(comment); } int size=Math.min(MAXNUMRECENTPROJECTS,p.getInt(GUISaveState.NUMPROJECTS,0)); for (int x=0;x<size;x++) { newInstance.addRecentFile(new File(p.get(GUISaveState.RECENTPROJECTKEYS[x],""))); } int sorterSize=p.getInt(GUISaveState.SORTERTABLELENGTH,-1); if (sorterSize!=-1) { Sortables[] sortColumns=new Sortables[sorterSize]; String[] sortKeys=GUISaveState.generateSorterKeys(sorterSize); for (int x=0;x<sorterSize;x++) { sortColumns[x]=Sortables.getSortableByPrettyName(p.get(sortKeys[x], "*none*")); if (sortColumns[x]==null) { if (MainFrame.DEBUG) System.err.println("Sort order was corrupted, using default sort order"); newInstance.useDefault=true; } } if(!newInstance.useDefault) { // the beauty of Java Set<Sortables> missingSortColumns = new HashSet<Sortables>(Arrays.asList(DEFAULT_COLUMN_HEADERS)); List<Sortables> sortColumnsWithMissingCols = new ArrayList<Sortables>(Arrays.asList(sortColumns)); missingSortColumns.removeAll(sortColumnsWithMissingCols); sortColumnsWithMissingCols.addAll(missingSortColumns); newInstance.starterTable=new SorterTableColumnModel(sortColumnsWithMissingCols); } } else newInstance.useDefault=true; newInstance.dockingLayout = p.getByteArray(DOCKINGLAYOUT, new byte[0]); String boundsString = p.get(FRAME_BOUNDS, null); Rectangle r = new Rectangle(0, 0, 800, 650); if (boundsString != null) { String[] a = boundsString.split(",", 4); if (a.length > 0) try { r.x = Math.max(0, Integer.parseInt(a[0])); } catch (NumberFormatException nfe) { assert true; } if (a.length > 1) try { r.y = Math.max(0, Integer.parseInt(a[1])); } catch (NumberFormatException nfe) { assert true; } if (a.length > 2) try { r.width = Math.max(40, Integer.parseInt(a[2])); } catch (NumberFormatException nfe) { assert true; } if (a.length > 3) try { r.height = Math.max(40, Integer.parseInt(a[3])); } catch (NumberFormatException nfe) { assert true; } } newInstance.frameBounds = r; newInstance.splitMain = p.getInt(SPLIT_MAIN, 400); newInstance.splitSummary = p.getInt(SPLIT_SUMMARY, 85); newInstance.splitTop = p.getInt(SPLIT_TOP, -1); newInstance.splitTreeComments = p.getInt(SPLIT_TREE_COMMENTS, 250); newInstance.packagePrefixSegments = p.getInt(PACKAGE_PREFIX_SEGEMENTS, 3); instance=newInstance; } public void save() { Preferences p=Preferences.userNodeForPackage(GUISaveState.class); p.putInt(TAB_SIZE, tabSize); p.putFloat(FONT_SIZE, fontSize); try { p.put(STARTERDIRECTORY,starterDirectoryForLoadBugs.getCanonicalPath()); } catch (IOException e) { Debug.println(e); } int sorterLength=MainFrame.getInstance().getSorter().getColumnCount(); ArrayList<Sortables> sortables=MainFrame.getInstance().getSorter().getOrder(); p.putInt(GUISaveState.SORTERTABLELENGTH, sorterLength); String[] sorterKeys=GUISaveState.generateSorterKeys(sorterLength); for (int x=0; x<sorterKeys.length;x++) { p.put(sorterKeys[x], sortables.get(x).prettyName); } p.putInt(GUISaveState.PREVCOMMENTSSIZE, previousComments.size()); for (int x=0; x<previousComments.size();x++) { String comment=previousComments.get(x); p.put(GUISaveState.COMMENTKEYS[x], comment); } int size=recentFiles.size(); while (recentFiles.size()>MAXNUMRECENTPROJECTS) { recentFiles.remove(0); } p.putInt(GUISaveState.NUMPROJECTS,Math.min(size,MAXNUMRECENTPROJECTS)); for (int x=0; x<Math.min(size,MAXNUMRECENTPROJECTS);x++) { File file=recentFiles.get(x); p.put(GUISaveState.RECENTPROJECTKEYS[x],file.getAbsolutePath()); } p.putByteArray(DOCKINGLAYOUT, dockingLayout); p.put(FRAME_BOUNDS, frameBounds.x+","+frameBounds.y+","+frameBounds.width+","+frameBounds.height); p.putInt(SPLIT_MAIN, splitMain); p.putInt(SPLIT_SUMMARY, splitSummary); p.putInt(SPLIT_TOP, splitTop); p.putInt(SPLIT_TREE_COMMENTS, splitTreeComments); p.putInt(PACKAGE_PREFIX_SEGEMENTS, packagePrefixSegments); } static void clear() { Preferences p=Preferences.userNodeForPackage(GUISaveState.class); try { p.clear(); } catch (BackingStoreException e) { Debug.println(e); } instance=new GUISaveState(); } /** * @return Returns the previousComments. */ public LinkedList<String> getPreviousComments() { return previousComments; } /** * @param previousComments The previousComments to set. */ public void setPreviousComments(LinkedList<String> previousComments) { this.previousComments = previousComments; } /** * @return Returns the frame bounds Rectangle. */ public Rectangle getFrameBounds() { return frameBounds; } /** * @param frameBounds The frame bourds Rectangle to set. */ public void setFrameBounds(Rectangle frameBounds) { this.frameBounds = frameBounds; } /** * @return Returns the fontSize. */ public float getFontSize() { return fontSize; } /** * @param fontSize The fontSize to set. */ public void setFontSize(float fontSize) { this.fontSize = fontSize; } /** * @return Returns the location of the main divider. */ public int getSplitMain() { return splitMain; } /** * @param splitMain The location of the main divider to set. */ public void setSplitMain(int splitMain) { this.splitMain = splitMain; } /** * @return Returns the location of the summary divider. */ public int getSplitSummary() { return splitSummary; } /** * @param splitSummary The location of the summar divider to set. */ public void setSplitSummary(int splitSummary) { this.splitSummary = splitSummary; } /** * @return Returns the location of the top divider. */ public int getSplitTop() { return splitTop; } /** * @param splitTop The location of the top divider to set. */ public void setSplitTop(int splitTop) { this.splitTop = splitTop; } /** * @return Returns the location of the tree-comments divider. */ public int getSplitTreeComments() { return splitTreeComments; } /** * @param splitTreeComments The location of the tree-comments divider to set. */ public void setSplitTreeComments(int splitTreeComments) { this.splitTreeComments = splitTreeComments; } }