From 43bec1cad4d8ed554484e8c6795cf07601c12074 Mon Sep 17 00:00:00 2001 From: Marko Živković Date: Wed, 25 Feb 2015 22:30:45 +0000 Subject: Major refactoring of everything related to configuration files, especially the way of storing files. git-svn-id: https://svn.code.sf.net/p/xlogo4schools/svn/trunk@24 3b0d7934-f7ef-4143-9606-b51f2e2281fd --- logo/build.xml | 12 +- logo/org.json-20131017.jar | Bin 0 -> 68883 bytes logo/src/xlogo/AppSettings.java | 177 ++--- logo/src/xlogo/Application.java | 197 +++--- logo/src/xlogo/Logo.java | 8 +- logo/src/xlogo/StyledDocument/DocumentLogo.java | 29 +- logo/src/xlogo/gui/Editor.java | 2 +- logo/src/xlogo/gui/HistoryPanel.java | 2 +- logo/src/xlogo/gui/ZoneCommande.java | 2 +- .../gui/components/ColorStyleSelectionPanel.java | 10 +- logo/src/xlogo/gui/components/X4SGui.java | 21 +- .../xlogo/gui/preferences/AbstractPanelColor.java | 225 ++++--- logo/src/xlogo/gui/welcome/WelcomeScreen.java | 91 +-- logo/src/xlogo/gui/welcome/WorkspaceSettings.java | 3 +- .../settings/tabs/AbstractWorkspacePanel.java | 79 ++- .../gui/welcome/settings/tabs/ContestTab.java | 44 +- .../xlogo/gui/welcome/settings/tabs/GlobalTab.java | 6 +- .../settings/tabs/SyntaxHighlightingTab.java | 72 +- .../settings/tabs/WorkspaceCreationPanel.java | 4 +- .../gui/welcome/settings/tabs/WorkspaceTab.java | 397 +++++------ logo/src/xlogo/interfaces/Observable.java | 31 + .../xlogo/interfaces/PropertyChangePublisher.java | 67 ++ .../kernel/userspace/context/LogoContext.java | 2 +- .../kernel/userspace/context/UserContext.java | 17 +- .../src/xlogo/kernel/userspace/files/LogoFile.java | 30 +- .../xlogo/kernel/userspace/files/RecordFile.java | 10 +- .../kernel/userspace/procedures/Procedure.java | 12 +- logo/src/xlogo/storage/JSONSerializer.java | 21 + logo/src/xlogo/storage/Storable.java | 91 ++- logo/src/xlogo/storage/StorableDocument.java | 91 +-- logo/src/xlogo/storage/StorableObject.java | 334 ++++++++-- logo/src/xlogo/storage/WSManager.java | 592 +++++++++++++---- logo/src/xlogo/storage/global/GlobalConfig.java | 474 +++---------- logo/src/xlogo/storage/user/UserConfig.java | 729 ++++++++------------ logo/src/xlogo/storage/workspace/Serializer.java | 6 + .../storage/workspace/SyntaxHighlightConfig.java | 8 +- .../xlogo/storage/workspace/WorkspaceConfig.java | 740 +++++++-------------- .../workspace/WorkspaceSettingJSONMapper.java | 165 +++++ logo/src/xlogo/utils/Utils.java | 617 ++++++++++------- 39 files changed, 2871 insertions(+), 2547 deletions(-) create mode 100644 logo/org.json-20131017.jar create mode 100644 logo/src/xlogo/interfaces/Observable.java create mode 100644 logo/src/xlogo/interfaces/PropertyChangePublisher.java create mode 100644 logo/src/xlogo/storage/JSONSerializer.java create mode 100644 logo/src/xlogo/storage/workspace/Serializer.java create mode 100644 logo/src/xlogo/storage/workspace/WorkspaceSettingJSONMapper.java (limited to 'logo') diff --git a/logo/build.xml b/logo/build.xml index 3845532..ffaea7d 100644 --- a/logo/build.xml +++ b/logo/build.xml @@ -37,10 +37,13 @@ + + + @@ -75,6 +78,7 @@ + @@ -148,12 +152,12 @@ - + @@ -164,6 +168,7 @@ + @@ -181,6 +186,7 @@ + diff --git a/logo/org.json-20131017.jar b/logo/org.json-20131017.jar new file mode 100644 index 0000000..99e2908 Binary files /dev/null and b/logo/org.json-20131017.jar differ diff --git a/logo/src/xlogo/AppSettings.java b/logo/src/xlogo/AppSettings.java index 49b81ca..9e94efb 100644 --- a/logo/src/xlogo/AppSettings.java +++ b/logo/src/xlogo/AppSettings.java @@ -1,15 +1,19 @@ package xlogo; import java.awt.Font; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.ArrayList; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import xlogo.interfaces.Observable; +import xlogo.interfaces.PropertyChangePublisher; +import xlogo.storage.WSManager; +import xlogo.storage.global.GlobalConfig; +import xlogo.storage.global.GlobalConfig.GlobalProperty; import xlogo.storage.workspace.Language; import xlogo.storage.workspace.SyntaxHighlightConfig; +import xlogo.storage.workspace.WorkspaceConfig; +import xlogo.storage.workspace.WorkspaceConfig.WorkspaceProperty; /** * This singleton class shall eliminate the accesses to Logo.messages.getString(), @@ -18,38 +22,87 @@ import xlogo.storage.workspace.SyntaxHighlightConfig; * @author Marko * */ -public class AppSettings -{ - private static Logger logger = LogManager.getLogger(AppSettings.class.getSimpleName()); +public class AppSettings implements Observable{ + private static Logger logger = LogManager.getLogger(AppSettings.class.getSimpleName()); - private static AppSettings instance; + private static AppSettings instance; - public static AppSettings getInstance() - { + public static AppSettings getInstance() { if (instance == null) instance = new AppSettings(); return instance; } + + private WorkspaceConfig wc; + + private PropertyChangeListener languageListener = () -> { + setLanguage(wc.getLanguage()); + }; + + private PropertyChangeListener syntaxListener = () -> { + setSyntaxHighlightingStyles(wc.getSyntaxHighlightStyles()); + }; + + private PropertyChangeListener workspaceListener = () -> { + setWorkspace(WSManager.getWorkspaceConfig()); + }; + + public AppSettings() { + GlobalConfig gc = WSManager.getGlobalConfig(); + WorkspaceConfig wc = WSManager.getWorkspaceConfig(); + gc.addPropertyChangeListener(GlobalProperty.CURRENT_WORKSPACE, workspaceListener); + if (wc != null) { + setWorkspace(gc.getCurrentWorkspace().get()); + } + } + + /* * * * * * * + * WORKSPACE + * * * * * * */ + + protected void setWorkspace(WorkspaceConfig wc){ + if (this.wc == wc){ + return; + } + + if (wc != null) { + wc.removePropertyChangeListener(WorkspaceProperty.LANGUAGE, languageListener); + wc.removePropertyChangeListener(WorkspaceProperty.SYNTAX_HIGHLIGHTING, syntaxListener); + } + this.wc = wc; + + if (wc != null) { + setLanguage(wc.getLanguage()); + setSyntaxHighlightingStyles(wc.getSyntaxHighlightStyles()); + + wc.addPropertyChangeListener(WorkspaceProperty.LANGUAGE, languageListener); + wc.addPropertyChangeListener(WorkspaceProperty.SYNTAX_HIGHLIGHTING, syntaxListener); + } + publisher.publishEvent(AppProperty.WORKSPACE); + } + + public WorkspaceConfig getWorkspace(){ + return wc; + } + /* * * * * * * * LANGUAGE * * * * * * */ - private Language language; + private Language language = Language.LANGUAGE_ENGLISH; - public Language getLanguage() - { + public Language getLanguage() { return language; } - public void setLanguage(Language language) - { + protected void setLanguage(Language language) { if (language == this.language) return; logger.trace("Change language from " + this.language + " to " + language); this.language = language; Logo.generateLanguage(language); - notifyLanguageChanged(); + publisher.publishEvent(AppProperty.LANGUAGE); } /** @@ -58,103 +111,65 @@ public class AppSettings * @param key * @return */ - public String translate(String key) - { + public String translate(String key) { if (Logo.messages == null) { Logo.generateLanguage(Language.LANGUAGE_ENGLISH); // TODO this is a temporary bug fix } return Logo.messages.getString(key); } - private ArrayList languageChangeListeners = new ArrayList(); - - public void addLanguageChangeListener(ActionListener listener) - { - languageChangeListeners.add(listener); - } - - public void removeLanguageChangeListener(ActionListener listener) - { - languageChangeListeners.remove(listener); - } - - private void notifyLanguageChanged() - { - ActionEvent event = new ActionEvent(this, 0, "languageChange"); - for (ActionListener listener : languageChangeListeners) - listener.actionPerformed(event); - } - /* * * * * * * * FONT * * * * * * */ - private Font font; + private Font font; - public Font getFont() - { + public Font getFont() { return font; } - public void setFont(Font font) - { + public void setFont(Font font) { + if(this.font == font){ + return; + } this.font = font; - notifyFontChanged(); - } - - private ArrayList fontChangeListeners = new ArrayList(); - - public void addFontChangeListener(ActionListener listener) - { - fontChangeListeners.add(listener); - } - - public void removeFontChangeListener(ActionListener listener) - { - fontChangeListeners.remove(listener); - } - - private void notifyFontChanged() - { - ActionEvent event = new ActionEvent(this, 0, "fontChange"); - for (ActionListener listener : fontChangeListeners) - listener.actionPerformed(event); + publisher.publishEvent(AppProperty.FONT); } - + /* * * * * * * * SYNTAX HIGHLIGHTING STYLE * * * * * * */ - private SyntaxHighlightConfig syntaxHighlightingStyles; + private SyntaxHighlightConfig syntaxHighlightingStyles; - public SyntaxHighlightConfig getSyntaxHighlightStyles() - { + public SyntaxHighlightConfig getSyntaxHighlightStyles() { return syntaxHighlightingStyles; } - public void setSyntaxHighlightingStyles(SyntaxHighlightConfig syntaxHighlighStyles) - { + protected void setSyntaxHighlightingStyles(SyntaxHighlightConfig syntaxHighlighStyles) { this.syntaxHighlightingStyles = syntaxHighlighStyles; - notifySyntaxHighlightStyleChanged(); + publisher.publishEvent(AppProperty.SYNTAX_HIGHLIGHTING); } - private ArrayList syntaxHighlightStyleChangeListeners = new ArrayList(); - public void addSyntaxHighlightStyleChangeListener(ActionListener listener) - { - syntaxHighlightStyleChangeListeners.add(listener); + /* * * * * * * + * EVENT HANDLING + * * * * * * */ + + public enum AppProperty { + LANGUAGE, SYNTAX_HIGHLIGHTING, FONT, WORKSPACE; } - public void removeSyntaxHighlightStyleChangeListener(ActionListener listener) - { - syntaxHighlightStyleChangeListeners.remove(listener); + private transient final PropertyChangePublisher publisher = new PropertyChangePublisher<>(); + + @Override + public void addPropertyChangeListener(AppProperty property, PropertyChangeListener listener) { + publisher.addPropertyChangeListener(property, listener); } - private void notifySyntaxHighlightStyleChanged() - { - ActionEvent event = new ActionEvent(this, 0, "fontChange"); - for (ActionListener listener : syntaxHighlightStyleChangeListeners) - listener.actionPerformed(event); + @Override + public void removePropertyChangeListener(AppProperty property, PropertyChangeListener listener) { + publisher.removePropertyChangeListener(property, listener); } - + } diff --git a/logo/src/xlogo/Application.java b/logo/src/xlogo/Application.java index 0c19b88..cbc239f 100644 --- a/logo/src/xlogo/Application.java +++ b/logo/src/xlogo/Application.java @@ -84,69 +84,69 @@ import xlogo.messages.async.history.HistoryMessenger; * @author Loic Le Coq */ public class Application extends X4SFrame { - private static final int BG_COLOR = 0xB3BCEA; - public static final String appName = "XLogo4Schools"; + private static final int BG_COLOR = 0xB3BCEA; + public static final String appName = "XLogo4Schools"; - private static Stack pile_historique; - private int index_historique; + private static Stack pile_historique; + private int index_historique; - private MenuListener menulistener; + private MenuListener menulistener; - public boolean error; - boolean stop; + public boolean error; + boolean stop; - public Affichage affichage; - private Sound_Player son; - private Touche touche; - private Popup jpop; + public Affichage affichage; + private Sound_Player son; + private Touche touche; + private Popup jpop; // pref Box /** * To display 3D View */ - private Viewer3D viewer3d; + private Viewer3D viewer3d; // Interpreter and drawer - private Kernel kernel; + private Kernel kernel; - private UserSpace userSpace; - private UserConfig uc; + private UserSpace userSpace; + private UserConfig uc; /* * My Layout */ - private JFrame mainFrame; - private JPanel filesAndProcedures; - private JPanel commandOrEditor; - private FilesList filesList; - private ProcedureSearch procedureSearch; + private JFrame mainFrame; + private JPanel filesAndProcedures; + private JPanel commandOrEditor; + private FilesList filesList; + private ProcedureSearch procedureSearch; // drawingOrEditor@Drawing - private JPanel commandCard; - private ZoneCommande commandLine; - private JLabel recordTimerLabel; - private JButton stopButton; - private JButton menuButton; - public JSplitPane drawingAndHistory; - - private JPanel drawingAndExtras; - private HistoryPanel history; - - public JScrollPane scrollArea; - private DrawPanel drawPanel; - private JPanel extrasPanel; - private JSlider speedSlider; - private TurtleComboBox turtleCombo; + private JPanel commandCard; + private ZoneCommande commandLine; + private JLabel recordTimerLabel; + private JButton stopButton; + private JButton menuButton; + public JSplitPane drawingAndHistory; + + private JPanel drawingAndExtras; + private HistoryPanel history; + + public JScrollPane scrollArea; + private DrawPanel drawPanel; + private JPanel extrasPanel; + private JSlider speedSlider; + private TurtleComboBox turtleCombo; // drawingOrEditor@Editor - private Editor editor; + private Editor editor; // Extras Menu - private JPopupMenu extras; - - private static final String COMMAND_CARD_ID = "command_card"; - private static final String EDITOR_CARD_ID = "editor_card"; + private JPopupMenu extras; + + private static final String COMMAND_CARD_ID = "command_card"; + private static final String EDITOR_CARD_ID = "editor_card"; /** Builds the main frame */ public Application() { @@ -266,11 +266,11 @@ public class Application extends X4SFrame { }); } - private void initFilesList(){ + private void initFilesList() { filesList = new FilesList(); boolean isEditable = userSpace.isFilesListEditable(); filesList.setEditable(isEditable); - for (String fileName : userSpace.getFileNames()){ + for (String fileName : userSpace.getFileNames()) { boolean hasErrors = userSpace.hasErrors(fileName); filesList.addFile(fileName, isEditable, hasErrors); } @@ -339,7 +339,7 @@ public class Application extends X4SFrame { /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * LAYOUT * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - + @Override protected void layoutComponent() { layoutFilesAndProcedures(); @@ -491,46 +491,43 @@ public class Application extends X4SFrame { //Create the popup menu. extras = new JPopupMenu(); - if (!uc.isVirtual()) // Contest Mode not available in virtual mode - { - startContestMenuItem = new JMenuItem(translate(MessageKeys.CONTEST_MODE_START)); - stopContestMenuItem = new JMenuItem(translate(MessageKeys.CONTEST_MODE_STOP)); - extras.add(startContestMenuItem, 0); - - startContestMenuItem.addActionListener(new ActionListener(){ - public void actionPerformed(ActionEvent e) { - WorkspaceConfig wc = WSManager.getWorkspaceConfig(); - int nOfFiles = wc.getNOfContestFiles(); - int nOfBonusFiles = wc.getNOfContestBonusFiles(); - - String[] contestFileNames = new String[nOfFiles + nOfBonusFiles]; - - String nameBase = translate("contest.mode.filename") + " "; - for (int i = 0; i < nOfFiles; i++) - contestFileNames[i] = nameBase + (i + 1); - - nameBase = translate("contest.mode.bonus.filename") + " "; - - for (int i = 0; i < nOfBonusFiles; i++) - contestFileNames[nOfFiles + i] = nameBase + (i + 1); - try { - userSpace.startRecordMode(contestFileNames); - } - catch (IOException e1) { - DialogMessenger.getInstance().dispatchMessage(translate("contest.error.title"), - translate("contest.could.not.create") + "\n" + e.toString()); - return; - } - commandLine.requestFocus(); + startContestMenuItem = new JMenuItem(translate(MessageKeys.CONTEST_MODE_START)); + stopContestMenuItem = new JMenuItem(translate(MessageKeys.CONTEST_MODE_STOP)); + extras.add(startContestMenuItem, 0); + + startContestMenuItem.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + WorkspaceConfig wc = WSManager.getWorkspaceConfig(); + int nOfFiles = wc.getNOfContestFiles(); + int nOfBonusFiles = wc.getNOfContestBonusFiles(); + + String[] contestFileNames = new String[nOfFiles + nOfBonusFiles]; + + String nameBase = translate("contest.mode.filename") + " "; + for (int i = 0; i < nOfFiles; i++) + contestFileNames[i] = nameBase + (i + 1); + + nameBase = translate("contest.mode.bonus.filename") + " "; + + for (int i = 0; i < nOfBonusFiles; i++) + contestFileNames[nOfFiles + i] = nameBase + (i + 1); + try { + userSpace.startRecordMode(contestFileNames); } - }); - stopContestMenuItem.addActionListener(new ActionListener(){ - public void actionPerformed(ActionEvent e) { - userSpace.stopRecordMode(); - commandLine.requestFocus(); + catch (IOException e1) { + DialogMessenger.getInstance().dispatchMessage(translate("contest.error.title"), + translate("contest.could.not.create") + "\n" + e.toString()); + return; } - }); - } + commandLine.requestFocus(); + } + }); + stopContestMenuItem.addActionListener(new ActionListener(){ + public void actionPerformed(ActionEvent e) { + userSpace.stopRecordMode(); + commandLine.requestFocus(); + } + }); importMenuItem = new JMenuItem(translate(MessageKeys.US_IMPORT)); extras.add(importMenuItem); @@ -712,8 +709,8 @@ public class Application extends X4SFrame { @Override public void run() { HistoryMessenger.getInstance().dispatchComment( - fileName + " : " + translate(MessageKeys.HIST_MSG_PROCEDURES_UNDEFINED) + " " + procedure - + ".\n"); + fileName + " : " + translate(MessageKeys.HIST_MSG_PROCEDURES_UNDEFINED) + " " + + procedure + ".\n"); } }); } @@ -731,8 +728,8 @@ public class Application extends X4SFrame { sb.delete(sb.length() - 2, sb.length() - 1); HistoryMessenger.getInstance().dispatchComment( - fileName + " : " + translate(MessageKeys.HIST_MSG_PROCEDURES_UNDEFINED) + " " + sb.toString() - + ".\n"); + fileName + " : " + translate(MessageKeys.HIST_MSG_PROCEDURES_UNDEFINED) + " " + + sb.toString() + ".\n"); } }); } @@ -893,7 +890,7 @@ public class Application extends X4SFrame { } }); } - + private Timer recordTimer; @Override @@ -915,7 +912,7 @@ public class Application extends X4SFrame { long diff = now.getTime() - start.getTime(); - recordTimerLabel.setText(UserConfig.getMinSec(diff)); + recordTimerLabel.setText(Utils.getMinSec(diff)); } }); recordTimer.start(); @@ -997,7 +994,7 @@ public class Application extends X4SFrame { userSpace.closeFile(fileName); } }); - + userSpace.addBroadcastListener(new MessageListener(){ @Override @@ -1012,7 +1009,7 @@ public class Application extends X4SFrame { }); } - public static void runOnGuiThread(Runnable runnable){ + public static void runOnGuiThread(Runnable runnable) { if (SwingUtilities.isEventDispatchThread()) { runnable.run(); return; @@ -1032,7 +1029,7 @@ public class Application extends X4SFrame { /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * COMMANDS * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - + public void showWelcomeMessage() { HistoryMessenger.getInstance().dispatchComment( translate(MessageKeys.APP_HELLO) + " " + uc.getUserName() + "!\n"); @@ -1088,26 +1085,24 @@ public class Application extends X4SFrame { CardLayout cardLayout = (CardLayout) commandOrEditor.getLayout(); cardLayout.show(commandOrEditor, EDITOR_CARD_ID); } - + public void focusCommandLine() { commandLine.requestFocus(); } - public void focusEditor(){ + public void focusEditor() { editor.requestFocus(); } - + public void closeWindow() { WSManager storageManager = WSManager.getInstance(); try { - if (!uc.isVirtual()) { - String openFile = userSpace.getOpenFileName(); - if (openFile != null) { - userSpace.writeFileText(openFile, editor.getText()); - userSpace.storeFile(openFile); - } - storageManager.getUserConfigInstance().store(); + String openFile = userSpace.getOpenFileName(); + if (openFile != null) { + userSpace.writeFileText(openFile, editor.getText()); + userSpace.storeFile(openFile); } + storageManager.storeAllSettings(); System.exit(0); } catch (Exception e1) { @@ -1220,7 +1215,7 @@ public class Application extends X4SFrame { public void run() { PrintWriter out = null; File logoFile = uc.getCommandLineContestFile(); - String line = UserConfig.getTimeStamp() + " : " + text; + String line = Utils.getTimeStamp() + " : " + text; try { out = new PrintWriter(new BufferedWriter(new FileWriter(logoFile, true))); out.println(line); diff --git a/logo/src/xlogo/Logo.java b/logo/src/xlogo/Logo.java index 0f7da76..34945fb 100644 --- a/logo/src/xlogo/Logo.java +++ b/logo/src/xlogo/Logo.java @@ -98,7 +98,7 @@ public class Logo { //Recuperer les fichiers de démarrage correspondant au double clic de souris // ou au lancement en ligne de commande - GlobalConfig gc = WSManager.getInstance().getGlobalConfigInstance(); + GlobalConfig gc = WSManager.getGlobalConfig(); for(int i=0;i combo_couleur; private String[] msg = { Logo.messages.getString("style.none"), Logo.messages.getString("style.bold"), Logo.messages.getString("style.italic"), Logo.messages.getString("style.underline") }; - private JComboBox style = new JComboBox(msg); + private JComboBox style = new JComboBox<>(msg); private JLabel titre = new JLabel(); private Color couleur_perso = Color.WHITE; private GridBagLayout gb = new GridBagLayout(); @@ -79,7 +79,7 @@ public class ColorStyleSelectionPanel { //titre.setFont(font); titre.setText(title + ":"); - combo_couleur = new JComboBox(intArray); + combo_couleur = new JComboBox<>(intArray); ComboBoxRenderer renderer = new ComboBoxRenderer(); combo_couleur.setRenderer(renderer); setColorAndStyle(rgb, sty); @@ -159,7 +159,7 @@ public class ColorStyleSelectionPanel { DrawPanel.defaultColors[combo_couleur.getSelectedIndex()]); } - private class ComboBoxRenderer extends JPanel implements ListCellRenderer { + private class ComboBoxRenderer extends JPanel implements ListCellRenderer { private static final long serialVersionUID = 1L; int id = 0; @@ -168,7 +168,7 @@ public class ColorStyleSelectionPanel { setPreferredSize(new Dimension(50, 20)); } - public Component getListCellRendererComponent(JList list, Object value, + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { // Get the selected index. (The index param isn't // always valid, so just use the value.) diff --git a/logo/src/xlogo/gui/components/X4SGui.java b/logo/src/xlogo/gui/components/X4SGui.java index f23f859..9aacaae 100644 --- a/logo/src/xlogo/gui/components/X4SGui.java +++ b/logo/src/xlogo/gui/components/X4SGui.java @@ -27,10 +27,9 @@ package xlogo.gui.components; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - import xlogo.AppSettings; +import xlogo.AppSettings.AppProperty; +import xlogo.interfaces.Observable.PropertyChangeListener; /** * Automatically calls all convenience features in the following order: @@ -68,29 +67,23 @@ public abstract class X4SGui { stopListenForLanguageChangeEvents(); } - private ActionListener languageChangeListener; + private PropertyChangeListener languageChangeListener = () -> { + setText(); + }; /** * Note: registers only once, even if called more than once. */ public void startListenForLanguageChangeEvents() { - if (languageChangeListener != null) - return; - languageChangeListener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - setText(); - } - }; - AppSettings.getInstance().addLanguageChangeListener(languageChangeListener); + AppSettings.getInstance().addPropertyChangeListener(AppProperty.LANGUAGE, languageChangeListener); } public void stopListenForLanguageChangeEvents() { if (languageChangeListener == null) return; - AppSettings.getInstance().removeLanguageChangeListener(languageChangeListener); + AppSettings.getInstance().removePropertyChangeListener(AppProperty.LANGUAGE, languageChangeListener); languageChangeListener = null; } diff --git a/logo/src/xlogo/gui/preferences/AbstractPanelColor.java b/logo/src/xlogo/gui/preferences/AbstractPanelColor.java index 6f43e99..d8b097b 100644 --- a/logo/src/xlogo/gui/preferences/AbstractPanelColor.java +++ b/logo/src/xlogo/gui/preferences/AbstractPanelColor.java @@ -1,29 +1,24 @@ -/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Loic Le Coq +/* + * XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Loic Le Coq * Copyright (C) 2013 Marko Zivkovic - * * Contact Information: marko88zivkovic at gmail dot com - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. This program 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 General - * Public License for more details. You should have received a copy of the - * GNU General Public License along with this program; if not, write to the Free - * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. This program 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 General + * Public License for more details. You should have received a copy of the + * GNU General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. - * - * * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic * during his Bachelor thesis at the computer science department of ETH Zurich, * in the year 2013 and/or during future work. - * * It is a reengineered version of XLogo written by Loic Le Coq, published * under the GPL License at http://xlogo.tuxfamily.org/ - * * Contents of this file were initially written by Loic Le Coq, - * modifications, extensions, refactorings might have been applied by Marko Zivkovic + * modifications, extensions, refactorings might have been applied by Marko Zivkovic */ package xlogo.gui.preferences; @@ -47,97 +42,107 @@ import xlogo.Logo; import xlogo.kernel.DrawPanel; import xlogo.storage.WSManager; -public abstract class AbstractPanelColor extends JPanel implements ActionListener{ - private static final long serialVersionUID = 1L; -// private ImageIcon[] images=new ImageIcon[17]; - private Integer[] intArray=new Integer[17]; - private JButton bchoisir=new JButton(Logo.messages.getString("pref.highlight.other")); - protected JComboBox combo_couleur; - private Color couleur_perso=Color.WHITE; - public AbstractPanelColor(Color c) { - setBackground(Color.blue); - for(int i=0;i<17;i++){ - intArray[i]=new Integer(i); - } - combo_couleur = new JComboBox(intArray); - ComboBoxRenderer renderer= new ComboBoxRenderer(); - combo_couleur.setRenderer(renderer); - setColorAndStyle(c); - combo_couleur.setActionCommand("combo"); - combo_couleur.addActionListener(this); - bchoisir.setFont(WSManager.getWorkspaceConfig().getFont()); - bchoisir.setActionCommand("bouton"); - bchoisir.addActionListener(this); - add(combo_couleur); - add(bchoisir); - } - public void setEnabled(boolean b){ - super.setEnabled(b); - combo_couleur.setEnabled(b); - bchoisir.setEnabled(b); - - } - void setColorAndStyle(Color rgb){ - int index=-1; - for(int i=0;i<17;i++){ - if (DrawPanel.defaultColors[i].equals(rgb)){ - index=i; - } - } - if (index==-1){couleur_perso=rgb;index=7;} - combo_couleur.setSelectedIndex(index); - } - abstract public void actionPerformed(ActionEvent e); - - private class ComboBoxRenderer extends JPanel - implements ListCellRenderer { - private static final long serialVersionUID = 1L; - int id=0; - public ComboBoxRenderer() { - setOpaque(true); - setPreferredSize(new Dimension(50,20)); - } - - public Component getListCellRendererComponent( - JList list, - Object value, - int index, - boolean isSelected, - boolean cellHasFocus) { -//Get the selected index. (The index param isn't -//always valid, so just use the value.) - int selectedIndex = ((Integer)value).intValue(); - this.id=selectedIndex; - if (isSelected) { - setBackground(list.getSelectionBackground()); - setForeground(list.getSelectionForeground()); - } else { - setBackground(list.getBackground()); - setForeground(list.getForeground()); - } - //Set the icon and text. If icon was null, say so. - - setBorder(BorderFactory.createEmptyBorder(2,2,2,2)); - return this; - } - public void paint(Graphics g){ - super.paint(g); - if (id!=7) g.setColor(DrawPanel.defaultColors[id]); - else g.setColor(couleur_perso); - g.fillRect(5,2,40,15); - } - } - public Color getValue(){ - int id=combo_couleur.getSelectedIndex(); - if (id!=7) return DrawPanel.defaultColors[id]; - return couleur_perso; - } - protected void actionButton(){ - Color color=JColorChooser.showDialog(this,"",DrawPanel.defaultColors[combo_couleur.getSelectedIndex()]); - if (null!=color){ - couleur_perso=color; - combo_couleur.setSelectedIndex(7); - combo_couleur.repaint(); - } - } +public abstract class AbstractPanelColor extends JPanel implements ActionListener { + private static final long serialVersionUID = 1L; + // private ImageIcon[] images=new ImageIcon[17]; + private Integer[] intArray = new Integer[17]; + private JButton bchoisir = new JButton(Logo.messages.getString("pref.highlight.other")); + protected JComboBox combo_couleur; + private Color couleur_perso = Color.WHITE; + + public AbstractPanelColor(Color c) { + setBackground(Color.blue); + for (int i = 0; i < 17; i++) { + intArray[i] = new Integer(i); + } + combo_couleur = new JComboBox<>(intArray); + ComboBoxRenderer renderer = new ComboBoxRenderer(); + combo_couleur.setRenderer(renderer); + setColorAndStyle(c); + combo_couleur.setActionCommand("combo"); + combo_couleur.addActionListener(this); + bchoisir.setFont(WSManager.getWorkspaceConfig().getFont()); + bchoisir.setActionCommand("bouton"); + bchoisir.addActionListener(this); + add(combo_couleur); + add(bchoisir); + } + + public void setEnabled(boolean b) { + super.setEnabled(b); + combo_couleur.setEnabled(b); + bchoisir.setEnabled(b); + + } + + void setColorAndStyle(Color rgb) { + int index = -1; + for (int i = 0; i < 17; i++) { + if (DrawPanel.defaultColors[i].equals(rgb)) { + index = i; + } + } + if (index == -1) { + couleur_perso = rgb; + index = 7; + } + combo_couleur.setSelectedIndex(index); + } + + abstract public void actionPerformed(ActionEvent e); + + private class ComboBoxRenderer extends JPanel implements ListCellRenderer { + private static final long serialVersionUID = 1L; + int id = 0; + + public ComboBoxRenderer() { + setOpaque(true); + setPreferredSize(new Dimension(50, 20)); + } + + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, + boolean cellHasFocus) { + //Get the selected index. (The index param isn't + //always valid, so just use the value.) + int selectedIndex = ((Integer) value).intValue(); + this.id = selectedIndex; + if (isSelected) { + setBackground(list.getSelectionBackground()); + setForeground(list.getSelectionForeground()); + } + else { + setBackground(list.getBackground()); + setForeground(list.getForeground()); + } + //Set the icon and text. If icon was null, say so. + + setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); + return this; + } + + public void paint(Graphics g) { + super.paint(g); + if (id != 7) + g.setColor(DrawPanel.defaultColors[id]); + else + g.setColor(couleur_perso); + g.fillRect(5, 2, 40, 15); + } + } + + public Color getValue() { + int id = combo_couleur.getSelectedIndex(); + if (id != 7) + return DrawPanel.defaultColors[id]; + return couleur_perso; + } + + protected void actionButton() { + Color color = JColorChooser.showDialog(this, "", DrawPanel.defaultColors[combo_couleur.getSelectedIndex()]); + if (null != color) { + couleur_perso = color; + combo_couleur.setSelectedIndex(7); + combo_couleur.repaint(); + } + } } diff --git a/logo/src/xlogo/gui/welcome/WelcomeScreen.java b/logo/src/xlogo/gui/welcome/WelcomeScreen.java index 890e806..61f27b6 100644 --- a/logo/src/xlogo/gui/welcome/WelcomeScreen.java +++ b/logo/src/xlogo/gui/welcome/WelcomeScreen.java @@ -33,11 +33,13 @@ import java.awt.*; import java.io.IOException; import xlogo.gui.components.X4SFrame; +import xlogo.interfaces.Observable.PropertyChangeListener; import xlogo.messages.MessageKeys; import xlogo.messages.async.dialog.DialogMessenger; import xlogo.storage.Storable; import xlogo.storage.WSManager; import xlogo.storage.global.GlobalConfig; +import xlogo.storage.global.GlobalConfig.GlobalProperty; import xlogo.storage.workspace.WorkspaceConfig; import xlogo.utils.Utils; import xlogo.utils.WebPage; @@ -59,8 +61,8 @@ public class WelcomeScreen extends X4SFrame { private JLabel workspace; private JLabel username; - private JComboBox workspaceSelection; - private JComboBox userSelection; + private JComboBox workspaceSelection; + private JComboBox userSelection; private JButton openWorkspaceSettingsBtn; private JButton enterButton; @@ -71,11 +73,13 @@ public class WelcomeScreen extends X4SFrame { private JPanel panel; private GroupLayout groupLayout; + private WorkspaceSettings workspaceSettings; + private ActionListener onApplicationEnterListener; - private ActionListener onWorkspaceListChangeListener; - private ActionListener onEnterWorkspaceListener; - private WorkspaceSettings workspaceSettings; + private PropertyChangeListener onWorkspaceListChangeListener; + private PropertyChangeListener onEnterWorkspaceListener; + /** * @@ -99,7 +103,7 @@ public class WelcomeScreen extends X4SFrame { @Override public void dispose() { try { - WSManager.getInstance().getGlobalConfigInstance().store(); + WSManager.getInstance().storeAllSettings(); } catch (IOException e) { DialogMessenger.getInstance().dispatchMessage(translate("ws.error.title"), @@ -117,8 +121,8 @@ public class WelcomeScreen extends X4SFrame { workspace = new JLabel("Workspace"); username = new JLabel("User"); - workspaceSelection = new JComboBox(); - userSelection = new JComboBox(); + workspaceSelection = new JComboBox<>(); + userSelection = new JComboBox<>(); openWorkspaceSettingsBtn = new JButton("Settings"); enterButton = new JButton("Enter"); @@ -207,32 +211,22 @@ public class WelcomeScreen extends X4SFrame { GlobalConfig gc = WSManager.getGlobalConfig(); - onEnterWorkspaceListener = new ActionListener(){ - - @Override - public void actionPerformed(ActionEvent e) { - ignoreGuiEvents = true; - populateWorkspaceList(); - populateUserList(); - ignoreGuiEvents = false; - - } + onWorkspaceListChangeListener = () -> { + ignoreGuiEvents = true; + populateWorkspaceList(); + populateUserList(); + ignoreGuiEvents = false; }; - gc.addEnterWorkspaceListener(onEnterWorkspaceListener); - - onWorkspaceListChangeListener = new ActionListener(){ - - @Override - public void actionPerformed(ActionEvent e) { - ignoreGuiEvents = true; - populateWorkspaceList(); - populateUserList(); - ignoreGuiEvents = false; - } + onEnterWorkspaceListener = () -> { + ignoreGuiEvents = true; + populateWorkspaceList(); + populateUserList(); + ignoreGuiEvents = false; }; - - WSManager.getGlobalConfig().addWorkspaceListChangeListener(onWorkspaceListChangeListener); + + gc.addPropertyChangeListener(GlobalProperty.CURRENT_WORKSPACE, onEnterWorkspaceListener); + gc.addPropertyChangeListener(GlobalProperty.WORKSPACES, onWorkspaceListChangeListener); workspaceSelection.addItemListener(new ItemListener(){ public void itemStateChanged(ItemEvent e) { @@ -314,25 +308,33 @@ public class WelcomeScreen extends X4SFrame { } private void populateWorkspaceList() { - GlobalConfig gc = WSManager.getInstance().getGlobalConfigInstance(); + GlobalConfig gc = WSManager.getGlobalConfig(); String[] workspaces = gc.getAllWorkspaces(); - workspaceSelection.setModel(new DefaultComboBoxModel(workspaces)); + workspaceSelection.setModel(new DefaultComboBoxModel<>(workspaces)); selectCurrentWorkspace(); } private void selectCurrentWorkspace(){ - GlobalConfig gc = WSManager.getInstance().getGlobalConfigInstance(); + GlobalConfig gc = WSManager.getGlobalConfig(); workspaceSelection.setSelectedItem(gc.getLastUsedWorkspace()); } private void populateUserList() { - WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance(); - String[] users = wc.getUserList(); - userSelection.setModel(new DefaultComboBoxModel(users)); - String lastUser = wc.getLastActiveUser(); + WorkspaceConfig wc = WSManager.getWorkspaceConfig(); + String[] users; + String lastUser = null; + boolean isUserCreationAllowed = false; + if (wc != null){ + users = wc.getUserList(); + lastUser = wc.getLastActiveUser(); + isUserCreationAllowed = wc.isUserCreationAllowed(); + } else { + users = new String[0]; + } + userSelection.setModel(new DefaultComboBoxModel<>(users)); userSelection.setSelectedItem(lastUser); enterButton.setEnabled(lastUser != null && lastUser.length() > 0); - userSelection.setEditable(wc.isUserCreationAllowed()); + userSelection.setEditable(isUserCreationAllowed); } protected void enterWorkspace(String workspaceName) { @@ -350,7 +352,7 @@ public class WelcomeScreen extends X4SFrame { Runnable runnable = new Runnable(){ public void run() { String authentification = null; - GlobalConfig gc = WSManager.getInstance().getGlobalConfigInstance(); + GlobalConfig gc = WSManager.getGlobalConfig(); if (gc.isPasswordRequired()) { authentification = showPasswordPopup(); if (authentification == null) @@ -408,9 +410,10 @@ public class WelcomeScreen extends X4SFrame { return; } + // The following is in case the user entered a new name : Need to create user first WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance(); if (!wc.existsUserLogically(username)) - wc.createUser(username); + WSManager.getInstance().createUser(username); try { WSManager.getInstance().enterUserSpace(username); @@ -429,10 +432,10 @@ public class WelcomeScreen extends X4SFrame { // TODO remove each reference to workspace settings workspaceSettings = null; } - WSManager.getGlobalConfig().removeEnterWorkspaceListener(onEnterWorkspaceListener); - WSManager.getGlobalConfig().removeWorkspaceListChangeListener(onWorkspaceListChangeListener); + WSManager.getGlobalConfig().removePropertyChangeListener(GlobalProperty.CURRENT_WORKSPACE, onEnterWorkspaceListener); + WSManager.getGlobalConfig().removePropertyChangeListener(GlobalProperty.WORKSPACES, onWorkspaceListChangeListener); try { - WSManager.getWorkspaceConfig().store(); + WSManager.getInstance().storeAllSettings(); } catch (IOException ignore) { } onApplicationEnterListener.actionPerformed(new ActionEvent(this, 0, null)); diff --git a/logo/src/xlogo/gui/welcome/WorkspaceSettings.java b/logo/src/xlogo/gui/welcome/WorkspaceSettings.java index fbc9f48..39b9647 100644 --- a/logo/src/xlogo/gui/welcome/WorkspaceSettings.java +++ b/logo/src/xlogo/gui/welcome/WorkspaceSettings.java @@ -85,8 +85,7 @@ public class WorkspaceSettings extends X4SFrame { public void dispose() { try { - WSManager.getInstance().getGlobalConfigInstance().store(); - WSManager.getInstance().getWorkspaceConfigInstance().store(); + WSManager.getInstance().storeAllSettings(); } catch (IOException e) { DialogMessenger.getInstance().dispatchMessage( translate("ws.error.title"), diff --git a/logo/src/xlogo/gui/welcome/settings/tabs/AbstractWorkspacePanel.java b/logo/src/xlogo/gui/welcome/settings/tabs/AbstractWorkspacePanel.java index 52d49e3..e3245bc 100644 --- a/logo/src/xlogo/gui/welcome/settings/tabs/AbstractWorkspacePanel.java +++ b/logo/src/xlogo/gui/welcome/settings/tabs/AbstractWorkspacePanel.java @@ -36,20 +36,21 @@ import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import javax.swing.JOptionPane; -import xlogo.AppSettings; import xlogo.gui.components.X4SComponent; +import xlogo.interfaces.Observable.PropertyChangeListener; import xlogo.messages.async.dialog.DialogMessenger; import xlogo.storage.Storable; import xlogo.storage.WSManager; import xlogo.storage.global.GlobalConfig; +import xlogo.storage.global.GlobalConfig.GlobalProperty; import xlogo.storage.workspace.WorkspaceConfig; public abstract class AbstractWorkspacePanel extends X4SComponent{ - private ActionListener enterWorkspaceListener; - private ActionListener workspaceListChangeListener; + private PropertyChangeListener enterWorkspaceListener; + private PropertyChangeListener workspaceListChangeListener; - protected abstract JComboBox getWorkspaceSelection(); + protected abstract JComboBox getWorkspaceSelection(); private boolean ignoreGuiEvents = false; @@ -63,31 +64,41 @@ public abstract class AbstractWorkspacePanel extends X4SComponent{ new Thread(new Runnable() { public void run() { String wsName = (String) getWorkspaceSelection().getSelectedItem(); - enterWorkspace(wsName); + if (wsName != null){ + enterWorkspace(wsName); + } } }).run(); } }); - final GlobalConfig gc = WSManager.getGlobalConfig(); - - gc.addWorkspaceListChangeListener(workspaceListChangeListener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent arg0) { - ignoreGuiEvents = true; - populateWorkspaceList(); - ignoreGuiEvents = false; + enterWorkspaceListener = () -> { + ignoreGuiEvents = true; + if (WSManager.getWorkspaceConfig() != null){ + enableComponents(); + } else { + disableComponents(); } - }); + selectCurrentWorkspace(); + ignoreGuiEvents = false; + }; - gc.addEnterWorkspaceListener(enterWorkspaceListener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent arg0) { - ignoreGuiEvents = true; - selectCurrentWorkspace(); - ignoreGuiEvents = false; + workspaceListChangeListener = () -> { + ignoreGuiEvents = true; + if (WSManager.getWorkspaceConfig() != null){ + enableComponents(); + } else { + disableComponents(); } - }); + populateWorkspaceList(); + ignoreGuiEvents = false; + }; + + final GlobalConfig gc = WSManager.getGlobalConfig(); + + gc.addPropertyChangeListener(GlobalProperty.WORKSPACES, workspaceListChangeListener); + + gc.addPropertyChangeListener(GlobalProperty.CURRENT_WORKSPACE, enterWorkspaceListener); } @Override @@ -95,9 +106,8 @@ public abstract class AbstractWorkspacePanel extends X4SComponent{ { super.stopEventListeners(); GlobalConfig gc = WSManager.getGlobalConfig(); - gc.removeEnterWorkspaceListener(enterWorkspaceListener); - gc.removeWorkspaceListChangeListener(workspaceListChangeListener); - + gc.removePropertyChangeListener(GlobalProperty.WORKSPACES, workspaceListChangeListener); + gc.removePropertyChangeListener(GlobalProperty.CURRENT_WORKSPACE, enterWorkspaceListener); } protected abstract void setValues(); @@ -107,14 +117,14 @@ public abstract class AbstractWorkspacePanel extends X4SComponent{ protected abstract void disableComponents(); protected void populateWorkspaceList() { - GlobalConfig gc = WSManager.getInstance().getGlobalConfigInstance(); + GlobalConfig gc = WSManager.getGlobalConfig(); String[] workspaces = gc.getAllWorkspaces(); - getWorkspaceSelection().setModel(new DefaultComboBoxModel(workspaces)); + getWorkspaceSelection().setModel(new DefaultComboBoxModel(workspaces)); selectCurrentWorkspace(); } protected void selectCurrentWorkspace(){ - GlobalConfig gc = WSManager.getInstance().getGlobalConfigInstance(); + GlobalConfig gc = WSManager.getGlobalConfig(); String lastUsed = gc.getLastUsedWorkspace(); getWorkspaceSelection().setSelectedItem(lastUsed); setValues(); @@ -133,13 +143,6 @@ public abstract class AbstractWorkspacePanel extends X4SComponent{ boolean ans = getUserYesOrNo(message, translate("ws.settings.delete.from.fs")); - try { - wsManager.enterWorkspace(WorkspaceConfig.VIRTUAL_WORKSPACE); - } catch (IOException e) { - DialogMessenger.getInstance().dispatchMessage( - translate("ws.error.title"), - translate("ws.settings.could.not.enter.virtual.ws") + e.toString()); - } wsManager.deleteWorkspace(wsName, ans); populateWorkspaceList(); @@ -205,12 +208,8 @@ public abstract class AbstractWorkspacePanel extends X4SComponent{ disableComponents(); return; } - if (wc.isVirtual()) - disableComponents(); - else - enableComponents(); + enableComponents(); setValues(); - AppSettings.getInstance().setLanguage(wc.getLanguage()); } catch (IOException e) { DialogMessenger.getInstance().dispatchMessage( translate("ws.error.title"), @@ -237,7 +236,7 @@ public abstract class AbstractWorkspacePanel extends X4SComponent{ String location = wscPanel.locationField.getText(); File dir = new File(location); - GlobalConfig gc = WSManager.getInstance().getGlobalConfigInstance(); + GlobalConfig gc = WSManager.getGlobalConfig(); // Make sure that the specified workspace name is non-empty and that it does not exist already if (wsName.length() == 0){ diff --git a/logo/src/xlogo/gui/welcome/settings/tabs/ContestTab.java b/logo/src/xlogo/gui/welcome/settings/tabs/ContestTab.java index 53d658b..9583de5 100644 --- a/logo/src/xlogo/gui/welcome/settings/tabs/ContestTab.java +++ b/logo/src/xlogo/gui/welcome/settings/tabs/ContestTab.java @@ -46,7 +46,7 @@ public class ContestTab extends AbstractWorkspacePanel { JPanel component; JLabel workspaceLabel; - JComboBox workspaceSelection; + JComboBox workspaceSelection; JLabel nOfFilesLabel; JSpinner nOfFileSpinner; JLabel nOfBonusFilesLabel; @@ -58,29 +58,42 @@ public class ContestTab extends AbstractWorkspacePanel { } @Override - protected JComboBox getWorkspaceSelection() { + protected JComboBox getWorkspaceSelection() { return workspaceSelection; } @Override protected void initComponent() { + int contestFiles = 6; + int bonusFiles = 2; WorkspaceConfig wc = WSManager.getWorkspaceConfig(); + if (wc != null){ + contestFiles = wc.getNOfContestFiles(); + bonusFiles = wc.getNOfContestBonusFiles(); + } + component = new JPanel(); workspaceLabel = new JLabel(); - workspaceSelection = new JComboBox(); + workspaceSelection = new JComboBox<>(); nOfFilesLabel = new JLabel(); - nOfFileSpinner = new JSpinner(new SpinnerNumberModel(wc.getNOfContestFiles(), 0, 100, 1)); + nOfFileSpinner = new JSpinner(new SpinnerNumberModel(contestFiles, 0, 100, 1)); JComponent editor = new JSpinner.NumberEditor(nOfFileSpinner); nOfFileSpinner.setEditor(editor); nOfBonusFilesLabel = new JLabel(); - nOfBonusFileSpinner = new JSpinner(new SpinnerNumberModel(wc.getNOfContestBonusFiles(), 0, 100, 1)); + nOfBonusFileSpinner = new JSpinner(new SpinnerNumberModel(bonusFiles, 0, 100, 1)); JComponent editor2 = new JSpinner.NumberEditor(nOfBonusFileSpinner); nOfBonusFileSpinner.setEditor(editor2); + if (wc != null){ + enableComponents(); + } else { + disableComponents(); + } + populateWorkspaceList(); } @@ -156,14 +169,21 @@ public class ContestTab extends AbstractWorkspacePanel { nOfFileSpinner.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent arg0) { - WSManager.getWorkspaceConfig().setNOfContestFiles((Integer) nOfFileSpinner.getValue()); + WorkspaceConfig wc = WSManager.getWorkspaceConfig(); + if (wc != null) { + wc.setNOfContestFiles((Integer) nOfFileSpinner.getValue()); + } } }); nOfBonusFileSpinner.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { - WSManager.getWorkspaceConfig().setNOfContestBonusFiles((Integer) nOfBonusFileSpinner.getValue()); + WorkspaceConfig wc = WSManager.getWorkspaceConfig(); + if (wc != null){ + wc.setNOfContestBonusFiles((Integer) nOfBonusFileSpinner.getValue()); + } + } }); } @@ -178,18 +198,28 @@ public class ContestTab extends AbstractWorkspacePanel { @Override protected void setValues() { WorkspaceConfig wc = WSManager.getWorkspaceConfig(); + if (wc == null) { + nOfFileSpinner.setValue(0); + nOfBonusFileSpinner.setValue(0); + disableComponents(); + return; + } else { + enableComponents(); + } nOfFileSpinner.setValue(wc.getNOfContestFiles()); nOfBonusFileSpinner.setValue(wc.getNOfContestBonusFiles()); } @Override protected void enableComponents() { + workspaceSelection.setEnabled(true); nOfFileSpinner.setEnabled(true); nOfBonusFileSpinner.setEnabled(true); } @Override protected void disableComponents() { + workspaceSelection.setEnabled(false); nOfFileSpinner.setEnabled(false); nOfBonusFileSpinner.setEnabled(false); } diff --git a/logo/src/xlogo/gui/welcome/settings/tabs/GlobalTab.java b/logo/src/xlogo/gui/welcome/settings/tabs/GlobalTab.java index ae03255..22d72fd 100644 --- a/logo/src/xlogo/gui/welcome/settings/tabs/GlobalTab.java +++ b/logo/src/xlogo/gui/welcome/settings/tabs/GlobalTab.java @@ -156,7 +156,7 @@ public class GlobalTab extends X4SComponent { passwordField.setText(null); retypeField.setText(null); if (!selected) - WSManager.getInstance().getGlobalConfigInstance().setNewPassword(authentification, null); + WSManager.getGlobalConfig().setNewPassword(authentification, null); } }); @@ -192,7 +192,7 @@ public class GlobalTab extends X4SComponent { private void savePassword() throws IOException { - GlobalConfig gc = WSManager.getInstance().getGlobalConfigInstance(); + GlobalConfig gc = WSManager.getGlobalConfig(); if (askPasswordCb.isSelected()) { @@ -202,7 +202,6 @@ public class GlobalTab extends X4SComponent { { gc.setNewPassword(authentification, pw1); authentification = pw1; - gc.store(); } else { @@ -214,7 +213,6 @@ public class GlobalTab extends X4SComponent { { gc.setNewPassword(authentification, null); authentification = null; - gc.store(); } } diff --git a/logo/src/xlogo/gui/welcome/settings/tabs/SyntaxHighlightingTab.java b/logo/src/xlogo/gui/welcome/settings/tabs/SyntaxHighlightingTab.java index c576e73..34a2bfe 100644 --- a/logo/src/xlogo/gui/welcome/settings/tabs/SyntaxHighlightingTab.java +++ b/logo/src/xlogo/gui/welcome/settings/tabs/SyntaxHighlightingTab.java @@ -41,8 +41,10 @@ import javax.swing.JPanel; import javax.swing.JTextPane; import xlogo.AppSettings; +import xlogo.AppSettings.AppProperty; import xlogo.StyledDocument.DocumentLogo; import xlogo.gui.components.ColorStyleSelectionPanel; +import xlogo.interfaces.Observable.PropertyChangeListener; import xlogo.storage.WSManager; import xlogo.storage.workspace.SyntaxHighlightConfig; import xlogo.storage.workspace.WorkspaceConfig; @@ -51,7 +53,7 @@ public class SyntaxHighlightingTab extends AbstractWorkspacePanel{ private JPanel component; private JLabel workspaceLabel; - private JComboBox workspaceSelection; + private JComboBox workspaceSelection; private ColorStyleSelectionPanel commentStyleSelection; private ColorStyleSelectionPanel braceStyleSelection; private ColorStyleSelectionPanel primitiveStyleSelection; @@ -62,7 +64,7 @@ public class SyntaxHighlightingTab extends AbstractWorkspacePanel{ private DocumentLogo previewLogoDocument; private JTextPane previewTextPane; - private ActionListener syntaxHighlightChangeListener; + private PropertyChangeListener syntaxHighlightChangeListener; public SyntaxHighlightingTab() { super(); @@ -76,14 +78,16 @@ public class SyntaxHighlightingTab extends AbstractWorkspacePanel{ protected void initComponent() { WorkspaceConfig wc = WSManager.getWorkspaceConfig(); + SyntaxHighlightConfig syntaxHighlighting = wc != null ? wc.getSyntaxHighlightStyles() : new SyntaxHighlightConfig(); + //Font font = wc.getFont(); component = new JPanel(); workspaceLabel = new JLabel(); - workspaceSelection = new JComboBox(); - commentStyleSelection=new ColorStyleSelectionPanel(wc.getCommentColor(), wc.getCommentStyle(), translate("pref.highlight.comment")); - braceStyleSelection=new ColorStyleSelectionPanel(wc.getBraceColor(), wc.getBraceStyle(), translate("pref.highlight.parenthesis")); - primitiveStyleSelection=new ColorStyleSelectionPanel(wc.getPrimitiveColor(), wc.getPrimitiveStyle(), translate("pref.highlight.primitive")); - operandStyleSelection=new ColorStyleSelectionPanel(wc.getOperatorColor(), wc.getOperatorStyle(), translate("pref.highlight.operand")); + workspaceSelection = new JComboBox<>(); + commentStyleSelection=new ColorStyleSelectionPanel(syntaxHighlighting.getCommentColor(), syntaxHighlighting.getCommentStyle(), translate("pref.highlight.comment")); + braceStyleSelection=new ColorStyleSelectionPanel(syntaxHighlighting.getBraceColor(), syntaxHighlighting.getBraceStyle(), translate("pref.highlight.parenthesis")); + primitiveStyleSelection=new ColorStyleSelectionPanel(syntaxHighlighting.getPrimitiveColor(), syntaxHighlighting.getPrimitiveStyle(), translate("pref.highlight.primitive")); + operandStyleSelection=new ColorStyleSelectionPanel(syntaxHighlighting.getOperandColor(), syntaxHighlighting.getOperandStyle(), translate("pref.highlight.operand")); previewTextPane=new JTextPane(); activateHighlightingCheckBox = new JCheckBox(); @@ -184,13 +188,12 @@ public class SyntaxHighlightingTab extends AbstractWorkspacePanel{ setValues(); } }); - AppSettings.getInstance().addSyntaxHighlightStyleChangeListener( - syntaxHighlightChangeListener = new ActionListener() { - @Override - public void actionPerformed(ActionEvent arg0) { - updateSyntaxHighlightingPreview(); - } - }); + + syntaxHighlightChangeListener = () -> { + updateSyntaxHighlightingPreview(); + }; + + AppSettings.getInstance().addPropertyChangeListener(AppProperty.SYNTAX_HIGHLIGHTING, syntaxHighlightChangeListener); operandStyleSelection.addStyleChangeListener(new ActionListener() { @Override @@ -230,25 +233,27 @@ public class SyntaxHighlightingTab extends AbstractWorkspacePanel{ } @Override - public void stopEventListeners() - { + public void stopEventListeners() { super.stopEventListeners(); - AppSettings.getInstance().removeSyntaxHighlightStyleChangeListener(syntaxHighlightChangeListener); - + AppSettings.getInstance().removePropertyChangeListener(AppProperty.SYNTAX_HIGHLIGHTING, syntaxHighlightChangeListener); } @Override - protected JComboBox getWorkspaceSelection() { + protected JComboBox getWorkspaceSelection() { return workspaceSelection; } @Override protected void setValues() { WorkspaceConfig wc = WSManager.getWorkspaceConfig(); - commentStyleSelection.setColorAndStyle(wc.getCommentColor(), wc.getCommentStyle()); - braceStyleSelection.setColorAndStyle(wc.getBraceColor(), wc.getBraceStyle()); - primitiveStyleSelection.setColorAndStyle(wc.getPrimitiveColor(), wc.getPrimitiveStyle()); - operandStyleSelection.setColorAndStyle(wc.getOperatorColor(), wc.getOperatorStyle()); + if (wc == null){ + disableComponents(); + } else { + commentStyleSelection.setColorAndStyle(wc.getCommentColor(), wc.getCommentStyle()); + braceStyleSelection.setColorAndStyle(wc.getBraceColor(), wc.getBraceStyle()); + primitiveStyleSelection.setColorAndStyle(wc.getPrimitiveColor(), wc.getPrimitiveStyle()); + operandStyleSelection.setColorAndStyle(wc.getOperandColor(), wc.getOperandStyle()); + } updateSyntaxHighlightingPreview(); } @@ -257,6 +262,8 @@ public class SyntaxHighlightingTab extends AbstractWorkspacePanel{ { WorkspaceConfig wc = WSManager.getWorkspaceConfig(); + if (wc == null){ return; } + boolean isHighlightingEnabled = wc.isSyntaxHighlightingEnabled(); activateHighlightingCheckBox.setSelected(isHighlightingEnabled); commentStyleSelection.setEnabled(isHighlightingEnabled); @@ -270,19 +277,32 @@ public class SyntaxHighlightingTab extends AbstractWorkspacePanel{ wc.getCommentColor(), wc.getCommentStyle(), wc.getPrimitiveColor(), wc.getPrimitiveStyle(), wc.getBraceColor(), wc.getBraceStyle(), - wc.getOperatorColor(), wc.getOperatorStyle()); + wc.getOperandColor(), wc.getOperandStyle()); + previewTextPane.setText(translate("pref.highlight.example")); } @Override protected void enableComponents() { - + workspaceSelection.setEnabled(true); + activateHighlightingCheckBox.setEnabled(true); + commentStyleSelection.setEnabled(true); + primitiveStyleSelection.setEnabled(true); + braceStyleSelection.setEnabled(true); + operandStyleSelection.setEnabled(true); + restoreDefaultsButton.setEnabled(true); } @Override protected void disableComponents() { - + workspaceSelection.setEnabled(false); + activateHighlightingCheckBox.setEnabled(false); + commentStyleSelection.setEnabled(false); + primitiveStyleSelection.setEnabled(false); + braceStyleSelection.setEnabled(false); + operandStyleSelection.setEnabled(false); + restoreDefaultsButton.setEnabled(false); } } diff --git a/logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceCreationPanel.java b/logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceCreationPanel.java index c7c6e9f..086e7ce 100644 --- a/logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceCreationPanel.java +++ b/logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceCreationPanel.java @@ -40,7 +40,7 @@ import javax.swing.JPanel; import javax.swing.JTextField; import xlogo.gui.components.X4SComponent; -import xlogo.storage.WSManager; +import xlogo.storage.global.GlobalConfig; class WorkspaceCreationPanel extends X4SComponent { @@ -68,7 +68,7 @@ class WorkspaceCreationPanel extends X4SComponent locationField = new JTextField(); openFilechooserBtn = new JButton("Browse"); - locationField.setText(WSManager.getInstance().getGlobalConfigInstance().getLocation().toString()); + locationField.setText(GlobalConfig.DEFAULT_LOCATION.getAbsolutePath()); locationField.setEditable(false); } diff --git a/logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceTab.java b/logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceTab.java index ff25672..259d16d 100644 --- a/logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceTab.java +++ b/logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceTab.java @@ -1,27 +1,22 @@ -/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Loic Le Coq +/* + * XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Loic Le Coq * Copyright (C) 2013 Marko Zivkovic - * * Contact Information: marko88zivkovic at gmail dot com - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. This program 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 General - * Public License for more details. You should have received a copy of the - * GNU General Public License along with this program; if not, write to the Free - * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. This program 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 General + * Public License for more details. You should have received a copy of the + * GNU General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. - * - * * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic * during his Bachelor thesis at the computer science department of ETH Zurich, * in the year 2013 and/or during future work. - * * It is a reengineered version of XLogo written by Loic Le Coq, published * under the GPL License at http://xlogo.tuxfamily.org/ - * * Contents of this file were entirely written by Marko Zivkovic */ @@ -31,7 +26,6 @@ import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; -import java.io.IOException; import javax.swing.DefaultComboBoxModel; import javax.swing.GroupLayout; @@ -49,56 +43,53 @@ import xlogo.Logo; import xlogo.messages.async.dialog.DialogMessenger; import xlogo.storage.Storable; import xlogo.storage.WSManager; -import xlogo.storage.global.GlobalConfig; import xlogo.storage.workspace.Language; import xlogo.storage.workspace.NumberOfBackups; import xlogo.storage.workspace.WorkspaceConfig; -public class WorkspaceTab extends AbstractWorkspacePanel{ - - JPanel component; +public class WorkspaceTab extends AbstractWorkspacePanel { - JLabel workspaceLabel; - JLabel wsLocationLabel; - JLabel wsLanguageLabel; - JLabel wsBackupLabel; - JLabel userLabel; + JPanel component; - JButton addWorkspaceBtn; - JButton addUserBtn; - - JButton removeWorkspaceBtn; - JButton removeUserBtn; + JLabel workspaceLabel; + JLabel wsLocationLabel; + JLabel wsLanguageLabel; + JLabel wsBackupLabel; + JLabel userLabel; + + JButton addWorkspaceBtn; + JButton addUserBtn; - JButton importWorkspaceBtn; - JButton importUserBtn; + JButton removeWorkspaceBtn; + JButton removeUserBtn; - JComboBox workspaceSelection; - JComboBox userSelection; - JLabel wsLocation; - JFileChooser wsLocationChooser; - JComboBox languageSelection; - JComboBox nOfBackupsSelecteion; - JCheckBox userCreatable; + JButton importWorkspaceBtn; + JButton importUserBtn; + + JComboBox workspaceSelection; + JComboBox userSelection; + JLabel wsLocation; + JFileChooser wsLocationChooser; + JComboBox languageSelection; + JComboBox nOfBackupsSelecteion; + JCheckBox userCreatable; public WorkspaceTab() { super(); } @Override - public JComponent getComponent() - { + public JComponent getComponent() { return component; } - + @Override - protected JComboBox getWorkspaceSelection() { + protected JComboBox getWorkspaceSelection() { return workspaceSelection; } - + @Override - protected void initComponent() - { + protected void initComponent() { component = new JPanel(); workspaceLabel = new JLabel("Workspace: "); @@ -109,36 +100,41 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ addWorkspaceBtn = new JButton("Add"); addUserBtn = new JButton("Add"); - + removeWorkspaceBtn = new JButton("Remove"); removeUserBtn = new JButton("Remove"); importWorkspaceBtn = new JButton("Import"); importUserBtn = new JButton("Import"); - workspaceSelection = new JComboBox(); - userSelection = new JComboBox(); + workspaceSelection = new JComboBox<>(); + userSelection = new JComboBox<>(); wsLocation = new JLabel(); wsLocationChooser = new JFileChooser(); - languageSelection = new JComboBox(Language.values()); - nOfBackupsSelecteion = new JComboBox(NumberOfBackups.values()); + languageSelection = new JComboBox<>(Language.values()); + nOfBackupsSelecteion = new JComboBox<>(NumberOfBackups.values()); userCreatable = new JCheckBox("Allow the users to create new user accounts?"); populateWorkspaceList(); setValues(); + + if (WSManager.getWorkspaceConfig() == null){ + disableComponents(); + } else { + enableComponents(); + } } - + @Override - protected void layoutComponent() - { - workspaceSelection.setMinimumSize(new Dimension(150,25)); - workspaceSelection.setMaximumSize(new Dimension(150,25)); - userSelection.setMinimumSize(new Dimension(150,25)); - userSelection.setMaximumSize(new Dimension(150,25)); - languageSelection.setMinimumSize(new Dimension(150,25)); - languageSelection.setMaximumSize(new Dimension(150,25)); - nOfBackupsSelecteion.setMinimumSize(new Dimension(75,25)); - nOfBackupsSelecteion.setMaximumSize(new Dimension(75,25)); + protected void layoutComponent() { + workspaceSelection.setMinimumSize(new Dimension(150, 25)); + workspaceSelection.setMaximumSize(new Dimension(150, 25)); + userSelection.setMinimumSize(new Dimension(150, 25)); + userSelection.setMaximumSize(new Dimension(150, 25)); + languageSelection.setMinimumSize(new Dimension(150, 25)); + languageSelection.setMaximumSize(new Dimension(150, 25)); + nOfBackupsSelecteion.setMinimumSize(new Dimension(75, 25)); + nOfBackupsSelecteion.setMaximumSize(new Dimension(75, 25)); component.add(workspaceLabel); component.add(wsLocationLabel); @@ -152,7 +148,7 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ component.add(removeUserBtn); component.add(importWorkspaceBtn); component.add(importUserBtn); - + component.add(workspaceSelection); component.add(userSelection); component.add(wsLocation); @@ -165,82 +161,73 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ groupLayout.setAutoCreateGaps(true); groupLayout.setAutoCreateContainerGaps(true); - groupLayout.setVerticalGroup( - groupLayout.createSequentialGroup() - .addGroup(groupLayout.createParallelGroup() - .addComponent(workspaceLabel) - .addComponent(workspaceSelection) - .addComponent(addWorkspaceBtn) - .addComponent(removeWorkspaceBtn) - .addComponent(importWorkspaceBtn)) - .addGroup(groupLayout.createParallelGroup() - .addComponent(wsLocationLabel) - .addComponent(wsLocation)) - .addGroup(groupLayout.createParallelGroup() - .addComponent(userLabel) - .addComponent(userSelection) - .addComponent(addUserBtn) - .addComponent(removeUserBtn) - .addComponent(importUserBtn)) - .addGroup(groupLayout.createParallelGroup() - .addComponent(wsLanguageLabel) - .addComponent(languageSelection)) - .addGroup(groupLayout.createParallelGroup() - .addComponent(wsBackupLabel) - .addComponent(nOfBackupsSelecteion)) - .addGroup(groupLayout.createParallelGroup() - .addComponent(userCreatable)) - ); - - groupLayout.setHorizontalGroup( - groupLayout.createParallelGroup() - .addGroup(groupLayout.createSequentialGroup() - .addGroup(groupLayout.createParallelGroup() - .addComponent(workspaceLabel) - .addComponent(userLabel) - .addComponent(wsLocationLabel) - .addComponent(wsLanguageLabel) - .addComponent(wsBackupLabel) - ) - .addGroup(groupLayout.createParallelGroup() - .addGroup(groupLayout.createSequentialGroup() - .addGroup(groupLayout.createParallelGroup() - .addComponent(workspaceSelection) - .addComponent(userSelection) - ) - .addGroup(groupLayout.createParallelGroup() - .addComponent(addWorkspaceBtn) - .addComponent(addUserBtn) - ) - .addGroup(groupLayout.createParallelGroup() - .addComponent(removeWorkspaceBtn) - .addComponent(removeUserBtn) - ) - .addGroup(groupLayout.createParallelGroup() - .addComponent(importWorkspaceBtn) - .addComponent(importUserBtn) - ) - ) - .addComponent(wsLocation) - .addComponent(languageSelection) - .addComponent(nOfBackupsSelecteion) - ) - ) - .addComponent(userCreatable) - ); + groupLayout + .setVerticalGroup(groupLayout + .createSequentialGroup() + .addGroup( + groupLayout.createParallelGroup().addComponent(workspaceLabel) + .addComponent(workspaceSelection).addComponent(addWorkspaceBtn) + .addComponent(removeWorkspaceBtn).addComponent(importWorkspaceBtn)) + .addGroup( + groupLayout.createParallelGroup().addComponent(wsLocationLabel) + .addComponent(wsLocation)) + .addGroup( + groupLayout.createParallelGroup().addComponent(userLabel).addComponent(userSelection) + .addComponent(addUserBtn).addComponent(removeUserBtn) + .addComponent(importUserBtn)) + .addGroup( + groupLayout.createParallelGroup().addComponent(wsLanguageLabel) + .addComponent(languageSelection)) + .addGroup( + groupLayout.createParallelGroup().addComponent(wsBackupLabel) + .addComponent(nOfBackupsSelecteion)) + .addGroup(groupLayout.createParallelGroup().addComponent(userCreatable))); + + groupLayout.setHorizontalGroup(groupLayout + .createParallelGroup() + .addGroup( + groupLayout + .createSequentialGroup() + .addGroup( + groupLayout.createParallelGroup().addComponent(workspaceLabel) + .addComponent(userLabel).addComponent(wsLocationLabel) + .addComponent(wsLanguageLabel).addComponent(wsBackupLabel)) + .addGroup( + groupLayout + .createParallelGroup() + .addGroup( + groupLayout + .createSequentialGroup() + .addGroup( + groupLayout.createParallelGroup() + .addComponent(workspaceSelection) + .addComponent(userSelection)) + .addGroup( + groupLayout.createParallelGroup() + .addComponent(addWorkspaceBtn) + .addComponent(addUserBtn)) + .addGroup( + groupLayout.createParallelGroup() + .addComponent(removeWorkspaceBtn) + .addComponent(removeUserBtn)) + .addGroup( + groupLayout.createParallelGroup() + .addComponent(importWorkspaceBtn) + .addComponent(importUserBtn))) + .addComponent(wsLocation).addComponent(languageSelection) + .addComponent(nOfBackupsSelecteion))).addComponent(userCreatable)); } @Override - protected void initEventListeners() - { + protected void initEventListeners() { /* * WORKSPACE [SELECT in super class]; ADD; REMOVE; IMPORT */ super.initEventListeners(); - - addWorkspaceBtn.addActionListener(new ActionListener() { + + addWorkspaceBtn.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { - new Thread(new Runnable() { + new Thread(new Runnable(){ public void run() { addWorkspace(); } @@ -248,9 +235,9 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ } }); - removeWorkspaceBtn.addActionListener(new ActionListener() { + removeWorkspaceBtn.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { - new Thread(new Runnable() { + new Thread(new Runnable(){ public void run() { deleteWorkspace(); } @@ -258,9 +245,9 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ } }); - importWorkspaceBtn.addActionListener(new ActionListener() { + importWorkspaceBtn.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { - new Thread(new Runnable() { + new Thread(new Runnable(){ public void run() { importWorkspace(); } @@ -272,9 +259,9 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ * USER ADD; REMOVE; IMPORT */ - addUserBtn.addActionListener(new ActionListener() { + addUserBtn.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { - new Thread(new Runnable() { + new Thread(new Runnable(){ public void run() { addUser(); } @@ -282,9 +269,9 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ } }); - removeUserBtn.addActionListener(new ActionListener() { + removeUserBtn.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { - new Thread(new Runnable() { + new Thread(new Runnable(){ public void run() { removeUser(); } @@ -292,9 +279,9 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ } }); - importUserBtn.addActionListener(new ActionListener() { + importUserBtn.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { - new Thread(new Runnable() { + new Thread(new Runnable(){ public void run() { importUser(); } @@ -306,9 +293,9 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ * LANGUAGE */ - languageSelection.addActionListener(new ActionListener() { + languageSelection.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { - new Thread(new Runnable() { + new Thread(new Runnable(){ public void run() { Language lang = (Language) languageSelection.getSelectedItem(); changeLanguage(lang); @@ -321,7 +308,7 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ * BACKUP VERSIONS */ - nOfBackupsSelecteion.addActionListener(new ActionListener() { + nOfBackupsSelecteion.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent arg0) { NumberOfBackups n = (NumberOfBackups) nOfBackupsSelecteion.getSelectedItem(); WSManager.getInstance().getWorkspaceConfigInstance().setNumberOfBackups(n); @@ -332,10 +319,13 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ * USER CREATION */ - userCreatable.addChangeListener(new ChangeListener() { + userCreatable.addChangeListener(new ChangeListener(){ public void stateChanged(ChangeEvent arg0) { boolean isAllowed = userCreatable.isSelected(); - WSManager.getInstance().getWorkspaceConfigInstance().setAllowUserCreation(isAllowed); + WorkspaceConfig wc = WSManager.getWorkspaceConfig(); + if (wc != null) { + wc.setUserCreationAllowed(isAllowed); + } } }); } @@ -347,23 +337,17 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ /** * On creation or when a workspace is set, the input elements must be set according to the current workspace properties. */ - protected void setValues() - { - WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance(); - GlobalConfig gc = WSManager.getInstance().getGlobalConfigInstance(); + protected void setValues() { + WorkspaceConfig wc = WSManager.getWorkspaceConfig(); // location text - if (wc == null) - { - String wsName = (String) workspaceSelection.getSelectedItem(); - wsLocation.setText(gc.getWorkspaceDirectory(wsName).toString()); - wsLocationLabel.setText(Logo.messages.getString("ws.settings.damaged")); - disableComponents(); + if (wc == null) { + wsLocation.setText("-"); return; - }else if(wc.isVirtual()) - wsLocation.setText(Logo.messages.getString("ws.settings.virtual.ws.not.stored")); - else - wsLocation.setText(wc.getLocation().toString()); + } + else { + wsLocation.setText(wc.getDirectory().toString()); + } // user list populateUserList(); @@ -383,14 +367,14 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ * Disable controls that depend on the workspace and that cannot be used with a virtual workspace * or a workspace that could not be loaded */ - protected void disableComponents() - { - WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance(); - removeWorkspaceBtn.setEnabled(wc == null); + protected void disableComponents() { + workspaceSelection.setEnabled(false); + removeWorkspaceBtn.setEnabled(false); userSelection.setEnabled(false); addUserBtn.setEnabled(false); removeUserBtn.setEnabled(false); importUserBtn.setEnabled(false); + languageSelection.setEnabled(false); nOfBackupsSelecteion.setEnabled(false); userCreatable.setEnabled(false); } @@ -399,13 +383,14 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ /** * Enable if Workspace is successfully entered and if it is not virtual. */ - protected void enableComponents() - { + protected void enableComponents() { + workspaceSelection.setEnabled(true); removeWorkspaceBtn.setEnabled(true); userSelection.setEnabled(true); addUserBtn.setEnabled(true); removeUserBtn.setEnabled(true); importUserBtn.setEnabled(true); + languageSelection.setEnabled(true); nOfBackupsSelecteion.setEnabled(true); userCreatable.setEnabled(true); } @@ -414,32 +399,28 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ * USER : ADD, REMOVE, IMPORT * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - private void populateUserList() - { + private void populateUserList() { WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance(); String[] users = wc.getUserList(); - userSelection.setModel(new DefaultComboBoxModel(users)); - try { - wc.enterInitialUserSpace(); - String lastUser = wc.getLastActiveUser(); - userSelection.setSelectedItem(lastUser); - } - catch (IOException ignore) { - } + userSelection.setModel(new DefaultComboBoxModel(users)); + //try { + //wc.enterInitialUserSpace(); // TODO maybe we still need this? + String lastUser = wc.getLastActiveUser(); + userSelection.setSelectedItem(lastUser); + //} + //catch (IOException ignore) { + //} } - private void addUser() - { - String username = getUserText(Logo.messages.getString("ws.settings.enter.user.name"), Logo.messages.getString("ws.settings.create.new.user")); - + private void addUser() { + String username = getUserText(Logo.messages.getString("ws.settings.enter.user.name"), + Logo.messages.getString("ws.settings.create.new.user")); + if ((username == null) || (username.length() == 0)) return; - if (WSManager.getInstance().getWorkspaceConfigInstance().existsUserLogically(username)) - { - DialogMessenger.getInstance().dispatchMessage( - Logo.messages.getString("ws.error.title"), + if (WSManager.getInstance().getWorkspaceConfigInstance().existsUserLogically(username)) { + DialogMessenger.getInstance().dispatchMessage(Logo.messages.getString("ws.error.title"), Logo.messages.getString("ws.settings.user.exists.already")); return; } @@ -448,18 +429,15 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ populateUserList(); } - private void removeUser() - { + private void removeUser() { WSManager wsManager = WSManager.getInstance(); WorkspaceConfig wc = wsManager.getWorkspaceConfigInstance(); - + String username = (String) userSelection.getSelectedItem(); if (username == null) return; String userDirectory = wc.getUserDirectroy(username).toString(); - String message = - Logo.messages.getString("ws.settings.want.delete.dir.1") - + userDirectory + String message = Logo.messages.getString("ws.settings.want.delete.dir.1") + userDirectory + Logo.messages.getString("ws.settings.want.delete.dir.1"); boolean ans = getUserYesOrNo(message, Logo.messages.getString("ws.settings.remove.user")); @@ -469,32 +447,27 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ populateUserList(); } - private void importUser() - { + private void importUser() { File dir = getUserSelectedDirectory(); if (dir == null) return; - - if (!WSManager.isUserDirectory(dir)) - { - DialogMessenger.getInstance().dispatchMessage( - Logo.messages.getString("i.am.sorry"), + + if (!WSManager.isUserDirectory(dir)) { + DialogMessenger.getInstance().dispatchMessage(Logo.messages.getString("i.am.sorry"), dir.toString() + Logo.messages.getString("ws.settings.not.legal.user.dir")); return; } String newName = dir.getName(); WorkspaceConfig wc = WSManager.getWorkspaceConfig(); - if (dir.equals(wc.getUserDirectroy(newName))) - { + if (dir.equals(wc.getUserDirectroy(newName))) { DialogMessenger.getInstance().dispatchMessage("This user was already in the list."); return; } - - while (wc.existsUserLogically(newName) || !Storable.checkLegalName(newName)) - { - String msg = wc.existsUserLogically(newName) ? - "The user name " + newName + " already exists. Please choose a new name." + + while (wc.existsUserLogically(newName) || !Storable.checkLegalName(newName)) { + String msg = wc.existsUserLogically(newName) ? "The user name " + newName + + " already exists. Please choose a new name." : "The chosen name contains illegal characters. Please choose a new name."; newName = getUserText(msg, "Name Conflict"); @@ -504,9 +477,9 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ try { WSManager.getInstance().importUser(dir, newName); - } catch (Exception e) { - DialogMessenger.getInstance().dispatchMessage( - Logo.messages.getString("ws.error.title"), + } + catch (Exception e) { + DialogMessenger.getInstance().dispatchMessage(Logo.messages.getString("ws.error.title"), Logo.messages.getString("ws.settings.could.not.import.user") + e.toString()); } populateUserList(); @@ -519,14 +492,12 @@ public class WorkspaceTab extends AbstractWorkspacePanel{ private void changeLanguage(Language lang) { WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance(); - if (wc.getLanguage() != lang) - { + if (wc.getLanguage() != lang) { wc.setLanguage(lang); } } - protected void setText() - { + protected void setText() { workspaceLabel.setText(Logo.messages.getString("ws.settings.workspace")); wsLocationLabel.setText(Logo.messages.getString("ws.settings.location")); wsLanguageLabel.setText(Logo.messages.getString("ws.settings.language")); diff --git a/logo/src/xlogo/interfaces/Observable.java b/logo/src/xlogo/interfaces/Observable.java new file mode 100644 index 0000000..3c3e526 --- /dev/null +++ b/logo/src/xlogo/interfaces/Observable.java @@ -0,0 +1,31 @@ +package xlogo.interfaces; + +/** Implementation Template

+ +private transient HashMap> listeners = new HashMap<>();

+ +public void addPropertyChangeListener(String property, PropertyChangeListener listener){
+ List list = null;
+ if (listeners.containsKey(property)){
+ list = listeners.get(property);
+ } else {
+ list = new ArrayList();
+ listeners.put(property, list);
+ }
+ list.add(listener);
+}
+
+ */ +public interface Observable> { + + /** + * @param property - if null, trigger for all properties + * @param listener + */ + public void addPropertyChangeListener(E property, PropertyChangeListener listener); + public void removePropertyChangeListener(E property, PropertyChangeListener listener); + + public interface PropertyChangeListener { + public void propertyChanged(); + } +} diff --git a/logo/src/xlogo/interfaces/PropertyChangePublisher.java b/logo/src/xlogo/interfaces/PropertyChangePublisher.java new file mode 100644 index 0000000..a953b8c --- /dev/null +++ b/logo/src/xlogo/interfaces/PropertyChangePublisher.java @@ -0,0 +1,67 @@ +package xlogo.interfaces; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class PropertyChangePublisher> implements Observable, Serializable { + + private static final long serialVersionUID = -1042512850782413412L; + + private boolean enableEvents = true; + + private final HashMap> listeners = new HashMap<>(); + private final List allPropertiesListeners = new ArrayList<>(); + + @Override + public void addPropertyChangeListener(E property, PropertyChangeListener listener) { + if (listener == null){ + throw new IllegalArgumentException("Cannot add an event listeners that is null."); + } + + if (property == null){ + allPropertiesListeners.add(listener); + return; + } + List list = listeners.get(property); + if (list == null) { + list = new ArrayList<>(); + listeners.put(property, list); + } + list.add(listener); + } + + @Override + public void removePropertyChangeListener(E property, PropertyChangeListener listener) { + if (listener == null){ + throw new IllegalArgumentException("Cannot remove an event listeners that is null."); + } + + if (property == null){ + allPropertiesListeners.remove(listener); + return; + } + + List list = listeners.get(property); + if(list != null){ + list.remove(listener); + } + } + + public void publishEvent(E property){ + if (!enableEvents){ + return; + } + setEnableEvents(false); + if (listeners.containsKey(property)) + listeners.get(property).forEach((listener) -> listener.propertyChanged()); + allPropertiesListeners.forEach((listener) -> listener.propertyChanged()); + setEnableEvents(true); + } + + public void setEnableEvents(boolean enableEvents) { + this.enableEvents = enableEvents; + } + +} diff --git a/logo/src/xlogo/kernel/userspace/context/LogoContext.java b/logo/src/xlogo/kernel/userspace/context/LogoContext.java index 6c4f979..218f91c 100644 --- a/logo/src/xlogo/kernel/userspace/context/LogoContext.java +++ b/logo/src/xlogo/kernel/userspace/context/LogoContext.java @@ -215,7 +215,7 @@ public class LogoContext files.remove(oldName); files.put(newName, file); - file.setFileName(newName); + file.setPlainName(newName); } private final ArrayList procedureMapListener = new ArrayList(); diff --git a/logo/src/xlogo/kernel/userspace/context/UserContext.java b/logo/src/xlogo/kernel/userspace/context/UserContext.java index cfa21d8..cf82a37 100644 --- a/logo/src/xlogo/kernel/userspace/context/UserContext.java +++ b/logo/src/xlogo/kernel/userspace/context/UserContext.java @@ -60,10 +60,7 @@ public class UserContext extends LogoContext private void loadUserFiles() { UserConfig userConfig = WSManager.getUserConfig(); - - if (userConfig.isVirtual()) - return; - + File sourceDir = userConfig.getSourceDirectory(); if (!sourceDir.exists()) sourceDir.mkdirs(); @@ -122,18 +119,10 @@ public class UserContext extends LogoContext public void createFile(String fileName, String text) throws IOException { /* - * Eager creation of files in file order list in user config. + * Eager creation of files in file-order list in user config. */ - if (!WSManager.getUserConfig().isVirtual()) - super.createFile(fileName, text); - else - { - LogoFile file = LogoFile.createNewVirtualFile(fileName); - file.setText(text); - getFilesTable().put(fileName, file); - installListeners(file); - } + super.createFile(fileName, text); WSManager.getUserConfig().addFile(fileName); } diff --git a/logo/src/xlogo/kernel/userspace/files/LogoFile.java b/logo/src/xlogo/kernel/userspace/files/LogoFile.java index 693e3f3..4575f73 100644 --- a/logo/src/xlogo/kernel/userspace/files/LogoFile.java +++ b/logo/src/xlogo/kernel/userspace/files/LogoFile.java @@ -62,7 +62,7 @@ import xlogo.utils.Utils; * This class holds the text file a user entered in the editor. * It analyzes the text and maintains a symbol table for all defined procedures that live within it. *

- * The file does never store itself implicitly, except for when it is created using {@link #createNewFile(String)} or renamed using {@link #setFileName(String)} + * The file does never store itself implicitly, except for when it is created using {@link #createNewFile(String)} or renamed using {@link #setPlainName(String)} * In every other case, {@link #store()}} or {@link #storeCopyToFile(File)}} must be invoked explicitly. *

* The file's text can be set using {@link #setTextFromReader(BufferedReader)}} (preferred) or {@link #setText(String)}}. @@ -116,9 +116,7 @@ public class LogoFile extends StorableDocument implements ExecutablesContainer, { super(); this.userConfig = WSManager.getUserConfig(); - if (!userConfig.isVirtual()) - setLocation(userConfig.getSourceDirectory()); - setFileName(fileName); + setPlainName(fileName); executables = new HashMap(); allProcedures = new ArrayList(); } @@ -164,8 +162,6 @@ public class LogoFile extends StorableDocument implements ExecutablesContainer, String text = Utils.readLogoFile(path.toString()); LogoFile file = new LogoFile(fileName); file.setText(text); - if (userConfig.isVirtual()) - file.makeVirtual(); return file; } @@ -183,8 +179,6 @@ public class LogoFile extends StorableDocument implements ExecutablesContainer, String text = Utils.readLogoFile(path.toString()); LogoFile file = new LogoFile(newFileName); file.setText(text); - if (WSManager.getUserConfig().isVirtual()) - file.makeVirtual(); file.store(); return file; } @@ -200,7 +194,7 @@ public class LogoFile extends StorableDocument implements ExecutablesContainer, * @param newFileName - without extension */ @Override - public void setFileName(String newFileName) + public void setPlainName(String newFileName) { logger.trace("Renaming file " + getPlainName() + " to " + newFileName); @@ -221,7 +215,7 @@ public class LogoFile extends StorableDocument implements ExecutablesContainer, } String oldPlainName = getPlainName(); - super.setFileName(newFileName); + super.setPlainName(newFileName); String newPlainName = getPlainName(); if (oldPlainName != null) @@ -271,11 +265,9 @@ public class LogoFile extends StorableDocument implements ExecutablesContainer, * and another copy in the backup folder, if this is required by {@link WorkspaceConfig#getNumberOfBackups()}. */ @Override - public void store() throws IOException + public void store() { super.store(); - if (isVirtual()) - return; doBackup(); } @@ -299,8 +291,10 @@ public class LogoFile extends StorableDocument implements ExecutablesContainer, * defined by {@link WorkspaceConfig#getNumberOfBackups()}} * @throws IOException */ - private void doBackup() throws IOException + private void doBackup() { + if (isVirtual()) + return; logger.trace("Creating backup file of current version of " + getPlainName()); WorkspaceConfig wc = WSManager.getInstance().getWorkspaceConfigInstance(); @@ -311,8 +305,12 @@ public class LogoFile extends StorableDocument implements ExecutablesContainer, if (!backupFolder.exists()) backupFolder.mkdirs(); - if (nob != NumberOfBackups.NO_BACKUPS) - storeCopyToFile(backupFile); + if (nob != NumberOfBackups.NO_BACKUPS) { + try { + storeCopyToFile(backupFile); + } + catch (Exception ignore) { } + } if (nob == NumberOfBackups.INFINITE) return; diff --git a/logo/src/xlogo/kernel/userspace/files/RecordFile.java b/logo/src/xlogo/kernel/userspace/files/RecordFile.java index ed89646..84c6d51 100644 --- a/logo/src/xlogo/kernel/userspace/files/RecordFile.java +++ b/logo/src/xlogo/kernel/userspace/files/RecordFile.java @@ -126,7 +126,7 @@ public class RecordFile extends LogoFile implements MessageBroadcaster totalMillis += now.getTime() - last.getTime(); last = now; - String time = UserConfig.getMinSec(totalMillis); + String time = Utils.getMinSec(totalMillis); String fileName = getPlainName(); for(MessageListener listener : timerEventListeners) @@ -203,9 +203,9 @@ public class RecordFile extends LogoFile implements MessageBroadcaster private String getTimeStampHeader(long totalTime, long lastEditStarted, long lastEditEnded) { - String tot = UserConfig.getMinSec(totalTime); - String lastStart = UserConfig.getTimeString(lastEditStarted); - String now = UserConfig.getTimeString(lastEditEnded); + String tot = Utils.getMinSec(totalTime); + String lastStart = Utils.getTimeString(lastEditStarted); + String now = Utils.getTimeString(lastEditEnded); return "# Total Time : " + tot + "\n# Edited from : " + lastStart + "\n# Until : " + now + "\n\n"; } @@ -222,7 +222,7 @@ public class RecordFile extends LogoFile implements MessageBroadcaster if(listener == null) throw new IllegalArgumentException("Listener must not be null."); timerEventListeners.add(listener); - listener.messageEvent(getPlainName(), UserConfig.getMinSec(totalMillis)); + listener.messageEvent(getPlainName(), Utils.getMinSec(totalMillis)); } @Override diff --git a/logo/src/xlogo/kernel/userspace/procedures/Procedure.java b/logo/src/xlogo/kernel/userspace/procedures/Procedure.java index f96975c..2e0ec38 100644 --- a/logo/src/xlogo/kernel/userspace/procedures/Procedure.java +++ b/logo/src/xlogo/kernel/userspace/procedures/Procedure.java @@ -283,18 +283,10 @@ public class Procedure instr.append("\\l"); instr.append(lineNumber); instr.append(" "); - while (bfr.ready()) + while (bfr.ready() && (line = bfr.readLine()) != null) { lineNumber++; - // read the line - try - { - line = bfr.readLine().trim(); - } - catch (NullPointerException e1) - { - break; - } + line = line.trim(); // delete comments line = deleteComments(line); line = Utils.decoupe(line).toString().trim(); diff --git a/logo/src/xlogo/storage/JSONSerializer.java b/logo/src/xlogo/storage/JSONSerializer.java new file mode 100644 index 0000000..2766da4 --- /dev/null +++ b/logo/src/xlogo/storage/JSONSerializer.java @@ -0,0 +1,21 @@ +package xlogo.storage; + +import org.json.JSONObject; + +import xlogo.storage.workspace.Serializer; + +public abstract class JSONSerializer implements Serializer{ + public abstract JSONObject serialize2JSON(T object); + + public abstract T deserialize(JSONObject json); + + @Override + public T deserialize(String json) { + return deserialize(new JSONObject(json)); + } + + @Override + public String serialize2String(T target) { + return serialize2JSON(target).toString(); + } +} diff --git a/logo/src/xlogo/storage/Storable.java b/logo/src/xlogo/storage/Storable.java index 97e19a3..a3d57b3 100644 --- a/logo/src/xlogo/storage/Storable.java +++ b/logo/src/xlogo/storage/Storable.java @@ -47,15 +47,26 @@ public abstract class Storable implements Serializable { private File location; /** - * Dirty : an object is dirty if it was changed since it was loaded or stored the last time. + * Dirty : an object is dirty if it was changed since it was created, loaded or stored the last time. */ private transient boolean dirty = true; + /** + * If true, the storable will only be stored after it has been marked dirty (thus, if an explicit change happened to it) + */ + private boolean isStoringDeferred = false; + /** * Will not be stored if virtual. */ private transient boolean isVirtual = false; + /** + * @see #isPersisted() + */ + private boolean isPersisted = false; + + /* * PATH BUILDERS */ @@ -73,11 +84,32 @@ public abstract class Storable implements Serializable { */ /** - * Store this object to the file specified by {@link #getFilePath()} if it is dirty + * Store this object to the file specified by {@link #getFilePath()} if it is dirty and storing is not deferred, but only if it is not virtual. * @throws IOException */ - public abstract void store() throws IOException; + public void store() { + if (!isStoringDeferred()){ + if (isDirty() && !isVirtual()){ + setPersisted(false); + try { + File file = getFilePath(); + mkParentDirs(file); + storeCopyToFile(file); + } + catch (IOException ignore) { } + setPersisted(true); + } + } else { + setStoringDeferred(false); + } + } + /** + * Persist the contents of this {@link Storable} to the specified file. + * @param file + * @throws IOException + * @throws IllegalArgumentException + */ public abstract void storeCopyToFile(File file) throws IOException, IllegalArgumentException; /** @@ -93,8 +125,12 @@ public abstract class Storable implements Serializable { public abstract String getFileNameExtension(); + public String getFileNamePrefix(){ + return ""; + } + public String getFileName() { - return getPlainName() + getFileNameExtension(); + return getFileNamePrefix() + getPlainName() + getFileNameExtension(); } /** @@ -107,16 +143,16 @@ public abstract class Storable implements Serializable { /** * If this exists on the file system, that file will be renamed.

* If newFileName already existed, it is deleted first. - * @param newFileName - * @throws IllegalArgumentException - If the provided name is not legal. + * @param newFileName - must not be null and must satisfy {@link #checkLegalName(String)} + * @throws IllegalArgumentException - If the provided name is not legal or null. */ - public void setFileName(String newFileName) throws IllegalArgumentException //TODO make sure callers conform with contract + public void setPlainName(String newFileName) throws IllegalArgumentException { if (newFileName == null || newFileName.length() == 0) throw new IllegalArgumentException("File name must not be null or empty."); if (!checkLegalName(newFileName)) - throw new IllegalArgumentException("The chose file name contains illegal characters."); + throw new IllegalArgumentException("The chosen file name contains illegal characters."); String ext = getFileNameExtension(); String oldName = getPlainName(); @@ -154,14 +190,16 @@ public abstract class Storable implements Serializable { /** * To set null or a file that is not a directory or a directory with no write permissions is an error, as long as this is not virtual.
* Setting location has no effect if this is virtual.
- * @param location - the directory where this should be stored to. + * @param location - the directory where this should be stored to. If null, location will be set to user home directory * @throws IOException * @throws IOException If the specified location is not a directory or no write permissions exist, or the chosen name is not legal. */ public void setLocation(File location) throws IllegalArgumentException { if (isVirtual) { return; } - if (location == null) { throw new IllegalArgumentException("Location must not be null."); } + if (location == null) { + location = new File(System.getProperty("user.home")); + } this.location = location; makeDirty(); @@ -225,6 +263,7 @@ public abstract class Storable implements Serializable { */ protected void makeDirty() { dirty = true; + setStoringDeferred(false); } /** @@ -235,6 +274,18 @@ public abstract class Storable implements Serializable { dirty = false; } + /* + * Deferred + */ + + public boolean isStoringDeferred() { + return isStoringDeferred; + } + + public void setStoringDeferred(boolean isDeferred) { + this.isStoringDeferred = isDeferred; + } + /* * isVirtual */ @@ -255,6 +306,26 @@ public abstract class Storable implements Serializable { return isVirtual; } + /* + * isPersisted + */ + + /** + * @see #isPersisted() + * @param isPersisted + */ + protected void setPersisted(boolean isPersisted){ + this.isPersisted = isPersisted; + } + + /** + * Whether the last attempt to store or load the object was successful + * @return + */ + public boolean isPersisted(){ + return isPersisted; + } + // The best I found : http://stackoverflow.com/questions/893977/java-how-to-find-out-whether-a-file-name-is-valid // some windows specific chars are not contained... public static final String ILLEGAL_NAME_CHARACTERS = "/\n\r\t\0\f`?*\\<>|\":"; diff --git a/logo/src/xlogo/storage/StorableDocument.java b/logo/src/xlogo/storage/StorableDocument.java index 0551ff9..0f13e29 100644 --- a/logo/src/xlogo/storage/StorableDocument.java +++ b/logo/src/xlogo/storage/StorableDocument.java @@ -1,27 +1,22 @@ -/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Loic Le Coq +/* + * XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Loic Le Coq * Copyright (C) 2013 Marko Zivkovic - * * Contact Information: marko88zivkovic at gmail dot com - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. This program 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 General - * Public License for more details. You should have received a copy of the - * GNU General Public License along with this program; if not, write to the Free - * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. This program 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 General + * Public License for more details. You should have received a copy of the + * GNU General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. - * - * * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic * during his Bachelor thesis at the computer science department of ETH Zurich, * in the year 2013 and/or during future work. - * * It is a reengineered version of XLogo written by Loic Le Coq, published * under the GPL License at http://xlogo.tuxfamily.org/ - * * Contents of this file were entirely written by Marko Zivkovic */ @@ -43,38 +38,35 @@ import org.apache.logging.log4j.Logger; import xlogo.storage.user.UserConfig; import xlogo.storage.workspace.WorkspaceConfig; -public abstract class StorableDocument extends Storable -{ +public abstract class StorableDocument extends Storable { /** * */ private static final long serialVersionUID = 8218323197066522297L; - private static Logger logger = LogManager.getLogger(StorableDocument.class.getSimpleName()); - + private static Logger logger = LogManager.getLogger(StorableDocument.class.getSimpleName()); + /** * Contents of the file */ - private String text; + private String text; /** * DEFINE TIME */ - private Calendar lastSync; + private Calendar lastSync; /** * When this was created or saved the last time using {@link #store()}. * Other store methods will not affect the time. * @return */ - public Calendar getLastSync() - { + public Calendar getLastSync() { return lastSync; } - public StorableDocument() - { + public StorableDocument() { super(); text = ""; synced(); @@ -85,26 +77,15 @@ public abstract class StorableDocument extends Storable * and it also stores a copy in the backup folder, if this is required by {@link WorkspaceConfig#getNumberOfBackups()}. */ @Override - public void store() throws IOException - { + public void store() { synced(); - if (isVirtual()) - return; - - File file = getFilePath(); - logger.trace("Storing document: " + file.getAbsolutePath()); - - if (!file.getParentFile().exists()) - file.getParentFile().mkdirs(); - - storeCopyToFile(file); + super.store(); } - protected void synced() - { + protected void synced() { lastSync = Calendar.getInstance(); } - + /** * * This is the counterpart to {@link #openFromAnyFile(UserConfig, File)}
@@ -112,11 +93,9 @@ public abstract class StorableDocument extends Storable * This works, even if the file is declared virtual. */ @Override - public void storeCopyToFile(File file) throws IOException, IllegalArgumentException - { + public void storeCopyToFile(File file) throws IOException, IllegalArgumentException { logger.trace("Storing copy of " + getFileName() + " to " + file.getAbsolutePath()); - try - { + try { mkParentDirs(file); FileOutputStream f = new FileOutputStream(file); BufferedOutputStream b = new BufferedOutputStream(f); @@ -127,8 +106,7 @@ public abstract class StorableDocument extends Storable f.close(); } - catch (FileNotFoundException e1) - { + catch (FileNotFoundException e1) { e1.printStackTrace(); } } @@ -136,8 +114,7 @@ public abstract class StorableDocument extends Storable /** * Deletes the current file path */ - public void delete() - { + public void delete() { if (isVirtual()) return; File file = getFilePath(); @@ -145,8 +122,7 @@ public abstract class StorableDocument extends Storable file.delete(); } - public String getText() - { + public String getText() { if (text == null) text = generateText(); return text; @@ -165,10 +141,9 @@ public abstract class StorableDocument extends Storable * If possible, use {@link #setTextFromReader(BufferedReader)} for performance reasons. * @param text */ - public void setText(String text) - { + public void setText(String text) { invalidateText(); - if(text == null) + if (text == null) return; String replIndent = text.replaceAll("\t", " "); @@ -181,7 +156,7 @@ public abstract class StorableDocument extends Storable * Whether the underlying text representation is the empty string * @return */ - public boolean isEmpty(){ + public boolean isEmpty() { return getText().equals(""); } @@ -190,8 +165,7 @@ public abstract class StorableDocument extends Storable * The new text is then parsed to the concrete document structure. * @param br */ - public void setTextFromReader(BufferedReader br) - { + public void setTextFromReader(BufferedReader br) { invalidateText(); parseText(br); } @@ -207,8 +181,7 @@ public abstract class StorableDocument extends Storable * Call this whenever the internal object structure has changed and it should be serialized first, using {@link #generateText()}, * when {@link #getText()} is called the next time. */ - protected void invalidateText() - { + protected void invalidateText() { this.text = null; } } diff --git a/logo/src/xlogo/storage/StorableObject.java b/logo/src/xlogo/storage/StorableObject.java index f48665f..4742673 100644 --- a/logo/src/xlogo/storage/StorableObject.java +++ b/logo/src/xlogo/storage/StorableObject.java @@ -2,7 +2,6 @@ * XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Loic Le Coq * Copyright (C) 2013 Marko Zivkovic * Contact Information: marko88zivkovic at gmail dot com - * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) @@ -13,39 +12,52 @@ * GNU General Public License along with this program; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. - * * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic * during his Bachelor thesis at the computer science department of ETH Zurich, * in the year 2013 and/or during future work. - * * It is a reengineered version of XLogo written by Loic Le Coq, published * under the GPL License at http://xlogo.tuxfamily.org/ - * * Contents of this file were entirely written by Marko Zivkovic */ package xlogo.storage; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import xlogo.interfaces.Observable; +import xlogo.interfaces.Observable.PropertyChangeListener; +import xlogo.storage.workspace.Serializer; +import xlogo.utils.Utils; + /** - * The base class for anything that must be stored persistently. + * The container class for anything that must be stored persistently. * @author Marko Zivkovic */ -public abstract class StorableObject extends Storable implements Serializable { +public class StorableObject, E extends Enum> extends Storable { + + private static final long serialVersionUID = -3394846264634066188L; + private static Logger logger = LogManager.getLogger(StorableObject.class.getSimpleName()); + + public static final String DEFAULT_PLAIN_NAME_PREFIX = "X4S_"; + public static final File DEFAULT_LOCATION = new File(System.getProperty("user.home")); - private static final long serialVersionUID = -1738873382662156052L; + private Class targetClass = null; + private T object = null; + private Serializer serializer = null; + private Initializer creationInitilizer = null; + private Initializer loadInitilizer = null; - private static Logger logger = LogManager.getLogger(StorableObject.class.getSimpleName()); + private transient final PropertyChangeListener objectDirtyListener = new PropertyChangeListener(){ + @Override + public void propertyChanged() { + makeDirty(); + } + }; /* * PATH BUILDERS @@ -55,67 +67,138 @@ public abstract class StorableObject extends Storable implements Serializable { * @param c * @return X4S_ClassName.ser */ - @SuppressWarnings("rawtypes") - public static String getX4SObjectFileName(Class c) { - return "X4S_" + c.getSimpleName(); + public static String getDefaulPlainName(Class c) { + return DEFAULT_PLAIN_NAME_PREFIX + c.getSimpleName(); + } + + public static String getFileName(String plainName){ + return "." + plainName; + } + + public static String getFileName(Class c){ + return getFileName(getDefaulPlainName(c)); + } + + public static File getFilePath(File dir, Class c) { + return new File(dir.toString() + File.separator + getFileName(c)); + } + + /* + * Constructors + */ + + /** + * @param c - The class of the object to be stored / loaded + */ + public StorableObject(Class c) { + this(c, null, getDefaulPlainName(c), false); } /** - * This naming scheme shall be used to store Objects in XLogo4Schools - * @param dir - where the instance of c should be stored. - * @param c - Class of Objects to be stored persistently - * @return pathname for dir/X4S_ClassName.ser + * @param c - The class of the object to be stored / loaded + * @param dir - The location where the object is stored */ - @SuppressWarnings("rawtypes") - public static String getFilePath(File dir, Class c) { - return dir.toString() + File.separator + getX4SObjectFileName(c); + public StorableObject(Class c, File dir) { + this(c, dir, getDefaulPlainName(c), false); } /** - * @param dir - * @param c - * @return file for pathname as defined by {@link #getFilePath(File, Class)} + * @param c - The class of the object to be stored / loaded + * @param dir - The location where the object is stored + * @param isDeferred - A deferred persistent object is not immediately persisted after creation, but after the next call to {@link #store()} */ - @SuppressWarnings("rawtypes") - public static File getFile(File dir, Class c) { - String path = getFilePath(dir, c) + ".ser"; - return new File(path); + public StorableObject(Class c, File dir, boolean isDeferred) { + this(c, dir, getDefaulPlainName(c), isDeferred); } - @Override - public String getFileNameExtension() { - return ".ser"; + /** + * @param c - The class of the object to be stored / loaded + * @param dir - The location where the object is stored + * @param plainName - The object's file name without extensions or dots + */ + public StorableObject(Class c, File dir, String plainName) { + this(c, dir, plainName, false); } /** - * Constructor. The FileName will be equal to + * @param c - The class of the object to be stored / loaded + * @param dir - The location where the object is stored + * @param plainName - The object's file name without extensions or dots + * @param isDeferred - A deferred persistent object is not immediately persisted after creation, but after the next call to {@link #store()} */ - public StorableObject() { - setFileName(getX4SObjectFileName(getClass())); + public StorableObject(Class c, File dir, String plainName, boolean isDeferred) { + this.targetClass = c; + setLocation(dir); + setPlainName(plainName); + setStoringDeferred(isDeferred); } /* - * Store & Load + * Create, Store & Load */ + + public StorableObject createOrLoad() throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, SecurityException { + File file = getFilePath(); + + if (file.exists()) { + try { + load(); + } + catch (Exception e) { + logger.warn("Could not load object from file " + e.toString()); + file.delete(); + } + } + + if(!file.exists() || object == null){ + create(); + store(); + } + + + return this; + } + + public StorableObject create() throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, SecurityException { + set(getTargetClass().getConstructor().newInstance()); + if (getCreationInitilizer() != null) { + getCreationInitilizer().init(get()); + } + store(); + return this; + } - public void store() throws IOException { - if (isDirty() && !isVirtual()) - storeCopyToFile(getFilePath()); + public StorableObject load() throws IOException, ClassNotFoundException, ClassCastException{ + setPersisted(false); + if (getSerializer() != null){ + set(loadObject(getFilePath(), getSerializer())); + } else { + set(loadObject(getFilePath(), getTargetClass())); + } + if (getLoadInitilizer() != null) { + getLoadInitilizer().init(get()); + } + makeClean(); + setPersisted(true); + return this; } + @Override public void storeCopyToFile(File file) throws IOException, IllegalArgumentException { if (file == null) throw new IllegalArgumentException("file must not be null."); logger.trace("Storing Object to " + file.getAbsolutePath()); + file.getParentFile().mkdirs(); + if (!isVirtual()) { - mkParentDirs(file); - FileOutputStream fileOut = new FileOutputStream(file); - ObjectOutputStream out = new ObjectOutputStream(fileOut); - out.writeObject(this); - out.close(); - fileOut.close(); + if (getSerializer() != null) { + Utils.store(file, getSerializer().serialize2String(get())); + } + else { + Utils.store(file, get()); + } } makeClean(); } @@ -123,27 +206,160 @@ public abstract class StorableObject extends Storable implements Serializable { /** * Load a Storable object from the specified file * @param file - * @return the loaded Storable + * @param c + * @return Load an object of the given class from a file in Java's own byte representation * @throws IOException * @throws ClassNotFoundException * @throws ClassCastException */ - public static StorableObject loadObject(File file) throws IOException, ClassNotFoundException, ClassCastException { + public static T loadObject(File file, Class c) throws IOException, ClassNotFoundException, ClassCastException { logger.trace("Loading Object from " + file.getAbsolutePath()); + return Utils.readObject(file, c); + } + + /** + * @param file + * @param serializer + * @return The object persistent in the file as interpreted by the serializer + * @throws IOException + */ + public static T loadObject(File file, Serializer serializer) throws IOException { + logger.trace("Loading Object from " + file.getAbsolutePath() + " with " + serializer.toString()); + String serialized = Utils.readFile(file); + T object = serializer.deserialize(serialized); + return object; + } + + + /* + * Getters & Setters + */ + + /** + * If this exists on the file system, that file will be renamed.

+ * If plainName already existed, it is deleted first. + * @param newFileName - if null, {@link #getDefaulPlainName(Class)} is taken + * @throws IllegalArgumentException - If the provided name is not legal. + */ + @Override + public void setPlainName(String plainName){ + if (plainName == null) { + plainName = getDefaulPlainName(targetClass); + } + super.setPlainName(plainName); + } + + @Override + public String getFileNamePrefix(){ + return "."; + } + + @Override + public String getFileNameExtension() { + return ""; + } + + public Class getTargetClass(){ + return targetClass; + } + + public Serializer getSerializer() { + return this.serializer; + } + + public void setSerializer(Serializer serializer){ + this.serializer = serializer; + } + + /** + * Get the persistent object + * @return + */ + public T get() { + return this.object; + } + + /** + * Set the persistent object + * @param object + */ + public void set(T object) { + if (this.object == object){ + return; + } - FileInputStream fileIn = new FileInputStream(file); - ObjectInputStream in = new ObjectInputStream(fileIn); - Object object = in.readObject(); - in.close(); - fileIn.close(); + if (this.object != null){ + this.object.removePropertyChangeListener(null, objectDirtyListener); + } else { + makeDirty(); + } + + this.object = object; - if (!(object instanceof StorableObject)) - throw new ClassCastException("The specified file (" + file.toString() - + ") does not contain an instance of Storable: " + object.getClass().toString()); + if (this.object != null){ + object.addPropertyChangeListener(null, objectDirtyListener); + } + } - StorableObject storable = (StorableObject) object; - storable.makeClean(); - return storable; + public Initializer getCreationInitilizer() { + return creationInitilizer; + } + + public void setCreationInitilizer(Initializer firstTimeInitilizer) { + this.creationInitilizer = firstTimeInitilizer; + } + + public Initializer getLoadInitilizer() { + return loadInitilizer; + } + + public void setLoadInitilizer(Initializer loadTimeInitilizer) { + this.loadInitilizer = loadTimeInitilizer; + } + + /* + * Chainable setters + */ + + /** + * Convenience method for {@link #setSerializer(Initializer)} + * that allows chaining with other methods such as {@link #createOrLoad()} + * @param serializer + * @return + */ + public StorableObject withSerializer(Serializer serializer){ + setSerializer(serializer); + return this; } + /** + * Convenience method for {@link #setCreationInitilizer(Initializer)} + * that allows chaining with other methods such as {@link #createOrLoad()} + * @param firstTimeInitializer + * @return + */ + public StorableObject withCreationInitializer(Initializer firstTimeInitializer){ + setCreationInitilizer(firstTimeInitializer); + return this; + } + + /** + * Convenience method for {@link #setLoadInitilizer(Initializer)} + * that allows chaining with other methods such as {@link #createOrLoad()} + * @param loadTimeInitializer + * @return + */ + public StorableObject withLoadInitializer(Initializer loadTimeInitializer){ + setLoadInitilizer(loadTimeInitializer); + return this; + } + + + /* + * Interfaces + */ + + public interface Initializer { + public void init(T target); + } } diff --git a/logo/src/xlogo/storage/WSManager.java b/logo/src/xlogo/storage/WSManager.java index b9fb6b9..3be37b4 100644 --- a/logo/src/xlogo/storage/WSManager.java +++ b/logo/src/xlogo/storage/WSManager.java @@ -1,27 +1,22 @@ -/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Loic Le Coq +/* + * XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Loic Le Coq * Copyright (C) 2013 Marko Zivkovic - * * Contact Information: marko88zivkovic at gmail dot com - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. This program 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 General - * Public License for more details. You should have received a copy of the - * GNU General Public License along with this program; if not, write to the Free - * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. This program 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 General + * Public License for more details. You should have received a copy of the + * GNU General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. - * - * * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic * during his Bachelor thesis at the computer science department of ETH Zurich, * in the year 2013 and/or during future work. - * * It is a reengineered version of XLogo written by Loic Le Coq, published * under the GPL License at http://xlogo.tuxfamily.org/ - * * Contents of this file were entirely written by Marko Zivkovic */ @@ -29,12 +24,27 @@ package xlogo.storage; import java.io.File; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +import net.samuelcampos.usbdrivedectector.USBDeviceDetectorManager; +import net.samuelcampos.usbdrivedectector.USBStorageDevice; +import net.samuelcampos.usbdrivedectector.events.IUSBDriveListener; +import net.samuelcampos.usbdrivedectector.events.USBStorageEvent; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import xlogo.AppSettings; +import xlogo.Logo; +import xlogo.messages.MessageKeys; import xlogo.messages.async.dialog.DialogMessenger; import xlogo.storage.global.GlobalConfig; +import xlogo.storage.global.GlobalConfig.GlobalProperty; import xlogo.storage.user.UserConfig; +import xlogo.storage.user.UserConfig.UserProperty; import xlogo.storage.workspace.WorkspaceConfig; +import xlogo.storage.workspace.WorkspaceConfig.WorkspaceProperty; import xlogo.utils.Utils; /** @@ -68,16 +78,22 @@ import xlogo.utils.Utils; * @author Marko */ public class WSManager { + private static Logger logger = LogManager.getLogger(WSManager.class.getSimpleName()); - /* * Singleton instantiation */ - private static WSManager instance; - - public static WSManager getInstance(){ - if (instance == null){ + private static WSManager instance; + private static boolean isConstructingSingleton = false; + + public static WSManager getInstance() { + if (instance == null) { + if (isConstructingSingleton){ + throw new IllegalStateException("Recursive Singleton Creation."); + } + isConstructingSingleton = true; instance = new WSManager(); + isConstructingSingleton = false; } return instance; } @@ -88,66 +104,294 @@ public class WSManager { * but it is for a short time while a workspace is being switched or the program fails to enter a workspace. * @return */ - public static WorkspaceConfig getWorkspaceConfig() - { + public static WorkspaceConfig getWorkspaceConfig() { return getInstance().getWorkspaceConfigInstance(); } - + /** * This is a shortcut for {@code WSManager.getInstance().getGlobalConfigInstance()} * This is never null by definition of {@link WSManager} * @return */ - public static GlobalConfig getGlobalConfig() - { + public static GlobalConfig getGlobalConfig() { return getInstance().getGlobalConfigInstance(); } - + /** * This is a shortcut for {@code WSManager.getInstance().getUserConfigInstance()} *

Note that this might be null, if no user has entered his user space. * @return */ - public static UserConfig getUserConfig() - { + public static UserConfig getUserConfig() { return getInstance().getUserConfigInstance(); } + + private transient USBDeviceDetectorManager driveDetector = new USBDeviceDetectorManager(); + private StorableObject globalConfig; private WSManager() { - globalConfig = GlobalConfig.create(); + try { + globalConfig = new StorableObject<>(GlobalConfig.class, GlobalConfig.DEFAULT_LOCATION) + .withCreationInitializer(gc -> { + createWorkspace(gc, WorkspaceConfig.DEFAULT_DIRECTORY); + WorkspaceConfig wc = gc.getCurrentWorkspace().get(); // TODO wc.getLocation() is null at this place :( + createUser(wc, UserConfig.DEFAULT_DIRECTORY); + init(gc); + }) + .withLoadInitializer(gc -> init(gc)) + .createOrLoad(); + } + catch (Exception e) { + DialogMessenger.getInstance().dispatchError("Unable to Initilize Global Configuration", e.toString()); + } + } + + protected void init(GlobalConfig gc){ + initUSBWorkspaces(gc); + gc.cleanUpWorkspaces(); + enterInitialWorkspace(gc); + } + + /** + * This is used to have a workspace ready at the beginning, without any user interaction. + *

+ * Tries to enter workspaces with the following priority. + * 1. Last used workspace (if any) + * 2. Default workspace, if there is no last used workspace + * 3. Virtual Workspace, if entering or creating the default workspace failed for some reason. + */ + protected void enterInitialWorkspace(GlobalConfig gc) { + logger.trace("Entering initial workspace."); + + if (gc.getAllWorkspaces().length == 0){ + logger.warn("No workspaces available."); + return; + } + + String initialWs = getFirstUSBWorkspace(gc); + + if (initialWs == null) { + initialWs = gc.getLastUsedWorkspace(); + } + + if (initialWs == null) { + logger.warn("No workspaces available."); + initialWs = gc.getAllWorkspaces()[0]; + } + + try { + enterWorkspace(gc, initialWs); + } + catch (IOException e1) { + DialogMessenger.getInstance().dispatchError("Workspace Error", "Cannot enter workspace: " + e1.toString()); + } + } + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * USB Detection & Handling + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + /** + * Detect External Drives + */ + protected void initUSBWorkspaces(GlobalConfig gc) { + if (driveDetector != null){ + return; + } + + driveDetector = new USBDeviceDetectorManager(800); + + for (USBStorageDevice rmDevice : driveDetector.getRemovableDevices()) { + if (rmDevice.canRead() && rmDevice.canWrite()) { + addUSBDrive(gc, rmDevice); + } + } + + driveDetector.addDriveListener(new IUSBDriveListener(){ + + @Override + public void usbDriveEvent(USBStorageEvent event) { + USBStorageDevice rmDevice = event.getStorageDevice(); + switch (event.getEventType()) { + case CONNECTED: + addUSBDrive(gc, rmDevice); + break; + case REMOVED: + removeUSBDrive(gc, rmDevice); + break; + } + } + }); + } + + protected void addUSBDrive(GlobalConfig gc, USBStorageDevice rmDevice) { + if (gc.getWorkspaceDirectory(rmDevice.getSystemDisplayName()) == null) { + logger.trace("USB Drive attached: " + rmDevice); + String deviceName = rmDevice.getSystemDisplayName(); + File location = rmDevice.getRootDirectory(); + gc.addWorkspace(deviceName, location.getAbsolutePath()); + } + } + + protected void removeUSBDrive(GlobalConfig gc, USBStorageDevice rmDevice) { + logger.trace("USB Drive removed: " + rmDevice); + String deviceName = rmDevice.getSystemDisplayName(); + gc.removeWorkspace(deviceName); + } + + protected StorableObject initUSBDrive(GlobalConfig gc, String deviceName) throws IOException { + logger.trace("Initializing USB Drive: " + deviceName); + File usbRoot = null; + for (USBStorageDevice device : driveDetector.getRemovableDevices()) { + if (deviceName.equals(device.getSystemDisplayName())) { + usbRoot = device.getRootDirectory(); + break; + } + } + if (usbRoot == null) { return null; } + + StorableObject wsc = + new StorableObject<>(WorkspaceConfig.class, usbRoot, WorkspaceConfig.USB_DEFAULT_WORKSPACE, true); + try { + wsc.createOrLoad(); + gc.addWorkspace(wsc); + } + catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + /* + File wsDir = StorableObject.getDirectory(usbRoot, WorkspaceConfig.USB_DEFAULT_WORKSPACE); + File wsConfigFile = StorableObject.getSerFile(wsDir, WorkspaceConfig.class); + if (wsConfigFile.exists()) { + logger.trace("Loading USB workspace from " + wsDir.getAbsolutePath()); + wsc = WorkspaceConfig.loadWorkspace(usbRoot, WorkspaceConfig.USB_DEFAULT_WORKSPACE); + for (String user : wsc.getUserList()) { + logger.trace("\t Having user " + user); + } + } + else { + logger.trace("Creating new temporary USB workspace at " + usbRoot); + wsc = WorkspaceConfig.createDeferredWorkspace(usbRoot, WorkspaceConfig.USB_DEFAULT_WORKSPACE); + wsc.setUserCreationAllowed(true); + }*/ + return wsc; + } + + public String getFirstUSBWorkspace(GlobalConfig gc) { + for (String ws : gc.getAllWorkspaces()) { + if (isUSBDrive(ws)) { return ws; } + } + return null; + } + + public boolean isUSBDrive(String workspaceName) { + List devices = driveDetector.getRemovableDevices(); + logger.trace("Is '" + workspaceName + "' on a USB Drive?"); + for (USBStorageDevice device : devices) { + if (workspaceName.equals(device.getSystemDisplayName())) { + logger.trace("\t = Yes, corresponding USB Device found."); + return true; + } + else { + logger.trace("\t Does not corresponding to " + device.getSystemDisplayName()); + } + } + + logger.trace("\t = No, could not find corresponding USB Drive."); + return false; + } + + /* + * Config Creation + */ + + /* * + * + * @param dir - where the workspace should be located + * @param workspaceName + * @param deferred - if true, the workspace is not persisted until changes are made to it + * @return + * @throws IOException + * / + public static StorableObject getWorkspace(File dir, String workspaceName, boolean deferred) + throws IOException { + if (!Storable.checkLegalName(workspaceName)) { + DialogMessenger.getInstance().dispatchError(Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE), + Logo.messages.getString(MessageKeys.ILLEGAL_NAME)); + return null; + } + File wsd = StorableObject.getDirectory(dir, workspaceName); + Serializer serializer = new WorkspaceSettingJSONMapper(); + StorableObject wsc = new StorableObject<>(WorkspaceConfig.class, wsd, deferred).withSerializer(serializer); + wsc.get().setLocation(wsd); + return wsc; + } + */ + /** + * Load the specified user's settings from the current workspace + * or create the user if it does not exist yet or if it was deleted for unknown reasons. + * @param workspace + * @param username + * @return the loaded UserConfig + * @throws IOException + */ + public static StorableObject getUser(File location, String username) { + + if (!isWorkspaceDirectory(location)) { + DialogMessenger.getInstance().dispatchError(Logo.messages.getString(MessageKeys.WS_ERROR_TITLE), + Logo.messages.getString(MessageKeys.WS_DOES_NOT_EXIST)); + return null; + } + + if (!Storable.checkLegalName(username)) { + DialogMessenger.getInstance().dispatchError(Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE), + Logo.messages.getString(MessageKeys.ILLEGAL_NAME)); + return null; + } + File userDir = Storable.getDirectory(location, username); + StorableObject userConfig = new StorableObject<>(UserConfig.class, userDir); + try { + userConfig.createOrLoad(); + } + catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | SecurityException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + userConfig.get().setDirectory(userDir); + return userConfig; } /* * Config access */ - private final GlobalConfig globalConfig; /** * @return the instance of the GlobalConfig */ - public GlobalConfig getGlobalConfigInstance() - { - return globalConfig; + public GlobalConfig getGlobalConfigInstance() { + return globalConfig.get(); } /** * @return the active workspace */ - public WorkspaceConfig getWorkspaceConfigInstance() - { - return getGlobalConfigInstance().getCurrentWorkspace(); + public WorkspaceConfig getWorkspaceConfigInstance() { + if (getGlobalConfigInstance().getCurrentWorkspace() != null){ + return getGlobalConfigInstance().getCurrentWorkspace().get(); + } + return null; } /** * @return the active user */ - public UserConfig getUserConfigInstance() - { + public UserConfig getUserConfigInstance() { WorkspaceConfig wc = getWorkspaceConfigInstance(); if (wc == null) return null; else - return wc.getActiveUser(); + return wc.getActiveUser().get(); } /* @@ -158,30 +402,66 @@ public class WSManager { * A new workspace is created in the defined directory. * All Necessary files and folders are created and the workspace is logically added to the globalConfig. * @see WorkspaceConfig#loadWorkspace(File) - * @param dir + * @param location * @param name * @throws IOException */ - public void createWorkspace(File dir, String name) throws IOException - { - getGlobalConfigInstance().createWorkspace(dir, name); + public void createWorkspace(File location, String name) throws IOException { // TODO delegate to overloaded method above + File wsDir = StorableObject.getDirectory(location, name); + GlobalConfig gc = getGlobalConfig(); + createWorkspace(gc, wsDir); + enterInitialWorkspace(gc); } - public void deleteWorkspace(String wsName, boolean deleteFromDisk) throws SecurityException - { - File wsDir = getGlobalConfigInstance().getWorkspaceDirectory(wsName); - getGlobalConfigInstance().removeWorkspace(wsName); + /** + * + * @param gc - The global config, where the new workspace should be registered + * @param wsDir - the workspace directory + * @return + */ + protected StorableObject createWorkspace(GlobalConfig gc, File wsDir){ + StorableObject wc; + wc = new StorableObject<>(WorkspaceConfig.class, wsDir); + try { + wc.createOrLoad(); + wc.get().setDirectory(wsDir); + gc.addWorkspace(wc); + enterWorkspace(gc, wsDir.getName()); + return wc; + } + catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | SecurityException | IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return wc; + } + + public void deleteWorkspace(String wsName, boolean deleteFromDisk) throws SecurityException { + GlobalConfig gc = getGlobalConfigInstance(); + File wsDir = gc.getWorkspaceDirectory(wsName); + gc.leaveWorkspace(); + gc.removeWorkspace(wsName); if (deleteFromDisk) deleteFullyRecursive(wsDir); + enterInitialWorkspace(gc); } /** * @param wsDir * @throws IllegalArgumentException if wsDir is not a legal workspace directory */ - public void importWorkspace(File wsDir, String wsName) throws IllegalArgumentException - { - getGlobalConfigInstance().importWorkspace(wsDir, wsName); + public void importWorkspace(File workspaceDir, String workspaceName) { + logger.trace("Importing workspace '" + workspaceName + "' from " + workspaceDir.getAbsolutePath()); + if (!isWorkspaceDirectory(workspaceDir)) { + DialogMessenger.getInstance().dispatchError(Logo.messages.getString(MessageKeys.WS_ERROR_TITLE), + workspaceDir + " " + Logo.messages.getString(MessageKeys.WS_NOT_A_WORKSPACE_DIRECTORY)); + return; + } + getGlobalConfigInstance().addWorkspace(workspaceName, workspaceDir.getParent()); + try { + enterWorkspace(workspaceName); + } + catch (IOException ignore) { /* This won't hopefully ever happen. */} } /** @@ -190,98 +470,159 @@ public class WSManager { * @param workspaceName - the workspace to load and enter * @throws IOException - if the old workspace could not be loaded */ - public void enterWorkspace(String workspaceName) throws IOException - { - getGlobalConfigInstance().enterWorkspace(workspaceName); + public void enterWorkspace(String workspaceName) throws IOException { + GlobalConfig gc = getGlobalConfigInstance(); + enterWorkspace(gc, workspaceName); } - /* * - * @throws IOException If workspace could not be saved. - * / - public void leaveWorkspace() throws IOException - { - getGlobalConfig().leaveWorkspace(); - }*/ - + protected void enterWorkspace(GlobalConfig gc, String workspaceName) throws IOException { + if(gc.getCurrentWorkspace() != null && workspaceName.equals(gc.getCurrentWorkspace().get().getWorkspaceName())){ + logger.trace("I'm already in workspace: " + workspaceName); + return; + } + if (isUSBDrive(workspaceName)) { + logger.trace("Retrieving USB workspace: " + workspaceName); + initUSBDrive(gc, workspaceName); + } + gc.enterWorkspace(workspaceName); + } /* * USER CONFIG : create, delete, enter */ - + /** * A new user is created in the current workspace. * All Necessary files and folders are created and the workspace is logically added to the globalConfig. * @param username */ - public void createUser(String username) - { - getWorkspaceConfigInstance().createUser(username); + public void createUser(String username) { + StorableObject wc = globalConfig.get().getCurrentWorkspace(); + if (wc == null || wc.get() == null){ + throw new IllegalStateException("Cannot create a user directory outside of workspaces. Use createUser(File dir) for special cases."); + } + File userDir = StorableObject.getDirectory(wc.getLocation(), username); + createUser(wc.get(), userDir); + } + + private void createUser(WorkspaceConfig wc, File userDir){ + StorableObject duc = new StorableObject<>(UserConfig.class, userDir); + try { + duc.createOrLoad(); + } + catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | SecurityException e) { } + if (!duc.isPersisted()){ + logger.warn("Could not persist user files."); + } + duc.get().setDirectory(userDir); + wc.addUser(duc); + try { + enterUserSpace(wc, userDir.getName()); + } + catch (IOException ignore) { /* This won't ever happen hopefully */ } } /** * @param username * @param deleteFromDisk */ - public void deleteUser(String username, boolean deleteFromDisk) - { - File location = StorableObject.getDirectory(getWorkspaceConfigInstance().getLocation(), username); + public void deleteUser(String username, boolean deleteFromDisk) { + File location = StorableObject.getDirectory(getWorkspaceConfigInstance().getDirectory(), username); getWorkspaceConfigInstance().removeUser(username); - if (deleteFromDisk) - { - try{ + if (deleteFromDisk) { + try { deleteFullyRecursive(location); } - catch(SecurityException e) { + catch (SecurityException e) { System.out.println("Files not deleted: " + e.toString()); } } } - - public void importUser(File userDir, String newUserName) throws IllegalArgumentException, IOException - { - getWorkspaceConfigInstance().importUser(userDir, newUserName); + + /** + * Import a user directory from anywhere in the file system to this workspace. + * All files in the user directory are copied. Already existing files might get overwritten. + *

This has no effect if this is virtual. + * @param srcUserDir - a legal user directory anywhere on the file system + * @param destUsername - Existing files of targetUser are overwritten. If targetUser does not exist, it will be created first. + * @throws IllegalArgumentException + * @throws IOException + * @see WSManager#isUserDirectory(File) + */ + public void importUser(File srcUserDir, String destUsername) throws IllegalArgumentException, IOException { + logger.trace("Importing user '" + destUsername + "' from " + srcUserDir.getAbsolutePath()); + + if (!isUserDirectory(srcUserDir)) + throw new IllegalArgumentException("Target directory is not a user directory."); + + createUser(destUsername); + File wsDir = getWorkspaceConfig().getDirectory(); + File targetUserDir = StorableObject.getDirectory(wsDir, destUsername); + + copyFullyRecursive(srcUserDir, targetUserDir); + + enterUserSpace(destUsername); } /** * @throws IOException If the old userConfig could not be stored. */ - public void enterUserSpace(String name) throws IOException - { - if (getWorkspaceConfigInstance() == null) + public void enterUserSpace(String name) throws IOException { + WorkspaceConfig wc = getWorkspaceConfigInstance(); + if (wc == null) throw new IllegalStateException("Must be in WorkspaceDirectory first to enter UserSpace."); - - getWorkspaceConfigInstance().enterUserSpace(name); + enterUserSpace(wc, name); + } + + private void enterUserSpace(WorkspaceConfig wc, String name) throws IOException { + wc.enterUserSpace(name); + } + + /* + * Short cuts + */ + + public void storeAllSettings() throws IOException { + globalConfig.store(); + StorableObject swc = globalConfig.get().getCurrentWorkspace(); + if (swc == null) { return; } + swc.store(); + StorableObject suc = swc.get().getActiveUser(); + if (suc == null) { return; } + suc.store(); } /* * DIRECTORIES & FILE MANIPULATION */ - - public static File[] listDirectories(File dir) - { - return dir.listFiles(new java.io.FileFilter() { + + public static File[] listDirectories(File dir) { + File[] dirs = dir.listFiles(new java.io.FileFilter(){ public boolean accept(File pathname) { return pathname.isDirectory(); } }); + if (dirs == null) { + dirs = new File[0]; + } + return dirs; } - + /** * A directory is considered a workspace directory, * if it contains a file for {@link WorkspaceConfig}, as defined by {@link StorableObject#getFilePath(File, Class)} * @param dir * @return */ - public static boolean isWorkspaceDirectory(File dir) - { + public static boolean isWorkspaceDirectory(File dir) { if (dir == null) return false; - if(!dir.isDirectory()) + if (!dir.isDirectory()) return false; - File wcf = WorkspaceConfig.getFile(dir, WorkspaceConfig.class); - if(!wcf.isFile()) + File wcf = StorableObject.getFilePath(dir, WorkspaceConfig.class); + if (!wcf.isFile()) return false; return true; @@ -293,21 +634,20 @@ public class WSManager { * @param dir * @return */ - public static boolean isUserDirectory(File dir) - { + public static boolean isUserDirectory(File dir) { if (dir == null) return false; - if(!dir.isDirectory()) + if (!dir.isDirectory()) return false; - File ucf = UserConfig.getFile(dir, UserConfig.class); - if(!ucf.isFile()) + File ucf = StorableObject.getFilePath(dir, UserConfig.class); + if (!ucf.isFile()) return false; return true; } - + /** * If "from" denotes a file, then "to" should be a file too. * The contents of file "from" are copied to file "to". @@ -320,46 +660,39 @@ public class WSManager { * @param to - must not exist * @throws IOException */ - public static void copyFullyRecursive(File from, File to) throws IOException - { + public static void copyFullyRecursive(File from, File to) throws IOException { if (!from.exists()) throw new IllegalArgumentException("'from' (" + from.toString() + ") must exist."); - if(from.isFile()) - { + if (from.isFile()) { copyFile(from, to); return; } // else to is directory to.mkdirs(); - - for(File src : from.listFiles()) - { + + for (File src : from.listFiles()) { File dest = new File(to.toString() + File.separator + src.getName()); - if (src.isFile()) - { + if (src.isFile()) { copyFile(src, dest); - }else if (src.isDirectory()) - { + } + else if (src.isDirectory()) { copyFullyRecursive(src, dest); } } } - public static void copyFile(File from, File to) throws IOException - { + public static void copyFile(File from, File to) throws IOException { if (!from.isFile()) throw new IllegalArgumentException("File 'from' (" + from.toString() + ") must exist."); - if (to.exists()) - { + if (to.exists()) { if (!to.isFile()) throw new IllegalArgumentException("File 'to' (" + from.toString() + ") must be a file."); } - else - { + else { File parent = to.getParentFile(); if (!parent.exists()) to.getParentFile().mkdirs(); @@ -372,19 +705,16 @@ public class WSManager { * @param victim * @throws SecurityException If one tries to delete some directory that is not under control of this application (Workspace or User) */ - public static void deleteFullyRecursive(File victim) throws SecurityException - { + public static void deleteFullyRecursive(File victim) throws SecurityException { if (!victim.exists()) return; - if (victim.isFile()) - { + if (victim.isFile()) { victim.delete(); return; } - if (!isGlobalConfigDirectory(victim) && !isWorkspaceDirectory(victim) && !isUserDirectory(victim)) - { + if (!isGlobalConfigDirectory(victim) && !isWorkspaceDirectory(victim) && !isUserDirectory(victim)) { String title = AppSettings.getInstance().translate("error.security.violation.title"); String message = AppSettings.getInstance().translate("error.attempt.delete.non.x4s.file"); DialogMessenger.getInstance().dispatchError(title, message + ' ' + victim.toString()); @@ -392,8 +722,7 @@ public class WSManager { } // Delete all sub-directories - for (File f : victim.listFiles()) - { + for (File f : victim.listFiles()) { uncheckedRecursiveDelete(f); victim.delete(); } @@ -407,20 +736,17 @@ public class WSManager { * Otherwise it could delete just everything. * @param victim */ - private static void uncheckedRecursiveDelete(File victim) - { + private static void uncheckedRecursiveDelete(File victim) { if (!victim.exists()) return; - if (victim.isFile()) - { + if (victim.isFile()) { victim.delete(); return; } // Delete all sub-directories - for (File f : victim.listFiles()) - { + for (File f : victim.listFiles()) { uncheckedRecursiveDelete(f); victim.delete(); } @@ -429,16 +755,14 @@ public class WSManager { victim.delete(); } - public static boolean isGlobalConfigDirectory(File dir) - { - if(!dir.isDirectory()) + public static boolean isGlobalConfigDirectory(File dir) { + if (!dir.isDirectory()) return false; String name = dir.getName(); - if(!name.startsWith("X4S_")) + if (!name.startsWith("X4S_")) return false; - return true; } } diff --git a/logo/src/xlogo/storage/global/GlobalConfig.java b/logo/src/xlogo/storage/global/GlobalConfig.java index 66a26b5..1c5a27d 100644 --- a/logo/src/xlogo/storage/global/GlobalConfig.java +++ b/logo/src/xlogo/storage/global/GlobalConfig.java @@ -24,16 +24,14 @@ package xlogo.storage.global; import java.awt.Font; import java.awt.GraphicsEnvironment; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.io.UnsupportedEncodingException; +import java.lang.reflect.InvocationTargetException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; -import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; @@ -41,106 +39,47 @@ import java.util.TreeMap; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import net.samuelcampos.usbdrivedectector.USBDeviceDetectorManager; -import net.samuelcampos.usbdrivedectector.USBStorageDevice; -import net.samuelcampos.usbdrivedectector.events.IUSBDriveListener; -import net.samuelcampos.usbdrivedectector.events.USBStorageEvent; import xlogo.AppSettings; -import xlogo.Logo; -import xlogo.messages.MessageKeys; -import xlogo.messages.async.dialog.DialogMessenger; +import xlogo.interfaces.Observable; +import xlogo.interfaces.PropertyChangePublisher; import xlogo.storage.StorableObject; -import xlogo.storage.WSManager; -import xlogo.storage.user.UserConfig; import xlogo.storage.workspace.WorkspaceConfig; +import xlogo.storage.workspace.WorkspaceConfig.WorkspaceProperty; /** * This Config is stored in default location : "user.home". It contains information about the currently used workspaces on the computer * @author Marko Zivkovic */ -public class GlobalConfig extends StorableObject implements Serializable { +public class GlobalConfig implements Serializable, Observable { private static final long serialVersionUID = 2787615728665011813L; private static Logger logger = LogManager.getLogger(GlobalConfig.class.getSimpleName()); + public static final File DEFAULT_LOCATION = new File(System.getProperty("user.home")); + + public static boolean DEBUG = true; // TODO set false public static final String LOGO_FILE_EXTENSION = ".lgo"; - public static boolean DEBUG = true; // TODO set false /** * Creates the global config at default location, together with a virtual workspace */ - protected GlobalConfig() { - try { - setLocation(getDefaultLocation()); - } - catch (IllegalArgumentException ignore) {} // This is thrown if name illegal, but it is legal + public GlobalConfig() { workspaces = new TreeMap(); - workspaces.put(WorkspaceConfig.VIRTUAL_WORKSPACE, ""); - workspaces.put(WorkspaceConfig.USER_DEFAULT_WORKSPACE, getDefaultLocation().getAbsolutePath()); } - - /** - * If GlobalConfig exists on the file system in default location, it is loaded, otherwise it will be created there. - * @return - */ - public static GlobalConfig create() { - File gcf = getFile(getDefaultLocation(), GlobalConfig.class); - GlobalConfig globalConfig = null; - if (gcf.exists()) { - logger.trace("Try to read GlobalConfig from " + gcf.getAbsolutePath()); - try { - globalConfig = (GlobalConfig) loadObject(gcf); - } - catch (Exception e) { - logger.error("GlobalConfig was corrupted."); - String title = AppSettings.getInstance().translate("error.loading.config.files.title"); - String message = AppSettings.getInstance().translate("error.loading.config.files"); - DialogMessenger.getInstance().dispatchError(title, message + e.toString()); - gcf.delete(); - globalConfig = null; - } - } - - if (globalConfig == null) { - try { - logger.info(gcf.getAbsolutePath() + " not found. Creating new."); - globalConfig = new GlobalConfig(); - globalConfig.store(); - } - catch (Exception e) { - // Best effort : We will try to operate the program without storing anything on disk - logger.error("Cannot store global config at " + gcf.getAbsolutePath() + ". Running in virtual mode."); - globalConfig = getNewVirtualInstance(); - String title = AppSettings.getInstance().translate("error.setting.up.x4s.title"); - String message = AppSettings.getInstance().translate("error.setting.up.x4s"); - DialogMessenger.getInstance().dispatchError(title, message + e.toString()); - } - } - globalConfig.init(); - return globalConfig; - } - - protected void init(){ - logger.trace("Initialize"); - initUSBWorkspaces(); - cleanUpWorkspaces(); - enterInitialWorkspace(); - } - - private void cleanUpWorkspaces() { + public void cleanUpWorkspaces() { logger.trace("Cleaning up workspaces."); Map existingWorkspaces = new TreeMap(); Map lostWorkspaces = new TreeMap(); for (Entry e : workspaces.entrySet()) { File file = new File(e.getValue()); - if (file.exists() || WorkspaceConfig.isSpecialWorkspace(e.getKey(), e.getValue())) { + if (file.exists()) { logger.trace("\tConfirmed existence: " + e.getKey() + " at " + e.getValue()); existingWorkspaces.put(e.getKey(), e.getValue()); } else { logger.trace("\tLost workspace: " + e.getKey() + " at " + e.getValue()); - if (e.getKey().equals(lastUsedWorkspace)){ + if (e.getKey().equals(lastUsedWorkspace)) { lastUsedWorkspace = null; currentWorkspace = null; } @@ -148,112 +87,56 @@ public class GlobalConfig extends StorableObject implements Serializable { } } - if(!existingWorkspaces.containsKey(WorkspaceConfig.USER_DEFAULT_WORKSPACE)){ - // This might be the case if the GlobalConfig version stored on the disk comes from a version - // that did not contain a default workspace - existingWorkspaces.put(WorkspaceConfig.USER_DEFAULT_WORKSPACE, getDefaultLocation().getAbsolutePath()); - } - + /*// TODO maybe reintroduce this warning. but currently ==> recursive singleton instantiation :-( if (lostWorkspaces.size() > 0) { StringBuilder msg = new StringBuilder(); String message = AppSettings.getInstance().translate("message.some.workspaces.not.found"); String at = AppSettings.getInstance().translate("word.at.filesystem.location"); msg.append(message); for (Entry e : lostWorkspaces.entrySet()) { - msg.append('\t').append(e.getKey()) - .append(' ').append(at).append(' ') - .append(e.getValue()).append('\n'); + msg.append('\t').append(e.getKey()).append(' ').append(at).append(' ').append(e.getValue()) + .append('\n'); } DialogMessenger.getInstance().dispatchMessage(msg.toString()); - } + }*/ workspaces = existingWorkspaces; } - - public static GlobalConfig getNewVirtualInstance() { - GlobalConfig gc = new GlobalConfig(); - gc.makeVirtual(); - return gc; - } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Physical Workspaces (stored on file system) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - public void createWorkspace(File dir, String workspaceName) throws IOException { - logger.trace("Creating workspace '" + workspaceName + "' at " + dir.getAbsolutePath()); - if (WorkspaceConfig.createNewWorkspace(dir, workspaceName) != null) - addWorkspace(workspaceName, dir.toString()); - } - - public void importWorkspace(File workspaceDir, String workspaceName) { - logger.trace("Importing workspace '" + workspaceName + "' from " + workspaceDir.getAbsolutePath()); - if (!WSManager.isWorkspaceDirectory(workspaceDir)) { - DialogMessenger.getInstance().dispatchError(Logo.messages.getString(MessageKeys.WS_ERROR_TITLE), - workspaceDir + " " + Logo.messages.getString(MessageKeys.WS_NOT_A_WORKSPACE_DIRECTORY)); - return; - } - addWorkspace(workspaceName, workspaceDir.getParent()); - } - /** * Load the specified workspace from the file system. * @param workspaceName * @return the specified workspace or null if it does not exist. * @throws IOException */ - private WorkspaceConfig retrieveWorkspace(String workspaceName) throws IOException { - WorkspaceConfig wsc = getCachedWorkspace(workspaceName); + private StorableObject retrieveWorkspace(String workspaceName) throws IOException { + StorableObject wsc = getCachedWorkspace(workspaceName); if (wsc != null) { - logger.trace("Retrieving cached workspace: " + workspaceName); + logger.trace("Retrieved cached workspace: " + workspaceName); return wsc; } - + if (!existsWorkspace(workspaceName)) { logger.warn("Attempting to load an inexistent workspace: " + workspaceName); return null; } - File location = getWorkspaceLocation(workspaceName); - - if (WorkspaceConfig.isDefaultWorkspace(workspaceName, location)) { - logger.trace("Retrieving Default workspace from: " + location.getAbsolutePath()); - wsc = getDefaultWorkspace(); - } - else if (isUSBDrive(workspaceName)) { - logger.trace("Retrieving USB workspace: " + workspaceName); - wsc = initUSBDrive(workspaceName); - } - else { - logger.trace("Retrieving workspace: " + workspaceName + " from " + location.getAbsolutePath()); - wsc = WorkspaceConfig.loadWorkspace(location, workspaceName); - } - - if (wsc == null) { - WSManager.getInstance().deleteWorkspace(workspaceName, false); - } - - cacheWorkspace(workspaceName, wsc); - - return wsc; - } - - private WorkspaceConfig getDefaultWorkspace() throws IOException{ - logger.trace("Get Default Workspace"); - File wsDir = WorkspaceConfig.getDefaultWorkspaceDirectory(); - File wsFile = getFile(wsDir, WorkspaceConfig.class); - File wsLocation = getDefaultLocation(); - WorkspaceConfig wsc = null; - if (wsFile.exists()) { - wsc = WorkspaceConfig.loadWorkspace(wsLocation, WorkspaceConfig.USER_DEFAULT_WORKSPACE); - } else { - wsc = WorkspaceConfig.createNewWorkspace(wsLocation, WorkspaceConfig.USER_DEFAULT_WORKSPACE); - wsc.setAllowUserCreation(true); - wsc.createUser(UserConfig.DEFAULT_USER); + File wsDir = getWorkspaceDirectory(workspaceName); + + logger.trace("Retrieving workspace: " + workspaceName + " from " + wsDir.toString()); + try { + wsc = new StorableObject<>(WorkspaceConfig.class, wsDir).createOrLoad(); + wsc.get().setDirectory(wsDir); + cacheWorkspace(workspaceName, wsc); } + catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | SecurityException ignore) { } + return wsc; } - + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Workspace Cache * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -261,20 +144,22 @@ public class GlobalConfig extends StorableObject implements Serializable { /** * Workspace Objects that have already been created or loaded from disk. */ - private transient Map cachedWorkspaces; + private transient Map> cachedWorkspaces; - private WorkspaceConfig getCachedWorkspace(String workspaceName) { + private StorableObject getCachedWorkspace(String workspaceName) { if (cachedWorkspaces == null) { - cachedWorkspaces = new TreeMap(); + cachedWorkspaces = new TreeMap<>(); } return cachedWorkspaces.get(workspaceName); } - private void cacheWorkspace(String workspaceName, WorkspaceConfig wsc) { + private void cacheWorkspace(String workspaceName, StorableObject wsc) { + if (cachedWorkspaces == null) { + cachedWorkspaces = new TreeMap<>(); + } cachedWorkspaces.put(workspaceName, wsc); } - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Workspaces * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -291,40 +176,38 @@ public class GlobalConfig extends StorableObject implements Serializable { public void addWorkspace(String workspaceName, String location) { logger.trace("Adding workspace: '" + workspaceName + "' at " + location); workspaces.put(workspaceName, location); - makeDirty(); - notifyWorkspaceListChanged(); + publisher.publishEvent(GlobalProperty.WORKSPACES); setLastUsedWorkspace(workspaceName); - enterInitialWorkspace(); } - public void addWorkspace(String workspaceName, File location) { - addWorkspace(workspaceName, location.getAbsolutePath()); + public void addWorkspace(StorableObject wc) { + File location = wc.getLocation(); + String name = wc.get().getWorkspaceName(); + cacheWorkspace(name, wc); + addWorkspace(name, location.getParentFile().toString()); } public void removeWorkspace(String workspaceName) { logger.trace("Removing workspace: " + workspaceName); - workspaces.remove(workspaceName); - cachedWorkspaces.remove(workspaceName); - makeDirty(); - notifyWorkspaceListChanged(); - if(lastUsedWorkspace.equals(workspaceName)){ + + if (currentWorkspace != null && currentWorkspace.get().getWorkspaceName().equals(workspaceName)){ + leaveWorkspace(); + } + + if (lastUsedWorkspace.equals(workspaceName)) { lastUsedWorkspace = null; - currentWorkspace = null; - enterInitialWorkspace(); + publisher.publishEvent(GlobalProperty.LAST_USED_WORKSPACE); } + + workspaces.remove(workspaceName); + cachedWorkspaces.remove(workspaceName); + publisher.publishEvent(GlobalProperty.WORKSPACES); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Workspace File Utility * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - /** - * @return File from system property "user.home" - */ - public static File getDefaultLocation() { - return new File(System.getProperty("user.home")); - } - /** * @param wsName * @return the parent directory of the workspace directory, or null if the workspace does not exist @@ -346,7 +229,7 @@ public class GlobalConfig extends StorableObject implements Serializable { return null; return new File(wsLocation.toString() + File.separator + wsName); } - + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Workspaces * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -366,14 +249,6 @@ public class GlobalConfig extends StorableObject implements Serializable { public boolean existsWorkspace(String workspace) { return workspaces.get(workspace) != null; } - - - public String getFirstUSBWorkspace() { - for (String ws : workspaces.keySet()) { - if (isUSBDrive(ws)) { return ws; } - } - return null; - } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Last used workspace @@ -392,7 +267,7 @@ public class GlobalConfig extends StorableObject implements Serializable { private void setLastUsedWorkspace(String workspace) { if (existsWorkspace(workspace)) { lastUsedWorkspace = workspace; - makeDirty(); + publisher.publishEvent(GlobalProperty.LAST_USED_WORKSPACE); } } @@ -400,52 +275,17 @@ public class GlobalConfig extends StorableObject implements Serializable { * Current Workspace * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - private transient WorkspaceConfig currentWorkspace; + private transient StorableObject currentWorkspace; /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Entering and Leaving Workspaces * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - public WorkspaceConfig getCurrentWorkspace() { + public StorableObject getCurrentWorkspace() { return currentWorkspace; } - /** - * This is used to have a workspace ready at the beginning, without any user interaction. - *

- * Tries to enter workspaces with the following priority. - * 1. Last used workspace (if any) - * 2. Default workspace, if there is no last used workspace - * 3. Virtual Workspace, if entering or creating the default workspace failed for some reason. - */ - private void enterInitialWorkspace() { - logger.trace("Entering initial workspace."); - - String initialWs = getFirstUSBWorkspace(); - - if (initialWs == null) { - initialWs = getLastUsedWorkspace(); - } - - if (initialWs == null) { - initialWs = WorkspaceConfig.USER_DEFAULT_WORKSPACE; - } - - if (initialWs == null) { - initialWs = WorkspaceConfig.VIRTUAL_WORKSPACE; // this exists, see constructor - } - - try { - enterWorkspace(initialWs); - } - catch (IOException e1) { - try { - enterWorkspace(WorkspaceConfig.VIRTUAL_WORKSPACE); - } - catch (IOException e2) {} - DialogMessenger.getInstance().dispatchError("Workspace Error", "Cannot enter workspace: " + e1.toString()); - } - } + /** * Load the workspace @@ -459,134 +299,40 @@ public class GlobalConfig extends StorableObject implements Serializable { leaveWorkspace(); } currentWorkspace = retrieveWorkspace(workspaceName); - if (currentWorkspace == null) - currentWorkspace = retrieveWorkspace(WorkspaceConfig.VIRTUAL_WORKSPACE); setLastUsedWorkspace(workspaceName); - notifyWorkspacEntered(); - - currentWorkspace.enterInitialUserSpace(); + publisher.publishEvent(GlobalProperty.CURRENT_WORKSPACE); - AppSettings.getInstance().setLanguage(currentWorkspace.getLanguage()); + currentWorkspace.get().enterInitialUserSpace(); } /** * Afterwards, currentWorkspace is null * @throws IOException If workspace could not be saved. */ - public void leaveWorkspace() throws IOException { + public void leaveWorkspace() { if (currentWorkspace == null) - throw new IllegalStateException(AppSettings.getInstance().translate("error.leaving.ws.without.being.in.one")); - logger.trace("Leaving workspace: " + currentWorkspace.getWorkspaceName()); + throw new IllegalStateException(AppSettings.getInstance() + .translate("error.leaving.ws.without.being.in.one")); + logger.trace("Leaving workspace: " + currentWorkspace.get().getWorkspaceName()); - if (currentWorkspace.getActiveUser() != null) { - currentWorkspace.leaveUserSpace(); + if (currentWorkspace.get().getActiveUser() != null) { + try { + currentWorkspace.get().leaveUserSpace(); + } + catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } } if (currentWorkspace.isDirty()) currentWorkspace.store(); currentWorkspace = null; - } - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * USB Detection & Handling - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - private transient USBDeviceDetectorManager driveDetector; - - /** - * Detect External Drives - */ - protected void initUSBWorkspaces() { - driveDetector = new USBDeviceDetectorManager(800); - - for (USBStorageDevice rmDevice : driveDetector.getRemovableDevices()) { - if (rmDevice.canRead() && rmDevice.canWrite()) { - addUSBDrive(rmDevice); - } - } - - driveDetector.addDriveListener(new IUSBDriveListener(){ - - @Override - public void usbDriveEvent(USBStorageEvent event) { - USBStorageDevice rmDevice = event.getStorageDevice(); - switch (event.getEventType()) { - case CONNECTED: - addUSBDrive(rmDevice); - break; - case REMOVED: - removeUSBDrive(rmDevice); - break; - } - } - }); - } - - protected void addUSBDrive(USBStorageDevice rmDevice) { - if (getWorkspaceDirectory(rmDevice.getSystemDisplayName()) == null) { - logger.trace("USB Drive attached: " + rmDevice); - String deviceName = rmDevice.getSystemDisplayName(); - File location = rmDevice.getRootDirectory(); - addWorkspace(deviceName, location.getAbsolutePath()); - } - } - - protected void removeUSBDrive(USBStorageDevice rmDevice) { - logger.trace("USB Drive removed: " + rmDevice); - String deviceName = rmDevice.getSystemDisplayName(); - removeWorkspace(deviceName); - } - - protected WorkspaceConfig initUSBDrive(String deviceName) throws IOException { - logger.trace("Initializing USB Drive: " + deviceName); - File usbRoot = null; - for (USBStorageDevice device : driveDetector.getRemovableDevices()) { - if (deviceName.equals(device.getSystemDisplayName())) { - usbRoot = device.getRootDirectory(); - break; - } - } - if (usbRoot == null) { return null; } - - File wsDir = WorkspaceConfig.getDirectory(usbRoot, WorkspaceConfig.USB_DEFAULT_WORKSPACE); - File wsConfigFile = WorkspaceConfig.getFile(wsDir, WorkspaceConfig.class); - - WorkspaceConfig wsc = null; - if (wsConfigFile.exists()) { - logger.trace("Loading USB workspace from " + wsDir.getAbsolutePath()); - wsc = WorkspaceConfig.loadWorkspace(usbRoot, WorkspaceConfig.USB_DEFAULT_WORKSPACE); - for (String user : wsc.getUserList()) { - logger.trace("\t Having user " + user); - } - } - else { - logger.trace("Creating new temporary USB workspace at " + usbRoot); - wsc = WorkspaceConfig.createDeferredWorkspace(usbRoot, WorkspaceConfig.USB_DEFAULT_WORKSPACE); - wsc.setAllowUserCreation(true); - } - return wsc; - } - - /* * - * Workspace Types - * */ - - public boolean isUSBDrive(String workspaceName) { - List devices = driveDetector.getRemovableDevices(); - logger.trace("Is '" + workspaceName + "' on a USB Drive?"); - for (USBStorageDevice device : devices) { - if (workspaceName.equals(device.getSystemDisplayName())) { - logger.trace("\t = Yes, corresponding USB Device found."); - return true; - } else { - logger.trace("\t Does not corresponding to " + device.getSystemDisplayName()); - } - } - logger.trace("\t = No, could not find corresponding USB Drive."); - return false; + + publisher.publishEvent(GlobalProperty.CURRENT_WORKSPACE); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -610,7 +356,7 @@ public class GlobalConfig extends StorableObject implements Serializable { masterPassword = null; else masterPassword = hash(newPw); - makeDirty(); + publisher.publishEvent(GlobalProperty.PASSWORD); return true; } else { @@ -675,7 +421,7 @@ public class GlobalConfig extends StorableObject implements Serializable { public void setPath(ArrayList path) { this.path = path; - makeDirty(); + publisher.publishEvent(GlobalProperty.PATH); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -720,8 +466,8 @@ public class GlobalConfig extends StorableObject implements Serializable { return maximumMemory; } - private transient int maxMemoryAtNextStart = getMaximumMemory(); - + private transient int maxMemoryAtNextStart = getMaximumMemory(); + /** * @return The amount of memory in MB that Lanceur will cause JVM to allocate to XLogo4Schools the next time this application is started. */ @@ -743,7 +489,7 @@ public class GlobalConfig extends StorableObject implements Serializable { //Preferences prefs = Preferences.systemRoot().node(PROPERTIES_PREFIX); //prefs.putInt("appMemory", maxMemory); } - + /** * The amount of memory that the memory checker allows the application to consume. * It's 0.9*{@link #getMaximumMemory()}} in bytes. @@ -756,8 +502,8 @@ public class GlobalConfig extends StorableObject implements Serializable { * Fonts * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - public static final Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment() - .getAllFonts(); // Toolkit.getDefaultToolkit().getFontList(); + public static final Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); // Toolkit.getDefaultToolkit().getFontList(); + static public int getFontId(Font font) { for (int i = 0; i < fonts.length; i++) { if (fonts[i].getFontName().equals(font.getFontName())) @@ -767,51 +513,25 @@ public class GlobalConfig extends StorableObject implements Serializable { } /* * * * * * * - * Event Handling + * Event Handling : Property Change Listeners * * * * * * */ - - // workspace list change - - private transient ArrayList workspaceListChangeListeners; - - public void addWorkspaceListChangeListener(ActionListener listener) { - if (workspaceListChangeListeners == null) - workspaceListChangeListeners = new ArrayList(); - workspaceListChangeListeners.add(listener); - } - - public void removeWorkspaceListChangeListener(ActionListener listener) { - workspaceListChangeListeners.remove(listener); - } - - private void notifyWorkspaceListChanged() { - if (workspaceListChangeListeners == null) - workspaceListChangeListeners = new ArrayList(); - ActionEvent event = new ActionEvent(this, 0, "workspaceListChanged"); - for (ActionListener listener : workspaceListChangeListeners) - listener.actionPerformed(event); - } - - // enter workspace event - - private transient ArrayList enterWorkspaceListeners; - - public void addEnterWorkspaceListener(ActionListener listener) { - if (enterWorkspaceListeners == null) - enterWorkspaceListeners = new ArrayList(); - enterWorkspaceListeners.add(listener); + + public enum GlobalProperty { + PATH, PASSWORD, LAST_USED_WORKSPACE, CURRENT_WORKSPACE, WORKSPACES; } + + private transient PropertyChangePublisher publisher = new PropertyChangePublisher<>(); - public void removeEnterWorkspaceListener(ActionListener listener) { - enterWorkspaceListeners.remove(listener); + @Override + public void addPropertyChangeListener(GlobalProperty property, PropertyChangeListener listener) { + if (publisher == null){ + publisher = new PropertyChangePublisher<>(); + } + publisher.addPropertyChangeListener(property, listener); } - private void notifyWorkspacEntered() { - if (enterWorkspaceListeners == null) - return; - ActionEvent event = new ActionEvent(this, 0, "workspaceEntered"); - for (ActionListener listener : enterWorkspaceListeners) - listener.actionPerformed(event); + @Override + public void removePropertyChangeListener(GlobalProperty property, PropertyChangeListener listener) { + publisher.removePropertyChangeListener(property, listener); } - } diff --git a/logo/src/xlogo/storage/user/UserConfig.java b/logo/src/xlogo/storage/user/UserConfig.java index ebe5584..7906818 100644 --- a/logo/src/xlogo/storage/user/UserConfig.java +++ b/logo/src/xlogo/storage/user/UserConfig.java @@ -1,27 +1,22 @@ -/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Loic Le Coq +/* + * XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Loic Le Coq * Copyright (C) 2013 Marko Zivkovic - * * Contact Information: marko88zivkovic at gmail dot com - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. This program 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 General - * Public License for more details. You should have received a copy of the - * GNU General Public License along with this program; if not, write to the Free - * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. This program 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 General + * Public License for more details. You should have received a copy of the + * GNU General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. - * - * * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic * during his Bachelor thesis at the computer science department of ETH Zurich, * in the year 2013 and/or during future work. - * * It is a reengineered version of XLogo written by Loic Le Coq, published * under the GPL License at http://xlogo.tuxfamily.org/ - * * Contents of this file were entirely written by Marko Zivkovic */ @@ -32,19 +27,11 @@ import java.awt.Font; import java.awt.GraphicsEnvironment; import java.awt.Toolkit; import java.io.File; -import java.io.IOException; import java.io.Serializable; -import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import xlogo.Logo; -import xlogo.messages.MessageKeys; -import xlogo.messages.async.dialog.DialogMessenger; -import xlogo.storage.Storable; -import xlogo.storage.StorableObject; -import xlogo.storage.WSManager; +import xlogo.interfaces.Observable; +import xlogo.interfaces.PropertyChangePublisher; import xlogo.storage.global.GlobalConfig; import xlogo.storage.workspace.WorkspaceConfig; import xlogo.utils.Utils; @@ -53,163 +40,68 @@ import xlogo.utils.Utils; * This Config is user specific. It is stored in the {@link #workspaceLocation} in the user's folder. * @author Marko Zivkovic */ -public class UserConfig extends StorableObject implements Serializable -{ - private static final long serialVersionUID = 8897730869795295485L; +public class UserConfig implements Serializable, Observable { - public static final String DEFAULT_USER = "Default User"; + private static final long serialVersionUID = 8897730869795295485L; + + /* + * Available Fonts + * TODO move them to a more meaningful place + */ // This was initially in Panel_Font, which is not used anymore. - public static final Font[] fontes = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); + public static final Font[] fontes = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); - static public int police_id(Font font) - { - for (int i = 0; i < fontes.length; i++) - { + public static int police_id(Font font) { // TODO linear search ... can be hashed + for (int i = 0; i < fontes.length; i++) { if (fontes[i].getFontName().equals(font.getFontName())) return i; } return 0; } - + + + /* + * Constants + */ /** * Name of the virtual user * @see #isVirtual() * @see #createVirtualUser() */ - public static final String VIRTUAL_USER = "Guest"; - - private static final String SRC_DIR_NAME = "src"; - private static final String BACKUPS_DIR_NAME = "backups"; - private static final String CONTEST_DIR_NAME = "contest"; - private static final String COMMAND_LINE_RECORD_NAME = "commandLineHistory"; + public static final String DEFAULT_USER = "Default User"; + public static final File DEFAULT_LOCATION = WorkspaceConfig.DEFAULT_DIRECTORY; + public static final File DEFAULT_DIRECTORY = new File(DEFAULT_LOCATION + File.separator + DEFAULT_USER); + + private static final String SRC_DIR_NAME = "src"; + private static final String BACKUPS_DIR_NAME = "backups"; + private static final String CONTEST_DIR_NAME = "contest"; + private static final String COMMAND_LINE_RECORD_NAME = "commandLineHistory"; + - protected UserConfig() - { - super(); - } - /* - * Static constructors + * Transient Fields */ - /** - * A virtual user can enter the application in a virtual workspace without having an actual user account on the file system. Hence nothing will be stored. - * A regular user (not virtual) will have his own folder in a regular workspace on the file system and all his preferences and files are stored there. - * To create a regular user, use {@link #createNewUser(File, String)}, - * to load a regular user from the file system, user {@link #loadUser(File, String)}}. - * @see #isVirtual() - * @return a virtual workspace - */ - public static UserConfig createVirtualUser() - { - UserConfig vuc = new UserConfig(); - vuc.makeVirtual(); - return vuc; + private transient File userDir; + + public void setDirectory(File location) { + this.userDir = location; } - /** - * Loads the UserConfig from the specified user, or creates it, if it's missing or corrupted. - * If workspace specifies a virtual workspace, a virtual user is created instead. In that case the error manager is informed. - * @param workspace - * @param username - * @return the loaded UserConfig - * @throws IOException - */ - public static UserConfig loadUser(WorkspaceConfig workspace, String username) - { - if(workspace.isVirtual()) - return createVirtualUser(); - - if (!WSManager.isWorkspaceDirectory(workspace.getLocation())) - throw new IllegalArgumentException(workspace.toString() + " is not a valid Workspace."); - - File userDirectory = getDirectory(workspace.getLocation(), username); - File userConfigFile = getFile(userDirectory, UserConfig.class); - - UserConfig userConfig = null; - - if(userConfigFile.exists()) - { - try - { - userConfig = (UserConfig) loadObject(userConfigFile); - userConfig.setLocation(userDirectory); - }catch(ClassNotFoundException e1) { } // this won't happen - catch(IOException e2) { - DialogMessenger.getInstance().dispatchError("Workspace Error", "Could not load user config file: " + e2.toString()); - } - } - - // If file is missing, it will be created again. - if (userConfig == null) - userConfig = createNewUser(workspace, username); - - return userConfig; + public File getDirectory() { + return userDir; } /** - * If workspace is valid, then a new userSpace is created within that workspace. - * All necessary files and directories are created. - *

- * If a UserConfig file for the specified user already exists, it will be overwritten. - * If an error occurs while creating the file, the userConfig is just returned without saving to file. - * TODO In that case, the global ErrorManager is informed. - * @param workspace - must exist physically - * @param username - * @return the created UserConfig - or null, if one of the arguments was not legal + * The user's name is the name of the folder, where the user data is stored + * @return */ - public static UserConfig createNewUser(WorkspaceConfig workspace, String username) - { - if(workspace.isVirtual()) - return createVirtualUser(); - - if (!WSManager.isWorkspaceDirectory(workspace.getLocation())) - { - DialogMessenger.getInstance().dispatchError( - Logo.messages.getString(MessageKeys.WS_ERROR_TITLE), - Logo.messages.getString(MessageKeys.WS_DOES_NOT_EXIST)); - return null; - } - - if(!Storable.checkLegalName(username)) - { - DialogMessenger.getInstance().dispatchError( - Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE), - Logo.messages.getString(MessageKeys.ILLEGAL_NAME)); - return null; - } - - File userDirectory = getDirectory(workspace.getLocation(), username); - - if(!userDirectory.exists()) - { - userDirectory.mkdirs(); - } - - // userDirectory exists - - //File userFile = getFile(userDirectory, UserConfig.class); - UserConfig userConfig = new UserConfig(); - try { - userConfig.setLocation(userDirectory); - userConfig.store(); - } catch (IOException e) { - // Best effort. Continue without saving to disk. - System.out.print("Could not create UserConfig file: " + e.toString()); - } - - return userConfig; - } - - public String getUserName() - { - if (isVirtual()) - return VIRTUAL_USER; + public String getUserName() { String result = null; - if(getLocation() != null) - result = getLocation().getName(); + if (getDirectory() != null) + result = getDirectory().getName(); return result; } @@ -220,76 +112,48 @@ public class UserConfig extends StorableObject implements Serializable /** * @return ../WorkspaceDir/UserDir/src/ or null if {@link #isVirtual()} */ - public File getSourceDirectory() - { - if (isVirtual()) - return null; - return new File(getLocation().toString() + File.separator + SRC_DIR_NAME); + public File getSourceDirectory() { + return new File(getDirectory().toString() + File.separator + SRC_DIR_NAME); } /** * @param fileName - without extension * @return */ - public File getLogoFilePath(String fileName) - { - if(isVirtual()) - return null; - - String path = getSourceDirectory().toString() - + File.separator + fileName - + GlobalConfig.LOGO_FILE_EXTENSION; + public File getLogoFilePath(String fileName) { + String path = getSourceDirectory().toString() + File.separator + fileName + GlobalConfig.LOGO_FILE_EXTENSION; return new File(path); } - + /** * @return ../WorkspaceDir/UserDir/backups/ or null if {@link #isVirtual()} */ - public File getBackupDirectory() - { - if (isVirtual()) - return null; - return new File(getLocation().toString() + File.separator + BACKUPS_DIR_NAME); + public File getBackupDirectory() { + return new File(getDirectory().toString() + File.separator + BACKUPS_DIR_NAME); } /** * @param fileName - without extension * @return */ - public File getFileBackupDir(String fileName) - { - if (isVirtual()) - return null; - String path = getBackupDirectory().toString() - + File.separator + fileName; + public File getFileBackupDir(String fileName) { + String path = getBackupDirectory().toString() + File.separator + fileName; return new File(path); } - public File getContestDir() - { - if (isVirtual()) - return null; - return new File(getLocation().toString() + File.separator + CONTEST_DIR_NAME); + public File getContestDir() { + return new File(getDirectory().toString() + File.separator + CONTEST_DIR_NAME); } - public File getContestFilePath(String fileName) - { - if (isVirtual()) - return null; - String path = getContestDir().toString() - + File.separator + SRC_DIR_NAME - + File.separator + fileName + public File getContestFilePath(String fileName) { + String path = getContestDir().toString() + File.separator + SRC_DIR_NAME + File.separator + fileName + GlobalConfig.LOGO_FILE_EXTENSION; return new File(path); } - public File getContestFileDir(String fileName) - { - if (isVirtual()) - return null; - String path = getContestDir().toString() - + File.separator + fileName; + public File getContestFileDir(String fileName) { + String path = getContestDir().toString() + File.separator + fileName; return new File(path); } @@ -297,10 +161,8 @@ public class UserConfig extends StorableObject implements Serializable * @param fileName * @return File descriptor for a backup file with the current timestamp */ - public File getBackupFilePath(String fileName) - { - String path = getFileBackupDir(fileName) - + File.separator + getTimeStamp() + public File getBackupFilePath(String fileName) { + String path = getFileBackupDir(fileName) + File.separator + Utils.getTimeStamp() + GlobalConfig.LOGO_FILE_EXTENSION; return new File(path); @@ -310,84 +172,34 @@ public class UserConfig extends StorableObject implements Serializable * @param fileName * @return File descriptor for a contest/record file with the current timestamp */ - public File getRecordFilePath(String fileName) - { - String path = getContestFileDir(fileName) - + File.separator + getTimeStamp() + public File getRecordFilePath(String fileName) { + String path = getContestFileDir(fileName) + File.separator + Utils.getTimeStamp() + GlobalConfig.LOGO_FILE_EXTENSION; return new File(path); } - public File getCommandLineContestFile() - { - if (isVirtual()) - return null; - String path = getContestDir().toString() - + File.separator + COMMAND_LINE_RECORD_NAME + public File getCommandLineContestFile() { + String path = getContestDir().toString() + File.separator + COMMAND_LINE_RECORD_NAME + GlobalConfig.LOGO_FILE_EXTENSION; return new File(path); } - public static final String dateTimeFormat = "yyyy-MM-dd-HH-mm-ss"; - public static final String timeFormat = "HH:mm:ss"; - public static final String minSecFormat = "mm:ss"; - - public static String getTimeStamp() - { - SimpleDateFormat sdf = new SimpleDateFormat(); - sdf.applyPattern(dateTimeFormat); - Calendar cal = Calendar.getInstance(); - return sdf.format(cal.getTime()); - } - - /** - * @param millis - * @return yyyy-MM-dd-HH-mm-ss - */ - public static String getDateTimeString(long millis) - { - SimpleDateFormat sdf = new SimpleDateFormat(); - sdf.applyPattern(dateTimeFormat); - return sdf.format(new Date(millis)); - } - /** - * @param millis - * @return HH:mm:ss + * The list of files in the UserSpace. */ - public static String getTimeString(long millis) - { - SimpleDateFormat sdf = new SimpleDateFormat(); - sdf.applyPattern(timeFormat); - return sdf.format(new Date(millis)); - } + private ArrayList fileOrder = new ArrayList(); - public static String getMinSec(long millis) - { - SimpleDateFormat sdf = new SimpleDateFormat(); - sdf.applyPattern(minSecFormat); - return sdf.format(new Date(millis)); - } - - /** - * The list of files UserSpace in the workspace. - */ - private ArrayList fileOrder = new ArrayList(); - - public void addFile(String fileName) - { + public void addFile(String fileName) { if (!fileOrder.contains(fileName)) fileOrder.add(fileName); } - public void removeFile(String fileName) - { + public void removeFile(String fileName) { fileOrder.remove(fileName); } - public void renameFile(String oldName, String newName) - { + public void renameFile(String oldName, String newName) { int i = fileOrder.indexOf(oldName); if (i < 0) return; @@ -395,158 +207,155 @@ public class UserConfig extends StorableObject implements Serializable fileOrder.add(i, newName); } - public ArrayList getFileOrder() - { + public ArrayList getFileOrder() { return fileOrder; } - + // The below properties are essentially copied from the old XLogo // they might get (re)moved or changed - + /** * Drawing Quality */ - private DrawQuality quality= DrawQuality.HIGH; - + private DrawQuality quality = DrawQuality.HIGH; + /** * This integer represents the selected looknfeel for the appplication */ - private LookAndFeel looknfeel=LookAndFeel.JAVA; - + private LookAndFeel looknfeel = LookAndFeel.JAVA; + /** * This integer represents the drawing area width */ - private int imageWidth= Math.max(1000, Toolkit.getDefaultToolkit().getScreenSize().width); + private int imageWidth = Math.max(1000, Toolkit.getDefaultToolkit().getScreenSize().width); /** * This integer represents the drawing area height */ - private int imageHeight= Math.max(1000, Toolkit.getDefaultToolkit().getScreenSize().height); + private int imageHeight = Math.max(1000, Toolkit.getDefaultToolkit().getScreenSize().height); /** * Integer that represents the active turtle's shape */ - private int activeTurtle=0; + private int activeTurtle = 0; /** * Maximum allowed pen size */ - private int maxPenWidth=-1; + private int maxPenWidth = -1; /** * This boolean indicates if the drawing area has to be cleaned when the editor is left. */ - private boolean eraseImage = false; + private boolean eraseImage = false; /** * This boolean indicates if variables are deleted when closing the editor. */ - private boolean clearVariables = false; + private boolean clearVariables = false; /** * Max value for the turtles number */ - private int maxTurtles = 16; + private int maxTurtles = 16; /** * Default screen color: This color is used when the primitive "clearscreen" is used. */ - private Color screencolor=Color.WHITE; + private Color screencolor = Color.WHITE; /** * Default pen color: This color is used when the primitive "clearscreen" is used. */ - private Color pencolor=Color.BLACK; - - + private Color pencolor = Color.BLACK; + /** * This represents the pen shape */ - private PenShape penShape = PenShape.SQUARE; - + private PenShape penShape = PenShape.SQUARE; + /** * This integer represents the turtle's speed for drawing
* Slow: 100 * Fast: 0 */ - private int turtleSpeed=0; + private int turtleSpeed = 0; /** * This String contains the command to execute on Startup.
* Configured in the dialog box "startup files" - */ - private String a_executer=""; + */ + private String a_executer = ""; /** Marko Zivkovic : this is used for a few Logo Commands that operate with files
* Loic: * The default folder for the user when the application starts.
* This folder corresponds to the last opened or saved file in format lgo // Marko : not true anymore */ - private String defaultFolder=Utils.rajoute_backslash(System.getProperty("user.home")); + private String defaultFolder = Utils.rajoute_backslash(System.getProperty("user.home")); /** * This boolean indicates if the grid is enabled */ - private boolean drawGrid=false; + private boolean drawGrid = false; /** * This integer represents the X distance for the grid */ - private int XGrid=20; + private int XGrid = 20; /** * This integer represents the Y distance for the grid */ - private int YGrid=20; + private int YGrid = 20; /** * This integer represents the grid Color */ - private int gridColor=Color.DARK_GRAY.getRGB(); + private int gridColor = Color.DARK_GRAY.getRGB(); /** * This boolean indicates if the X axis is enabled */ - private boolean drawXAxis=false; + private boolean drawXAxis = false; /** * This boolean indicates if the Y axis is enabled */ - private boolean drawYAxis=false; + private boolean drawYAxis = false; /** * This integer represents the axis Color */ - private int axisColor=new Color(255,0,102).getRGB(); + private int axisColor = new Color(255, 0, 102).getRGB(); /** * This integer represents the X distance between two divisions on the X Axis */ - private int XAxis=30; + private int XAxis = 30; /** * This integer represents the X distance between two divisions on the Y Axis */ - private int YAxis=30; - + private int YAxis = 30; + /** * This long represents the hour of XLogo starting */ - private long heure_demarrage; + private long heure_demarrage; /** * Color for the border around drawing area */ - private Color borderColor=null; + private Color borderColor = null; /** * The Image is for the border around drawing area */ - private String borderImageSelected="background.png"; - + private String borderImageSelected = "background.png"; + /** * This Vector contains all images added by the user for image Border */ - private ArrayList borderExternalImage=new ArrayList(); + private ArrayList borderExternalImage = new ArrayList(); /** * The default image defined by default that are included in XLogo */ - private String[] borderInternalImage={"background.png"}; + private String[] borderInternalImage = { "background.png" }; /** * This String represents the main command accessible with the button play in the toolbar */ - private String mainCommand=""; + private String mainCommand = ""; /** * This boolean indicates if Xlogo must launch the main Command on XLogo startup * It overrides the String a_executer */ - private boolean autoLaunch=false; - - + private boolean autoLaunch = false; + /** * TCP Port for robotics and network flows */ - private int tcpPort = 1948; + private int tcpPort = 1948; public int searchInternalImage(String st) { for (int i = 0; i < borderInternalImage.length; i++) { @@ -555,161 +364,137 @@ public class UserConfig extends StorableObject implements Serializable } return -1; } - + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Generated Getters & Setters * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - + public DrawQuality getQuality() { return quality; } - - + public void setQuality(DrawQuality quality) { this.quality = quality; - makeDirty(); + publisher.publishEvent(UserProperty.QUALITY); } - + public LookAndFeel getLooknfeel() { return looknfeel; } - + public void setLooknfeel(LookAndFeel looknfeel) { this.looknfeel = looknfeel; - makeDirty(); + publisher.publishEvent(UserProperty.LOOKNFEEL); } - - + public int getImageWidth() { return imageWidth; } - - + public void setImageWidth(int imageWidth) { this.imageWidth = imageWidth; - makeDirty(); + publisher.publishEvent(UserProperty.IMAGE_WIDTH); } - - + public int getImageHeight() { return imageHeight; } - - + public void setImageHeight(int imageHeight) { this.imageHeight = imageHeight; - makeDirty(); + publisher.publishEvent(UserProperty.IMAGE_HEIGHT); } - + public int getActiveTurtle() { return activeTurtle; } - - + public void setActiveTurtle(int activeTurtle) { this.activeTurtle = activeTurtle; - makeDirty(); + publisher.publishEvent(UserProperty.ACTIVE_TURTLE); } - - + public int getMaxPenWidth() { return maxPenWidth; } - - + public void setMaxPenWidth(int maxPenWidth) { this.maxPenWidth = maxPenWidth; - makeDirty(); + publisher.publishEvent(UserProperty.MAX_PEN_WIDTH); } - - + public boolean isEraseImage() { return eraseImage; } - - + public void setEraseImage(boolean eraseImage) { this.eraseImage = eraseImage; - makeDirty(); + publisher.publishEvent(UserProperty.ERASE_IMAGE); } - - + public boolean isClearVariables() { return clearVariables; } - - + public void setClearVariables(boolean clearVariables) { this.clearVariables = clearVariables; - makeDirty(); + publisher.publishEvent(UserProperty.CLEAR_VARIABLES); } - - + public int getMaxTurtles() { return maxTurtles; } - - + public void setMaxTurtles(int maxTurtles) { this.maxTurtles = maxTurtles; - makeDirty(); + publisher.publishEvent(UserProperty.MAX_TURTLES); } - - + public Color getScreencolor() { return screencolor; } - - + public void setScreencolor(Color screencolor) { this.screencolor = screencolor; - makeDirty(); + publisher.publishEvent(UserProperty.SCREENCOLOR); } - - + public Color getPencolor() { return pencolor; } - - + public void setPencolor(Color pencolor) { this.pencolor = pencolor; - makeDirty(); + publisher.publishEvent(UserProperty.PENCOLOR); } - - + public PenShape getPenShape() { return penShape; } - - + public void setPenShape(PenShape penShape) { this.penShape = penShape; - makeDirty(); + publisher.publishEvent(UserProperty.PEN_SHAPE); } - - + public int getTurtleSpeed() { return turtleSpeed; } - - + public void setTurtleSpeed(int turtleSpeed) { this.turtleSpeed = turtleSpeed; - makeDirty(); + publisher.publishEvent(UserProperty.TURTLE_SPEED); } - - + public String getA_executer() { return a_executer; } - - + public void setA_executer(String a_executer) { this.a_executer = a_executer; - makeDirty(); + publisher.publishEvent(UserProperty.A_EXECUTER); } - + /** * Default : User source directory * @return the current defaultDirectory @@ -720,199 +505,217 @@ public class UserConfig extends StorableObject implements Serializable return getSourceDirectory().toString(); return defaultFolder; } - public void setDefaultFolder(String defaultFolder) { this.defaultFolder = defaultFolder; - makeDirty(); + publisher.publishEvent(UserProperty.DEFAULT_FOLDER); } - - - public boolean isDrawGrid() { return drawGrid; } - - + public void setDrawGrid(boolean drawGrid) { this.drawGrid = drawGrid; - makeDirty(); + publisher.publishEvent(UserProperty.DRAW_GRID); } - - + public int getXGrid() { return XGrid; } - - + public void setXGrid(int xGrid) { XGrid = xGrid; - makeDirty(); + publisher.publishEvent(UserProperty.X_GRID); } - - + public int getYGrid() { return YGrid; } - - + public void setYGrid(int yGrid) { YGrid = yGrid; - makeDirty(); + publisher.publishEvent(UserProperty.Y_GRID); } - - + public int getGridColor() { return gridColor; } - - + public void setGridColor(int gridColor) { this.gridColor = gridColor; - makeDirty(); + publisher.publishEvent(UserProperty.GRID_COLOR); } - - + public boolean isDrawXAxis() { return drawXAxis; } - - + public void setDrawXAxis(boolean drawXAxis) { this.drawXAxis = drawXAxis; - makeDirty(); + publisher.publishEvent(UserProperty.DRAW_X_AXIS); } - - + public boolean isDrawYAxis() { return drawYAxis; } - - + public void setDrawYAxis(boolean drawYAxis) { this.drawYAxis = drawYAxis; - makeDirty(); + publisher.publishEvent(UserProperty.DRAW_Y_AXIS); } - - + public int getAxisColor() { return axisColor; } - - + public void setAxisColor(int axisColor) { this.axisColor = axisColor; - makeDirty(); + publisher.publishEvent(UserProperty.AXIS_COLOR); } - - + public int getXAxis() { return XAxis; } - - + public void setXAxis(int xAxis) { XAxis = xAxis; - makeDirty(); + publisher.publishEvent(UserProperty.X_AXIS); } - - + public int getYAxis() { return YAxis; } - - + public void setYAxis(int yAxis) { YAxis = yAxis; - makeDirty(); + publisher.publishEvent(UserProperty.Y_AXIS); } - - + public long getHeure_demarrage() { return heure_demarrage; } - - + public void setHeure_demarrage(long heure_demarrage) { this.heure_demarrage = heure_demarrage; - makeDirty(); + publisher.publishEvent(UserProperty.HEURE_DEMARRAGE); } - public Color getBorderColor() { return borderColor; } - - + public void setBorderColor(Color borderColor) { this.borderColor = borderColor; - makeDirty(); + publisher.publishEvent(UserProperty.BORDER_COLOR); } - - + public String getBorderImageSelected() { return borderImageSelected; } - - + public void setBorderImageSelected(String borderImageSelected) { this.borderImageSelected = borderImageSelected; - makeDirty(); + publisher.publishEvent(UserProperty.BORDER_IMAGE_SELECTED); } - - + public ArrayList getBorderExternalImage() { return borderExternalImage; } - - + public void setBorderExternalImage(ArrayList borderExternalImage) { this.borderExternalImage = borderExternalImage; - makeDirty(); + publisher.publishEvent(UserProperty.BORDER_EXTERNAL_IMAGE); } - - + public String[] getBorderInternalImage() { return borderInternalImage; } - - + public void setBorderInternalImage(String[] borderInternalImage) { this.borderInternalImage = borderInternalImage; - makeDirty(); + publisher.publishEvent(UserProperty.BORDER_INTERNAL_IMAGE); } - - + public String getMainCommand() { return mainCommand; } - - + public void setMainCommand(String mainCommand) { this.mainCommand = mainCommand; - makeDirty(); + publisher.publishEvent(UserProperty.MAIN_COMMAND); } - - + public boolean isAutoLaunch() { return autoLaunch; } - - + public void setAutoLaunch(boolean autoLaunch) { this.autoLaunch = autoLaunch; - makeDirty(); + publisher.publishEvent(UserProperty.AUTO_LAUNCH); } - + public int getTcpPort() { return tcpPort; } - - + public void setTcpPort(int tcpPort) { this.tcpPort = tcpPort; - makeDirty(); + publisher.publishEvent(UserProperty.TCP_PORT); + } + + /* + * Property Change Listeners + */ + + public enum UserProperty { + QUALITY, + LOOKNFEEL, + IMAGE_WIDTH, + IMAGE_HEIGHT, + ACTIVE_TURTLE, + MAX_PEN_WIDTH, + ERASE_IMAGE, + CLEAR_VARIABLES, + MAX_TURTLES, + SCREENCOLOR, + PENCOLOR, + PEN_SHAPE, + TURTLE_SPEED, + A_EXECUTER, + DEFAULT_FOLDER, + DRAW_GRID, + X_GRID, + Y_GRID, + GRID_COLOR, + DRAW_X_AXIS, + DRAW_Y_AXIS, + AXIS_COLOR, + X_AXIS, + Y_AXIS, + HEURE_DEMARRAGE, + BORDER_COLOR, + BORDER_IMAGE_SELECTED, + BORDER_EXTERNAL_IMAGE, + BORDER_INTERNAL_IMAGE, + MAIN_COMMAND, + AUTO_LAUNCH, + TCP_PORT; + } + + private transient PropertyChangePublisher publisher = new PropertyChangePublisher<>(); + + @Override + public void addPropertyChangeListener(UserProperty property, PropertyChangeListener listener) { + if (publisher == null){ + publisher = new PropertyChangePublisher<>(); + } + publisher.addPropertyChangeListener(property, listener); + } + + @Override + public void removePropertyChangeListener(UserProperty property, PropertyChangeListener listener) { + publisher.removePropertyChangeListener(property, listener); } } diff --git a/logo/src/xlogo/storage/workspace/Serializer.java b/logo/src/xlogo/storage/workspace/Serializer.java new file mode 100644 index 0000000..9c3e04d --- /dev/null +++ b/logo/src/xlogo/storage/workspace/Serializer.java @@ -0,0 +1,6 @@ +package xlogo.storage.workspace; + +public interface Serializer { + public String serialize2String(T target); + public T deserialize(String serialized); +} diff --git a/logo/src/xlogo/storage/workspace/SyntaxHighlightConfig.java b/logo/src/xlogo/storage/workspace/SyntaxHighlightConfig.java index b4fcf10..52b9929 100644 --- a/logo/src/xlogo/storage/workspace/SyntaxHighlightConfig.java +++ b/logo/src/xlogo/storage/workspace/SyntaxHighlightConfig.java @@ -92,19 +92,19 @@ public class SyntaxHighlightConfig implements Serializable{ this.primitiveStyle = primitiveStyle; } - public int getOperatorColor() { + public int getOperandColor() { return operatorColor; } - public void setOperatorColor(int operatorColor) { + public void setOperandColor(int operatorColor) { this.operatorColor = operatorColor; } - public int getOperatorStyle() { + public int getOperandStyle() { return operatorStyle; } - public void setOperatorStyle(int operatorStyle) { + public void setOperandStyle(int operatorStyle) { this.operatorStyle = operatorStyle; } diff --git a/logo/src/xlogo/storage/workspace/WorkspaceConfig.java b/logo/src/xlogo/storage/workspace/WorkspaceConfig.java index 6e52877..800270b 100644 --- a/logo/src/xlogo/storage/workspace/WorkspaceConfig.java +++ b/logo/src/xlogo/storage/workspace/WorkspaceConfig.java @@ -38,15 +38,13 @@ import java.util.TreeMap; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import xlogo.AppSettings; -import xlogo.Logo; -import xlogo.messages.MessageKeys; -import xlogo.messages.async.dialog.DialogMessenger; -import xlogo.storage.Storable; +import xlogo.interfaces.Observable; +import xlogo.interfaces.PropertyChangePublisher; import xlogo.storage.StorableObject; import xlogo.storage.WSManager; import xlogo.storage.global.GlobalConfig; import xlogo.storage.user.UserConfig; +import xlogo.storage.user.UserConfig.UserProperty; /** * WorkspaceConfig maintains a workspace (i.e. a "class room") that consists of several projects or "Users". @@ -63,304 +61,190 @@ import xlogo.storage.user.UserConfig; * If a user is deleted, it is deleted only logically, so it can be reintegrated later again. * @author Marko Zivkovic */ -public class WorkspaceConfig extends StorableObject implements Serializable { +public class WorkspaceConfig implements Serializable, Observable { private static final long serialVersionUID = -3554871695113998509L; - /** - * Name of the virtual workspace + /* + * Constants */ - public static final String VIRTUAL_WORKSPACE = "Guest Workspace (no automatic save)"; + public static final String USB_DEFAULT_WORKSPACE = "XLogo4Schools"; public static final String USER_DEFAULT_WORKSPACE = "XLogo4Schools-Workspace"; - public static final int MAX_EMPTY_FILES = 4; - - private static Logger logger = LogManager.getLogger(WorkspaceConfig.class.getSimpleName()); + public static final int MAX_EMPTY_FILES = 4; - public static File getDefaultWorkspaceDirectory(){ - File location = GlobalConfig.getDefaultLocation(); - return getDirectory(location, USER_DEFAULT_WORKSPACE); - } + public static final File DEFAULT_LOCATION = GlobalConfig.DEFAULT_LOCATION; + public static final File DEFAULT_DIRECTORY = new File(DEFAULT_LOCATION + File.separator + USER_DEFAULT_WORKSPACE); - public static File getDefaultWorkspaceLocation(){ - return GlobalConfig.getDefaultLocation(); - } + public static final Font DEFAULT_FONT = new Font("dialog", Font.PLAIN, 14); - public static boolean isSpecialWorkspace(String workspaceName, String location) { - return isVirtualWorkspace(workspaceName) || isDefaultWorkspace(workspaceName, location); - } - - public static boolean isSpecialWorkspace(String workspaceName, File location) { - return isVirtualWorkspace(workspaceName) || isDefaultWorkspace(workspaceName, location); - } + private static Logger logger = LogManager.getLogger(WorkspaceConfig.class.getSimpleName()); - public static boolean isVirtualWorkspace(String workspaceName) { - return VIRTUAL_WORKSPACE.equals(workspaceName); - } + /* + * Transient Fields + */ - public static boolean isDefaultWorkspace(String workspaceName, String location) { - return isDefaultWorkspace(workspaceName, new File(location)); - } + private transient File wsDir; - public static boolean isDefaultWorkspace(String workspaceName, File location) { - return workspaceName.equals(USER_DEFAULT_WORKSPACE) && - location.equals(getDefaultWorkspaceLocation()); + public File getDirectory() { + return wsDir; } - - protected WorkspaceConfig() { - super(); - userList = new ArrayList(); - language = Language.LANGUAGE_ENGLISH; - font = new Font("dialog", Font.PLAIN, 14); // TODO on access check if it is null. - // TODO what if incompatible? - AppSettings.getInstance().setFont(font); - syntaxHighlightingStyles = new SyntaxHighlightConfig(); + + public void setDirectory(File wsDir) { + this.wsDir = wsDir; } - /** - * @return - * @throws IllegalStateException if this is not virtual and {@link #getLocation()} returns null - */ - public String getWorkspaceName() throws IllegalStateException { - if (isVirtual()) - return VIRTUAL_WORKSPACE; - - File wsDir = getLocation(); + public String getWorkspaceName(){ + File wsDir = getDirectory(); if (wsDir == null) throw new IllegalStateException("Name is not available because location is null."); return wsDir.getName(); } /* - * Static constructors + * Persistent Fields */ - private static WorkspaceConfig virtualWS; - /** - * A virtual user can enter the application in a virtual workspace without having an actual user account on the file system. Hence nothing will be stored. - * A regular user (not virtual) will have his own folder in a regular workspace on the file system and all his preferences and files are stored there. - * To create a regular workspace, use {@link #createNewWorkspace(File, String)}, - * to load a regular workspace from the file system, use {@link #loadWorkspace(File)}}. - * @see #isVirtual() - * @return a virtual workspace + private ArrayList userList = new ArrayList(); + private String lastActiveUser; + private NumberOfBackups numberOfBackups = NumberOfBackups.INFINITE; + private Language language; + private boolean allowUserCreation = true; + private ContestConfig contestSettings; // Contest //TODO create options in workspace settings + private SyntaxHighlightConfig syntaxHighlightingStyles; // TODO = new SyntaxHighlightStyles(); + private Font font; // This font is the default font for all menus ... in XLogo Application + + /* + * Constructor */ - public static WorkspaceConfig createVirtualWorkspace() { - logger.trace("Creating virtual workspace."); - if (virtualWS == null) { - virtualWS = new WorkspaceConfig(); - virtualWS.makeVirtual(); - } - return virtualWS; - } - @Override - protected void makeVirtual() { - super.makeVirtual(); + public WorkspaceConfig() { + super(); userList = new ArrayList(); - userList.add(UserConfig.VIRTUAL_USER); - lastActiveUser = UserConfig.VIRTUAL_USER; - try { - enterUserSpace(UserConfig.VIRTUAL_USER); - } - catch (IOException e) { /* Does not happen */} } - private static WorkspaceConfig createWorkspace(File dir, String workspaceName) throws IOException { - if (!Storable.checkLegalName(workspaceName)) { - DialogMessenger.getInstance().dispatchError(Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE), - Logo.messages.getString(MessageKeys.ILLEGAL_NAME)); + + public File getUserDirectroy(String username) { + if (!existsUserLogically(username)) return null; - } - File wsd = getDirectory(dir, workspaceName); - WorkspaceConfig wsc = new WorkspaceConfig(); - wsc.setLocation(wsd); - return wsc; + return StorableObject.getDirectory(getDirectory(), username); } - + /** - * @param dir - * @param workspaceName + * Produces a list of user names by reading the contents of the current workspace directory. + * The users in this list may contain users that have been deleted logically before. * @return - * @throws IOException */ - public static WorkspaceConfig createNewWorkspace(File dir, String workspaceName) throws IOException { - logger.trace("Creating workspace " + workspaceName + " at " + dir.getAbsolutePath()); - WorkspaceConfig wsc = createWorkspace(dir, workspaceName); - wsc.store(); - return wsc; + public ArrayList getPhysicalUserList() { + + ArrayList users = new ArrayList(); + + for (File dir : WSManager.listDirectories(getDirectory())) { + if (WSManager.isUserDirectory(dir)) { + users.add(dir.getName()); + } + } + return users; } - - /** - * Physically storing this workspace is deferred until explicitly disabled. - * This is used to temporarily make the USB Workspace and the Default Workspace available, but only store it when {@link #store()} is called the next time. - * @param dir - * @return - * @throws IOException + + /* + * active user */ - public static WorkspaceConfig createDeferredWorkspace(File dir, String workspaceName) throws IOException { - logger.trace("Creating deferred workspace " + workspaceName + " at " + dir.getAbsolutePath()); - WorkspaceConfig wsc = createWorkspace(dir, workspaceName); - wsc.setStoringDeferred(true); - return wsc; - } - - private transient boolean isStoringDeferred = false; - public void setStoringDeferred(boolean val) { - this.isStoringDeferred = val; - } + private transient StorableObject activeUser; - @Override - protected void makeDirty(){ - super.makeDirty(); - setStoringDeferred(false); + public StorableObject getActiveUser() { + return activeUser; } - @Override - public void store() throws IOException { - if (!isStoringDeferred) { - super.store(); - } - else { - isStoringDeferred = false; + public void enterInitialUserSpace() throws IOException{ + String user = getLastActiveUser(); + if (user != null && userList.contains(user)){ + enterUserSpace(user); } } /** - * @see #loadWorkspace(File) - * @param dir - location of the workspace - * @param workspaceName - * @return - * @throws IOException + * @throws IOException If the old userConfig could not be stored. */ - public static WorkspaceConfig loadWorkspace(File dir, String workspaceName) throws IOException { - File wsc = getDirectory(dir, workspaceName); - return loadWorkspace(wsc); + public void enterUserSpace(String username) throws IOException { + enterUserSpace(retrieveUserSpace(username)); } - /** - * @param workspaceDir - * @return Load an existing workspace from the file system. - * If workspaceDir specifies a {@link WorkspaceConfig#VIRTUAL_WORKSPACE}, the virtual workspace is returned instead. - * @throws IOException - */ - public static WorkspaceConfig loadWorkspace(File workspaceDir) throws IOException { - logger.trace("Loading workspace from " + workspaceDir.getAbsolutePath()); - if (workspaceDir.getName().equals(WorkspaceConfig.VIRTUAL_WORKSPACE)) { return createVirtualWorkspace(); } - - File wsf = getFile(workspaceDir, WorkspaceConfig.class); - - WorkspaceConfig wsc; - try { - wsc = (WorkspaceConfig) WorkspaceConfig.loadObject(wsf); - wsc.setLocation(workspaceDir); - return wsc; + public void enterUserSpace(StorableObject userConfig) throws IOException { + if(userConfig == activeUser){ + return; } - catch (ClassNotFoundException e) { - return null; + + if (activeUser != null) { + leaveUserSpace(); } + logger.trace("Entering user space: " + userConfig.get().getUserName()); + + activeUser = userConfig; + setLastActiveUser(userConfig.get().getUserName()); } - /* - * User list - */ - /** - * @see #getUserList() - */ - private ArrayList userList; - - /** - * The names of the logical users in the workspace - * @return + * @throws IOException If userConfig could not be stored. */ - public String[] getUserList() { - String[] users = new String[userList.size()]; - return userList.toArray(users); + public void leaveUserSpace() throws IOException { + logger.trace("Leaving user space: " + activeUser.get().getUserName()); + if (activeUser.isDirty()) + activeUser.store(); + activeUser = null; } - public File getUserDirectroy(String username) { - if (!existsUserLogically(username) || isVirtual()) - return null; + + protected StorableObject retrieveUserSpace(String username){ + StorableObject uc = getCachedUserSpace(username); + if (uc == null && getDirectory() != null){ + uc = WSManager.getUser(getDirectory(), username); + cacheUserSpace(username, uc); + } - return getDirectory(getLocation(), username); + return uc; } /** - * Create new user directory and UserConfig file in this workspace on the file system, and add it logically to the userList. - * If either existed, only the non-existing parts are created. - * To create a user in a virtual workspace will have no effect, but an error message is printed. - *

Has no effect if this is virtual. - * @param username + * User Cache + * + * UserConfigs that have already been created or loaded from disk. */ - public void createUser(String username) { - logger.trace("Creating user: " + username); - if (!Storable.checkLegalName(username)) { - DialogMessenger.getInstance().dispatchError(Logo.messages.getString(MessageKeys.NAME_ERROR_TITLE), - Logo.messages.getString(MessageKeys.ILLEGAL_NAME)); - return; - } - - if (isVirtual()) { - DialogMessenger.getInstance().dispatchError("Workspace Error", - "Attempt to create new user to virtual workspace."); - return; - } - - File userDir = getDirectory(getLocation(), username); - - if (!userDir.mkdirs() && !userDir.isDirectory()) { - DialogMessenger.getInstance().dispatchError("Workspace Error", - "Could not make required directories: " + userDir.toString()); - return; - } - - if (!existsUserLogically(username)) { - userList.add(username); - makeDirty(); - } - - // Make new user logically existent in workspace config file - try { - store(); - } - catch (IOException e) { - String title = AppSettings.getInstance().translate("general.error.title"); - String message = AppSettings.getInstance().translate("error.could.not.store.ws"); - DialogMessenger.getInstance().dispatchError(title, message); + private transient Map> cachedUserSpaces; + + private StorableObject getCachedUserSpace(String username) { + if (cachedUserSpaces == null){ + cachedUserSpaces= new TreeMap>(); } - if (!existsUserPhysically(username)){ - UserConfig.createNewUser(this, username); + return cachedUserSpaces.get(username); + } + + private void cacheUserSpace(String username, StorableObject wsc){ + if (cachedUserSpaces == null){ + cachedUserSpaces= new TreeMap>(); } - lastActiveUser = username; + cachedUserSpaces.put(username, wsc); } - /** - * Import a user directory from anywhere in the file system to this workspace. - * All files in the user directory are copied. Already existing files might get overwritten. - *

This has no effect if this is virtual. - * @param srcUserDir - a legal user directory anywhere on the file system - * @param destUsername - Existing files of targetUser are overwritten. If targetUser does not exist, it will be created first. - * @throws IllegalArgumentException - * @throws IOException - * @see WSManager#isUserDirectory(File) + /* + * User list */ - public void importUser(File srcUserDir, String destUsername) throws IllegalArgumentException, IOException { - logger.trace("Importing user '" + destUsername + "' from " + srcUserDir.getAbsolutePath()); - - if (isVirtual()) - return; - - if (!WSManager.isUserDirectory(srcUserDir)) - throw new IllegalArgumentException(); - - createUser(destUsername); - File targetUserDir = getDirectory(getLocation(), destUsername); - - WSManager.copyFullyRecursive(srcUserDir, targetUserDir); - lastActiveUser = destUsername; + public void addUser(String username) { + if (userList.contains(username)){ + logger.warn("Adding user name that is already present in user list. Ignore adding."); + return; + } + userList.add(username); + publisher.publishEvent(WorkspaceProperty.USER_LIST); + } + + public void addUser(StorableObject uc){ + String name = uc.get().getUserName(); + cacheUserSpace(name, uc); + addUser(name); } /** @@ -370,137 +254,33 @@ public class WorkspaceConfig extends StorableObject implements Serializable { public void removeUser(String username){ logger.trace("Removing user: " + username); if (existsUserLogically(username)){ - makeDirty(); + userList.remove(username); + cachedUserSpaces.remove(username); + publisher.publishEvent(WorkspaceProperty.USER_LIST); + } else { + userList.remove(username); + cachedUserSpaces.remove(username); } - userList.remove(username); - cachedUserSpaces.remove(username); - - if (activeUser != null && activeUser.getUserName().equals(username)){ + if (activeUser != null && activeUser.get().getUserName().equals(username)){ activeUser = null; - lastActiveUser = null; - makeDirty(); - } - } - - /** - * @param username - if this does not exists logically in the workspace, null is returned. - * @return a {@link UserConfig} generated from the file system. If this is a virtual workspace, a virtual user is created instead. - * @throws IOException if the UserConfig could not be loaded - * @see UserConfig#loadUser(File, String) - */ - public UserConfig loadUser(String username) throws IOException { - logger.trace("Loading user: " + username); - if (!existsUserLogically(username)) { - AppSettings as = AppSettings.getInstance(); - String title = as.translate("general.error.title"); - String msg1 = as.translate("error.attempt.load.inexistent.user"); - String msg2 = as.translate("error.suggest.try.to.import.user"); - DialogMessenger.getInstance().dispatchError(title, msg1 + username + ". " + msg2); - return null; - } - - if (isVirtual()) - return UserConfig.createVirtualUser(); - - // exists logically and is not virtual - - if (!existsUserPhysically(username)) { - // but it does exist logically => it must have been corrupted externally. - // => restore it. - if (!getLocation().mkdirs()) { - AppSettings as = AppSettings.getInstance(); - String title = as.translate("general.error.title"); - String msg = as.translate("error.could.not.make.directories"); - DialogMessenger.getInstance().dispatchError(title, msg); - return null; - } - // user creation requires existence of the workspace on file system - try { - store(); - } - catch (IOException e) { - AppSettings as = AppSettings.getInstance(); - String title = as.translate("general.error.title"); - String msg = as.translate("error.could.not.store.ws"); - DialogMessenger.getInstance().dispatchError(title, msg); - } - return UserConfig.createNewUser(this, username); - } - // exists physically - return UserConfig.loadUser(this, username); - } - - /** - * Produces a list of user names by reading the contents of the current workspace directory. - * The users in this list may contain users that have been deleted logically before. - * @return - */ - public ArrayList getPhysicalUserList() { - if (isVirtual()) - return new ArrayList(); - - ArrayList users = new ArrayList(); - - if (WSManager.isWorkspaceDirectory(getLocation())) { - AppSettings as = AppSettings.getInstance(); - String title = as.translate("general.error.title"); - String msg = as.translate("error.current.ws.deleted.I.will.recreate.it"); - DialogMessenger.getInstance().dispatchError(title, msg); - try { - store(); - } - catch (IOException e) { - String msg2 = as.translate("error.could.not.recreate.try.manually"); - DialogMessenger.getInstance().dispatchError(title, msg2); - return users; - } + setLastActiveUser(null); } - - for (File dir : WSManager.listDirectories(getLocation())) { - if (WSManager.isUserDirectory(dir)) { - users.add(dir.getName()); - } - } - return users; - } - - /** - * A user exists logically, if its name is known by the workspace. - * @param userName - * @return - */ - public boolean existsUserLogically(String username) { - return userList.contains(username); - } - - /** - * A user exists physically, if a folder with the user's name exists in this workspace and if it contains a UserConfig file. - * @param username - * @return - * @see WSManager#isUserDirectory(File) - */ - public boolean existsUserPhysically(String username) { - File userDir = getDirectory(getLocation(), username); - return WSManager.isUserDirectory(userDir); } /* * last active user */ - - private String lastActiveUser; - + /** * @return name of the last active user */ public String getLastActiveUser() { - if (lastActiveUser == null){ - if (userList.size() > 0){ + if (lastActiveUser == null) { + if (userList.size() > 0) { lastActiveUser = userList.get(0); } } - return lastActiveUser; } @@ -509,125 +289,50 @@ public class WorkspaceConfig extends StorableObject implements Serializable { * @param workspace */ public void setLastActiveUser(String username) { - if (existsUserLogically(username) && !username.equals(lastActiveUser)) { + if (existsUserLogically(username) && !username.equals(getLastActiveUser())) { lastActiveUser = new String(username); - makeDirty(); - } - } - - /* - * active user - */ - - private transient UserConfig activeUser; - - public UserConfig getActiveUser() { - return activeUser; - } - - public void enterInitialUserSpace() throws IOException{ - String user = getLastActiveUser(); - if (user != null){ - enterUserSpace(user); + publisher.publishEvent(WorkspaceProperty.LAST_ACTIVE_USER); } } /** - * @throws IOException If the old userConfig could not be stored. - */ - public void enterUserSpace(String username) throws IOException { - if (activeUser != null) { - leaveUserSpace(); - } - logger.trace("Entering user space: " + username); - - activeUser = retrieveUserSpace(username); - - setLastActiveUser(username); - } - - /** - * @throws IOException If userConfig could not be stored. - */ - public void leaveUserSpace() throws IOException { - logger.trace("Leaving user space: " + activeUser.getUserName()); - if (activeUser.isDirty()) - activeUser.store(); - activeUser = null; - } - - protected UserConfig retrieveUserSpace(String username){ - UserConfig uc = getCachedUserSpace(username); - if (uc != null){ - return uc; - } - - if (isVirtual()){ - uc = UserConfig.createVirtualUser(); - } else { - uc = UserConfig.loadUser(this, username); - } - - cacheUserSpace(username, uc); - return uc; - } - - /** - * UserConfigs that have already been created or loaded from disk. + * A user exists logically, if its name is known by the workspace. + * @param userName + * @return */ - private transient Map cachedUserSpaces; - - private UserConfig getCachedUserSpace(String username) { - if (cachedUserSpaces == null){ - cachedUserSpaces= new TreeMap(); - } - return cachedUserSpaces.get(username); + public boolean existsUserLogically(String username) { + return userList.contains(username); } - private void cacheUserSpace(String username, UserConfig wsc){ - cachedUserSpaces.put(username, wsc); + public String[] getUserList() { + String[] users = new String[userList.size()]; + return userList.toArray(users); } /* * Version control */ - - /** - * How many old versions of a file should be kept, in addition to the most recent one? - * Default is infinite. - */ - private NumberOfBackups numberOfBackups = NumberOfBackups.INFINITE; - - /** - * @see #numberOfBackups - */ + public NumberOfBackups getNumberOfBackups() { return numberOfBackups; } - - /** - * @see #numberOfBackups - */ + public void setNumberOfBackups(NumberOfBackups n) { + if (this.numberOfBackups == n) { return; } numberOfBackups = n; - makeDirty(); + publisher.publishEvent(WorkspaceProperty.N_OF_BACKUPS); } /* * Workspace language */ - - /** - * The language to be used within this workspace - */ - public Language language; - + public void setLanguage(Language language) { + if (this.language == language) { return; } this.language = language; - AppSettings.getInstance().setLanguage(language); - makeDirty(); + publisher.publishEvent(WorkspaceProperty.LANGUAGE); } - + public Language getLanguage() { if (language == null) return Language.LANGUAGE_ENGLISH; @@ -637,72 +342,63 @@ public class WorkspaceConfig extends StorableObject implements Serializable { /* * Allow users (children) to create new user accounts in workspaces? */ - - private boolean allowUserCreation = true; - - public void setAllowUserCreation(boolean allowed) { + public void setUserCreationAllowed(boolean allowed) { + if (this.allowUserCreation == allowed) { return; } this.allowUserCreation = allowed; - makeDirty(); + publisher.publishEvent(WorkspaceProperty.ALLOW_USER_CREATION); } - + public boolean isUserCreationAllowed() { - return allowUserCreation && !isVirtual(); + return allowUserCreation; } - /* - * Contest //TODO create options in workspace settings - */ - - private ContestConfig contestConfig; - - protected ContestConfig getContestSettings() { - return contestConfig; + public ContestConfig getContestSettings() { + if (contestSettings == null) + contestSettings = new ContestConfig(); + return contestSettings; } + public void setContestSettings(ContestConfig contestConfig) { + if (this.contestSettings == contestConfig) { return; } + this.contestSettings = contestConfig; + publisher.publishEvent(WorkspaceProperty.CONTEST); + } + public int getNOfContestFiles() { - if (contestConfig == null) - contestConfig = new ContestConfig(); return getContestSettings().getNOfContestFiles(); } - + public void setNOfContestFiles(int nOfContestFiles) { + if (this.getNOfContestFiles() == nOfContestFiles) { return; } getContestSettings().setNOfContestFiles(nOfContestFiles); + publisher.publishEvent(WorkspaceProperty.CONTEST); } - + public int getNOfContestBonusFiles() { return getContestSettings().getNOfContestBonusFiles(); } - + public void setNOfContestBonusFiles(int nOfContestBonusFiles) { + if (this.getNOfContestBonusFiles() == nOfContestBonusFiles) { return; } getContestSettings().setNOfContestBonusFiles(nOfContestBonusFiles); + publisher.publishEvent(WorkspaceProperty.CONTEST); } - public int getMaxEmptyFiles(){ + public int getMaxEmptyFiles() { return MAX_EMPTY_FILES; } - /* - * Syntax Highlighting - */ - private SyntaxHighlightConfig syntaxHighlightingStyles; // TODO = new SyntaxHighlightStyles(); - - /** - * This font is the default font for all menus ... in XLogo Application - */ - private Font font; // TODO =new Font("dialog",Font.PLAIN,14); - public SyntaxHighlightConfig getSyntaxHighlightStyles() { if (syntaxHighlightingStyles == null) { syntaxHighlightingStyles = new SyntaxHighlightConfig(); - makeDirty(); } return syntaxHighlightingStyles; } public void setSyntaxHighlightConfig(SyntaxHighlightConfig syntaxHighlightingStyles) { + if (this.syntaxHighlightingStyles == syntaxHighlightingStyles) { return; } this.syntaxHighlightingStyles = syntaxHighlightingStyles; - makeDirty(); - AppSettings.getInstance().setSyntaxHighlightingStyles(syntaxHighlightingStyles); + publisher.publishEvent(WorkspaceProperty.SYNTAX_HIGHLIGHTING); } public int getPrimitiveColor() { @@ -710,9 +406,9 @@ public class WorkspaceConfig extends StorableObject implements Serializable { } public void setPrimitiveColor(int primitiveColor) { + if (this.getPrimitiveColor() == primitiveColor) { return; } getSyntaxHighlightStyles().setPrimitiveColor(primitiveColor); - makeDirty(); - AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles()); + publisher.publishEvent(WorkspaceProperty.SYNTAX_HIGHLIGHTING); } public int getPrimitiveStyle() { @@ -720,29 +416,29 @@ public class WorkspaceConfig extends StorableObject implements Serializable { } public void setPrimitiveStyle(int primitiveStyle) { + if (this.getPrimitiveStyle() == primitiveStyle) { return; } getSyntaxHighlightStyles().setPrimitiveStyle(primitiveStyle); - makeDirty(); - AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles()); + publisher.publishEvent(WorkspaceProperty.SYNTAX_HIGHLIGHTING); } - public int getOperatorColor() { - return getSyntaxHighlightStyles().getOperatorColor(); + public int getOperandColor() { + return getSyntaxHighlightStyles().getOperandColor(); } - public void setOperandColor(int operatorColor) { - getSyntaxHighlightStyles().setOperatorColor(operatorColor); - makeDirty(); - AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles()); + public void setOperandColor(int operandColor) { + if (this.getOperandColor() == operandColor) { return; } + getSyntaxHighlightStyles().setOperandColor(operandColor); + publisher.publishEvent(WorkspaceProperty.SYNTAX_HIGHLIGHTING); } - public int getOperatorStyle() { - return getSyntaxHighlightStyles().getOperatorStyle(); + public int getOperandStyle() { + return getSyntaxHighlightStyles().getOperandStyle(); } - public void setOperandStyle(int operatorStyle) { - getSyntaxHighlightStyles().setOperatorStyle(operatorStyle); - makeDirty(); - AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles()); + public void setOperandStyle(int operandStyle) { + if (this.getOperandStyle() == operandStyle) { return; } + getSyntaxHighlightStyles().setOperandStyle(operandStyle); + publisher.publishEvent(WorkspaceProperty.SYNTAX_HIGHLIGHTING); } public int getCommentColor() { @@ -750,9 +446,9 @@ public class WorkspaceConfig extends StorableObject implements Serializable { } public void setCommentColor(int commentColor) { + if (this.getCommentColor() == commentColor) { return; } getSyntaxHighlightStyles().setCommentColor(commentColor); - makeDirty(); - AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles()); + publisher.publishEvent(WorkspaceProperty.SYNTAX_HIGHLIGHTING); } public int getCommentStyle() { @@ -760,9 +456,9 @@ public class WorkspaceConfig extends StorableObject implements Serializable { } public void setCommentStyle(int commentStyle) { + if (this.getCommentStyle() == commentStyle) { return; } getSyntaxHighlightStyles().setCommentStyle(commentStyle); - makeDirty(); - AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles()); + publisher.publishEvent(WorkspaceProperty.SYNTAX_HIGHLIGHTING); } public int getBraceColor() { @@ -770,9 +466,9 @@ public class WorkspaceConfig extends StorableObject implements Serializable { } public void setBraceColor(int braceColor) { + if (this.getBraceColor() == braceColor) { return; } getSyntaxHighlightStyles().setBraceColor(braceColor); - makeDirty(); - AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles()); + publisher.publishEvent(WorkspaceProperty.SYNTAX_HIGHLIGHTING); } public int getBraceStyle() { @@ -780,9 +476,9 @@ public class WorkspaceConfig extends StorableObject implements Serializable { } public void setBraceStyle(int braceStyle) { + if (this.getBraceStyle() == braceStyle) { return; } getSyntaxHighlightStyles().setBraceStyle(braceStyle); - makeDirty(); - AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles()); + publisher.publishEvent(WorkspaceProperty.SYNTAX_HIGHLIGHTING); } public boolean isSyntaxHighlightingEnabled() { @@ -790,19 +486,47 @@ public class WorkspaceConfig extends StorableObject implements Serializable { } public void setSyntaxHighlightingEnabled(boolean colorEnabled) { + if (this.isSyntaxHighlightingEnabled() == colorEnabled) { return; } getSyntaxHighlightStyles().setColorEnabled(colorEnabled); - makeDirty(); - AppSettings.getInstance().setSyntaxHighlightingStyles(getSyntaxHighlightStyles()); + publisher.publishEvent(WorkspaceProperty.SYNTAX_HIGHLIGHTING); } public Font getFont() { + if (font == null) { + font = DEFAULT_FONT; + } return font; } public void setFont(Font font) { + if (this.font == font) { return; } this.font = font; - makeDirty(); - AppSettings.getInstance().setFont(font); + publisher.publishEvent(WorkspaceProperty.FONT); + } + + + + /* + * Property Change Listeners + */ + + public enum WorkspaceProperty { + FONT, SYNTAX_HIGHLIGHTING, ALLOW_USER_CREATION, LANGUAGE, N_OF_BACKUPS, LAST_ACTIVE_USER, USER_LIST, CONTEST; } + + private transient PropertyChangePublisher publisher = new PropertyChangePublisher<>(); -} + @Override + public void addPropertyChangeListener(WorkspaceProperty property, PropertyChangeListener listener) { + if (publisher == null){ + publisher = new PropertyChangePublisher<>(); + } + publisher.addPropertyChangeListener(property, listener); + } + + @Override + public void removePropertyChangeListener(WorkspaceProperty property, PropertyChangeListener listener) { + publisher.removePropertyChangeListener(property, listener); + } + +} \ No newline at end of file diff --git a/logo/src/xlogo/storage/workspace/WorkspaceSettingJSONMapper.java b/logo/src/xlogo/storage/workspace/WorkspaceSettingJSONMapper.java new file mode 100644 index 0000000..022c288 --- /dev/null +++ b/logo/src/xlogo/storage/workspace/WorkspaceSettingJSONMapper.java @@ -0,0 +1,165 @@ +package xlogo.storage.workspace; + +import java.awt.Font; + +import org.json.JSONArray; +import org.json.JSONObject; + +import xlogo.storage.JSONSerializer; + +public class WorkspaceSettingJSONMapper extends JSONSerializer { + + private static final String FONT = "font"; + private static final String SIZE = "size"; + private static final String STYLE = "style"; + private static final String NAME = "name"; + private static final String SYNTAX_HIGHLIGHTING_STYLES = "syntaxHighlightingStyles"; + private static final String PRIMITIVE_STYLE = "primitiveStyle"; + private static final String PRIMITIVE_COLOR = "primitiveColor"; + private static final String OPERAND_STYLE = "operandStyle"; + private static final String OPERAND_COLOR = "operandColor"; + private static final String COMMENT_STYLE = "commentStyle"; + private static final String COMMENT_COLOR = "commentColor"; + private static final String BRACE_STYLE = "braceStyle"; + private static final String BRACE_COLOR = "braceColor"; + private static final String CONTEST_SETTINGS = "contestSettings"; + private static final String N_OF_CONTEST_BONUS_FILES = "nOfContestBonusFiles"; + private static final String N_OF_CONTEST_FILES = "nOfContestFiles"; + private static final String IS_USER_CREATION_ALLOWED = "isUserCreationAllowed"; + private static final String LANGUAGE = "language"; + private static final String NUMBER_OF_BACKUPS = "numberOfBackups"; + private static final String USER_LIST = "userList"; + private static final String LAST_ACTIVE_USER = "lastActiveUser"; + + @Override + public JSONObject serialize2JSON(WorkspaceConfig ws) { + + JSONObject json = new JSONObject(); + + json.put(USER_LIST, new JSONArray(ws.getUserList())); + json.put(LAST_ACTIVE_USER, ws.getLastActiveUser()); + json.put(NUMBER_OF_BACKUPS, ws.getNumberOfBackups().toString()); + json.put(LANGUAGE, ws.getLanguage().toString()); + json.put(IS_USER_CREATION_ALLOWED, ws.isUserCreationAllowed()); + + JSONObject jsonContestSettings = new JSONObject(); + jsonContestSettings.put(N_OF_CONTEST_FILES, ws.getNOfContestFiles()); + jsonContestSettings.put(N_OF_CONTEST_BONUS_FILES, ws.getNOfContestBonusFiles()); + json.put(CONTEST_SETTINGS, jsonContestSettings); + + JSONObject jsonSyntaxHighlightingStyles = new JSONObject().put(BRACE_COLOR, ws.getBraceColor()) + .put(BRACE_STYLE, ws.getBraceStyle()).put(COMMENT_COLOR, ws.getCommentColor()) + .put(COMMENT_STYLE, ws.getCommentStyle()).put(OPERAND_COLOR, ws.getOperandColor()) + .put(OPERAND_STYLE, ws.getOperandStyle()).put(PRIMITIVE_COLOR, ws.getPrimitiveColor()) + .put(PRIMITIVE_STYLE, ws.getPrimitiveStyle()); + json.put(SYNTAX_HIGHLIGHTING_STYLES, jsonSyntaxHighlightingStyles); + + JSONObject jsonFont = new JSONObject().put(NAME, ws.getFont().getName()).put(STYLE, ws.getFont().getStyle()) + .put(SIZE, ws.getFont().getSize()); + json.put(FONT, jsonFont); + + return json; + } + + @Override + public WorkspaceConfig deserialize(JSONObject json) { + WorkspaceConfig ws = new WorkspaceConfig(); + + if (json.has(USER_LIST)) { + JSONArray jsonUserList = json.getJSONArray(USER_LIST); + for (int i = 0; i < jsonUserList.length(); i++) { + ws.addUser(jsonUserList.getString(i)); + } + } + + if (json.has(LAST_ACTIVE_USER)) { + ws.setLastActiveUser(json.getString(LAST_ACTIVE_USER)); + } + + if (json.has(NUMBER_OF_BACKUPS)) { + ws.setNumberOfBackups(NumberOfBackups.valueOf(json.getString(NUMBER_OF_BACKUPS))); + } + + if (json.has(LANGUAGE)) { + ws.setLanguage(Language.valueOf(json.getString(LANGUAGE))); + } + + if (json.has(IS_USER_CREATION_ALLOWED)) { + ws.setUserCreationAllowed(json.getBoolean(IS_USER_CREATION_ALLOWED)); + } + + if (json.has(CONTEST_SETTINGS)) { + JSONObject jsonContestSettings = json.getJSONObject(CONTEST_SETTINGS); + + if (jsonContestSettings.has(N_OF_CONTEST_FILES)) { + ws.setNOfContestFiles(jsonContestSettings.getInt(N_OF_CONTEST_FILES)); + + } + if (jsonContestSettings.has(N_OF_CONTEST_BONUS_FILES)) { + ws.setNOfContestBonusFiles(jsonContestSettings.getInt(N_OF_CONTEST_BONUS_FILES)); + } + + } + + if (json.has(SYNTAX_HIGHLIGHTING_STYLES)) { + JSONObject jsonContestSettings = json.getJSONObject(SYNTAX_HIGHLIGHTING_STYLES); + + if (jsonContestSettings.has(BRACE_COLOR)) { + ws.setBraceColor(jsonContestSettings.getInt(BRACE_COLOR)); + } + + if (jsonContestSettings.has(BRACE_STYLE)) { + ws.setBraceStyle(jsonContestSettings.getInt(BRACE_STYLE)); + } + + if (jsonContestSettings.has(COMMENT_COLOR)) { + ws.setCommentColor(jsonContestSettings.getInt(COMMENT_COLOR)); + } + + if (jsonContestSettings.has(COMMENT_STYLE)) { + ws.setCommentStyle(jsonContestSettings.getInt(COMMENT_STYLE)); + } + + if (jsonContestSettings.has(OPERAND_COLOR)) { + ws.setOperandColor(jsonContestSettings.getInt(OPERAND_COLOR)); + } + + if (jsonContestSettings.has(OPERAND_STYLE)) { + ws.setOperandStyle(jsonContestSettings.getInt(OPERAND_STYLE)); + } + + if (jsonContestSettings.has(PRIMITIVE_COLOR)) { + ws.setPrimitiveColor(jsonContestSettings.getInt(PRIMITIVE_COLOR)); + } + + if (jsonContestSettings.has(PRIMITIVE_STYLE)) { + ws.setPrimitiveStyle(jsonContestSettings.getInt(PRIMITIVE_STYLE)); + } + + } + + if (json.has(FONT)) { + JSONObject jsonFont = json.getJSONObject(FONT); + + String name = ws.getFont().getName(); + if (jsonFont.has(NAME)) { + name = jsonFont.getString(NAME); + } + + int style = ws.getFont().getStyle(); + if (jsonFont.has(STYLE)) { + style = jsonFont.getInt(STYLE); + } + + int size = ws.getFont().getSize(); + if (jsonFont.has(SIZE)) { + size = jsonFont.getInt(SIZE); + } + + ws.setFont(new Font(name, style, size)); + } + + return ws; + } + +} diff --git a/logo/src/xlogo/utils/Utils.java b/logo/src/xlogo/utils/Utils.java index 5cf549f..612d4b8 100644 --- a/logo/src/xlogo/utils/Utils.java +++ b/logo/src/xlogo/utils/Utils.java @@ -1,38 +1,33 @@ -/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Loic Le Coq +/* + * XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Loic Le Coq * Copyright (C) 2013 Marko Zivkovic - * * Contact Information: marko88zivkovic at gmail dot com - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the Free - * Software Foundation; either version 2 of the License, or (at your option) - * any later version. This program 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 General - * Public License for more details. You should have received a copy of the - * GNU General Public License along with this program; if not, write to the Free - * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. This program 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 General + * Public License for more details. You should have received a copy of the + * GNU General Public License along with this program; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. - * - * * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic * during his Bachelor thesis at the computer science department of ETH Zurich, * in the year 2013 and/or during future work. - * * It is a reengineered version of XLogo written by Loic Le Coq, published * under the GPL License at http://xlogo.tuxfamily.org/ - * * Contents of this file were initially written by Loic Le Coq, - * modifications, extensions, refactorings might have been applied by Marko Zivkovic + * modifications, extensions, refactorings might have been applied by Marko Zivkovic */ -/** - * Title : XLogo - * Description : XLogo is an interpreter for the Logo - * programming language - * @author Loïc Le Coq - */ +/** Title : XLogo + * Description : XLogo is an interpreter for the Logo + * programming language + * + * @author Loïc Le Coq */ package xlogo.utils; + import java.awt.Container; import java.awt.Font; import java.awt.Image; @@ -46,14 +41,24 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.OutputStreamWriter; import java.net.URL; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; import java.util.Locale; import java.util.ResourceBundle; import java.util.StringTokenizer; import javax.swing.ImageIcon; +import org.json.JSONException; +import org.json.JSONObject; + import xlogo.kernel.MyCalculator; import xlogo.kernel.Affichage; import xlogo.storage.WSManager; @@ -61,7 +66,7 @@ import xlogo.storage.workspace.Language; import xlogo.Logo; public class Utils { - + /** * Marko : Here a technique was used for loading images, that happens to be from the last milenium. * https://weblogs.java.net/blog/chet/archive/2004/07/imageio_just_an.html @@ -72,8 +77,8 @@ public class Utils { * @param jf * @return */ - public static ImageIcon dimensionne_image(String name,Component jf){ - + public static ImageIcon dimensionne_image(String name, Component jf) { + Image img = Toolkit.getDefaultToolkit().getImage(Utils.class.getResource(name)); return new ImageIcon(img.getScaledInstance(20, 20, Image.SCALE_SMOOTH)); @@ -87,7 +92,6 @@ public class Utils { } catch (IOException e) { - // TODO Auto-generated catch block e.printStackTrace(); return null; } @@ -112,240 +116,274 @@ public class Utils { } return image; */ - } + } + public static void recursivelySetFonts(Component comp, Font font) { - comp.setFont(font); - if (comp instanceof Container) { - Container cont = (Container) comp; - for(int j=0, ub=cont.getComponentCount(); j-1) buffer.append(c); - backslash=false; + else if ("[]()#\\".indexOf(c) > -1) + buffer.append(c); + backslash = false; } else { - if (c=='\\') backslash=true; - else if (!ignore) buffer.append(c); - else if (c==' ') ignore=false; + if (c == '\\') + backslash = true; + else if (!ignore) + buffer.append(c); + else if (c == ' ') + ignore = false; } } return MyCalculator.getOutputNumber(new String(buffer)); } + /** * This method is formatting the String st.
* - Unused white spaces are deleted.
* - The character \ is modified to \\
* - The sequence "\ " is modified to "\e"
* - The sequence "\ " is modified to "\e"
- * - The sequence "\ " is modified to "\e"
- * - The sequence "\ " is modified to "\e"
+ * - The sequence "\ " is modified to "\e"
+ * - The sequence "\ " is modified to "\e"
* @param st The String instruction to format * @return The formatted instructions */ - public static StringBuffer decoupe(String st) { + public static StringBuffer decoupe(String st) { StringBuffer buffer = new StringBuffer(); // If last character is a white space - boolean espace=false; + boolean espace = false; // If last character is a backslash - boolean backslash=false; + boolean backslash = false; // If last character is a word - boolean mot=false; + boolean mot = false; - int crochet_liste=0; -// boolean variable=false; + int crochet_liste = 0; + // boolean variable=false; // If XLogo is running a program - boolean execution_lancee=Affichage.execution_lancee; - for(int i=0;i'||c=='&'||c=='|'){ + else if (c == '+' || c == '-' || c == '*' || c == '/' || c == '=' || c == '<' || c == '>' || c == '&' + || c == '|') { //System.out.println(mot+" "+espace); // à modifier (test + fin) - if (mot||crochet_liste>0) { + if (mot || crochet_liste > 0) { buffer.append(c); - if (espace) espace=false; + if (espace) + espace = false; } - else { - String op=String.valueOf(c); + else { + String op = String.valueOf(c); // Looking for operator <= or >= - if (c=='<'||c=='>'){ - if (i+1') { + if (i + 1 < st.length()) { + if (st.charAt(i + 1) == '=') { + op += "="; i++; } } } - if (espace) buffer.append(op+" "); + if (espace) + buffer.append(op + " "); else { - espace=true; - if (buffer.length()!=0) buffer.append(" "+op+" "); + espace = true; + if (buffer.length() != 0) + buffer.append(" " + op + " "); // If buffer is empty no white space before - else buffer.append(op+" "); + else + buffer.append(op + " "); } } } - else{ - if (backslash){ - if (c=='n') buffer.append("\\n"); - else if (c=='\\') buffer.append("\\\\"); - else if (c=='v'&& execution_lancee) buffer.append("\""); - else if(c=='e'&& execution_lancee) buffer.append("\\e"); - else if (c=='#') buffer.append("\\#"); - else if (c=='l'&&execution_lancee) buffer.append("\\l"); - else { + else { + if (backslash) { + if (c == 'n') + buffer.append("\\n"); + else if (c == '\\') + buffer.append("\\\\"); + else if (c == 'v' && execution_lancee) + buffer.append("\""); + else if (c == 'e' && execution_lancee) + buffer.append("\\e"); + else if (c == '#') + buffer.append("\\#"); + else if (c == 'l' && execution_lancee) + buffer.append("\\l"); + else { buffer.append(c); } } else { - buffer.append(c); + buffer.append(c); } - backslash=false; - espace=false; + backslash = false; + espace = false; } } -// System.out.println(buffer); + // System.out.println(buffer); // Remove the space when the user write only "*" or "+" in the command line //if (buffer.length()>0&&buffer.charAt(0)==' ') buffer.deleteCharAt(0); return (buffer); } - - - public static String specialCharacterXML(String st){ - st=st.replaceAll("&","&"); - st=st.replaceAll("<","<"); - st=st.replaceAll("\"","""); - st=st.replaceAll(">",">"); - st=st.replaceAll("'","'"); + public static String specialCharacterXML(String st) { + st = st.replaceAll("&", "&"); + st = st.replaceAll("<", "<"); + st = st.replaceAll("\"", """); + st = st.replaceAll(">", ">"); + st = st.replaceAll("'", "'"); return st; } - public static String readLogoFile(String path) throws IOException{ // ADAPT READ LOGO FILE - String txt=""; + + public static String readLogoFile(String path) throws IOException { // ADAPT READ LOGO FILE + String txt = ""; // The old format before XLogo 0.9.23 is no longer supported from version 0.9.30 - try{ - // New format for XLogo >=0.923 - // Encoded with UTF-8 - StringBuffer sb=new StringBuffer(); - FileInputStream fr = new FileInputStream(path); - InputStreamReader isr = new InputStreamReader(fr, "UTF8"); - BufferedReader brd=new BufferedReader(isr); - while (brd.ready()){ - sb.append(brd.readLine()); - sb.append("\n"); - } - txt=new String(sb); - brd.close(); - } - catch(FileNotFoundException e1){ - // tentative fichier réseau - try{ - URL url =new java.net.URL(path); - StringBuffer sb=new StringBuffer(); - java.io.InputStream fr = url.openStream(); - InputStreamReader isr = new InputStreamReader(fr, "UTF8"); - BufferedReader brd=new BufferedReader(isr); - while (brd.ready()){ - String st=brd.readLine(); - sb.append(st); - sb.append("\n"); - } - txt=new String(sb); - } - catch( java.net.MalformedURLException e){ - System.out.println("File not found: "+path.toString()); - } - } - catch(Exception e){e.printStackTrace();} - if (txt.startsWith("# "+Logo.messages.getString("mainCommand"))){ - int id=txt.indexOf("\n"); - if (id!=-1){ - WSManager.getUserConfig().setMainCommand(txt.substring(("# "+Logo.messages.getString("mainCommand")).length(),id).trim()); - txt=txt.substring(id+1); - } - }; - return txt; + try { + // New format for XLogo >=0.923 + // Encoded with UTF-8 + StringBuffer sb = new StringBuffer(); + FileInputStream fr = new FileInputStream(path); + InputStreamReader isr = new InputStreamReader(fr, "UTF8"); + BufferedReader brd = new BufferedReader(isr); + while (brd.ready()) { + sb.append(brd.readLine()); + sb.append("\n"); + } + txt = new String(sb); + brd.close(); + } + catch (FileNotFoundException e1) { + // tentative fichier réseau + try { + URL url = new java.net.URL(path); + StringBuffer sb = new StringBuffer(); + java.io.InputStream fr = url.openStream(); + InputStreamReader isr = new InputStreamReader(fr, "UTF8"); + BufferedReader brd = new BufferedReader(isr); + while (brd.ready()) { + String st = brd.readLine(); + sb.append(st); + sb.append("\n"); + } + txt = new String(sb); + } + catch (java.net.MalformedURLException e) { + System.out.println("File not found: " + path.toString()); + } + } + catch (Exception e) { + e.printStackTrace(); + } + if (txt.startsWith("# " + Logo.messages.getString("mainCommand"))) { + int id = txt.indexOf("\n"); + if (id != -1) { + WSManager.getUserConfig().setMainCommand( + txt.substring(("# " + Logo.messages.getString("mainCommand")).length(), id).trim()); + txt = txt.substring(id + 1); + } + } + ; + return txt; } /** @@ -355,25 +393,29 @@ public class Utils { * @param txt * @throws IOException */ - public static void writeLogoFile(String path,String txt) throws IOException{ // ADAPT write logo file - try{ + public static void writeLogoFile(String path, String txt) throws IOException { // ADAPT write logo file + try { if (!WSManager.getUserConfig().getMainCommand().trim().equals("")) { - String heading="# "+Logo.messages.getString("mainCommand")+" "+WSManager.getUserConfig().getMainCommand()+"\n"; - txt=heading+txt; + String heading = "# " + Logo.messages.getString("mainCommand") + " " + + WSManager.getUserConfig().getMainCommand() + "\n"; + txt = heading + txt; } FileOutputStream f = new FileOutputStream(path); - BufferedOutputStream b = new BufferedOutputStream(f); - OutputStreamWriter osw = new OutputStreamWriter(b, "UTF8"); - osw.write(txt); - osw.close(); - b.close(); - f.close(); - + BufferedOutputStream b = new BufferedOutputStream(f); + OutputStreamWriter osw = new OutputStreamWriter(b, "UTF8"); + osw.write(txt); + osw.close(); + b.close(); + f.close(); + + } + catch (FileNotFoundException e1) { + e1.printStackTrace(); } - catch(FileNotFoundException e1){e1.printStackTrace();} } - public static boolean fileExists(String name){ - File f=new File(name); + + public static boolean fileExists(String name) { + File f = new File(name); return f.exists(); } @@ -382,16 +424,17 @@ public class Utils { * @return * @author Marko Zivkovic */ - public static boolean isFile(String path){ + public static boolean isFile(String path) { File f = new File(path); return f.isFile(); } + /** * @param path * @return * @author Marko Zivkovic */ - public static boolean isDirectory(String path){ + public static boolean isDirectory(String path) { File f = new File(path); return f.isDirectory(); } @@ -404,10 +447,10 @@ public class Utils { * @throws IOException If there is a problem with either src or dest * @author Marko Zivkovic */ - public static void copyFile(String src, String dest) throws IOException - { + public static void copyFile(String src, String dest) throws IOException { copyFile(new File(src), new File(dest)); } + /** * Implementation inspired by "JAVA ist auch eine Insel" - Christian Ullenboom, Galileo Computing *

If destination exists, it will be replaced @@ -416,31 +459,118 @@ public class Utils { * @throws IOException If there is a problem with either src or dest * @author Marko Zivkovic */ - public static void copyFile(File src, File dest) throws IOException - { + public static void copyFile(File src, File dest) throws IOException { FileInputStream fis = null; FileOutputStream fos = null; - try - { + try { fis = new FileInputStream(src); fos = new FileOutputStream(dest); byte[] buffer = new byte[0xFFFF]; - for (int len; (len = fis.read(buffer)) != -1;) - { + for (int len; (len = fis.read(buffer)) != -1;) { fos.write(buffer, 0, len); } - }catch (IOException e) { + } + catch (IOException e) { throw e; } finally { if (fis != null) - try { fis.close(); } catch (IOException e) {} + try { + fis.close(); + } + catch (IOException e) {} if (fos != null) - try { fos.close(); } catch (IOException e) {} + try { + fos.close(); + } + catch (IOException e) {} } } + /** + * Read a file in UTF8 encoding and return its contents as a String + * @param src + * @return + * @author Marko Zivkovic + * @throws IOException + */ + public static String readFile(File src) throws IOException { + return readFile(src, Charset.forName("UTF8")); + } + + public static String readFile(File src, Charset charset) throws IOException { + byte[] encoded = Files.readAllBytes(src.toPath()); + return new String(encoded, charset); + } + + /** + * Store a simple string to a file in UTF8 encoding + * @param file + * @param content + * @throws IOException + * @author Marko Zivkovic + */ + public static void storeFile(File file, String content) throws IOException { + store(file, content, Charset.forName("UTF8")); + } + + /** + * Store a simple string to a file in the given encoding + * @param file + * @param content + * @param charset + * @throws IOException + * @author Marko Zivkovic + */ + public static void store(File file, String content, Charset charset) throws IOException { + mkParentDirs(file); + FileOutputStream f = new FileOutputStream(file); + BufferedOutputStream b = new BufferedOutputStream(f); + OutputStreamWriter osw = new OutputStreamWriter(b, charset); + osw.write(content); + osw.close(); + b.close(); + f.close(); + } + + public static JSONObject readJSON(File file) throws JSONException, IOException { + return new JSONObject(readFile(file)); + } + + public static void store(File file, JSONObject json) throws IOException { + storeFile(file, json.toString()); + } + + public static T readObject(File file, Class c) throws IOException, ClassNotFoundException { + FileInputStream fileIn = new FileInputStream(file); + ObjectInputStream in = new ObjectInputStream(fileIn); + Object object = in.readObject(); + in.close(); + fileIn.close(); + + try{ + T typed = c.cast(object); + return typed; + } catch(ClassCastException e) { + throw new ClassCastException("The specified file (" + file.toString() + + ") does not contain an instance of " + c.getName() + ": " + object.getClass().toString()); + } + } + + public static void store(File file, Object obj) throws IOException { + mkParentDirs(file); + FileOutputStream fileOut = new FileOutputStream(file); + ObjectOutputStream out = new ObjectOutputStream(fileOut); + out.writeObject(obj); + out.close(); + fileOut.close(); + } + + private static void mkParentDirs(File file) { + file.getParentFile().mkdirs(); + } + /** * First copy file to dest and then delete file. * @param file @@ -448,8 +578,7 @@ public class Utils { * @throws IOException If there is a problem with either file or dest * @author Marko Zivkovic */ - public static void moveFile(String file, String dest) throws IOException - { + public static void moveFile(String file, String dest) throws IOException { moveFile(new File(file), new File(dest)); } @@ -460,23 +589,65 @@ public class Utils { * @throws IOException If there is a problem with either file or dest * @author Marko Zivkovic */ - public static void moveFile(File file, File dest) throws IOException - { + public static void moveFile(File file, File dest) throws IOException { copyFile(file, dest); file.delete(); } + public static String primitiveName(String generic) { + Language lang = WSManager.getInstance().getWorkspaceConfigInstance().getLanguage(); + Locale locale = lang.getLocale(); + ResourceBundle prim = ResourceBundle.getBundle("primitives", locale); + String st = prim.getString(generic); + StringTokenizer str = new StringTokenizer(st); + while (str.hasMoreTokens()) { + st = str.nextToken(); + } + return st; + } + + /* + * Time Formats + */ - public static String primitiveName(String generic){ - Language lang = WSManager.getInstance().getWorkspaceConfigInstance().getLanguage(); - Locale locale = lang.getLocale(); - ResourceBundle prim = ResourceBundle.getBundle( - "primitives", locale); - String st = prim.getString(generic); - StringTokenizer str = new StringTokenizer(st); - while (str.hasMoreTokens()) { - st = str.nextToken(); - } - return st; - } + public static final String dateTimeFormat = "yyyy-MM-dd-HH-mm-ss"; + public static final String timeFormat = "HH:mm:ss"; + public static final String minSecFormat = "mm:ss"; + + public static String getTimeStamp() + { + SimpleDateFormat sdf = new SimpleDateFormat(); + sdf.applyPattern(dateTimeFormat); + Calendar cal = Calendar.getInstance(); + return sdf.format(cal.getTime()); + } + + /** + * @param millis + * @return yyyy-MM-dd-HH-mm-ss + */ + public static String getDateTimeString(long millis) + { + SimpleDateFormat sdf = new SimpleDateFormat(); + sdf.applyPattern(dateTimeFormat); + return sdf.format(new Date(millis)); + } + + /** + * @param millis + * @return HH:mm:ss + */ + public static String getTimeString(long millis) + { + SimpleDateFormat sdf = new SimpleDateFormat(); + sdf.applyPattern(timeFormat); + return sdf.format(new Date(millis)); + } + + public static String getMinSec(long millis) + { + SimpleDateFormat sdf = new SimpleDateFormat(); + sdf.applyPattern(minSecFormat); + return sdf.format(new Date(millis)); + } } -- cgit v1.2.3