summaryrefslogtreecommitdiffstats
path: root/logo/src
diff options
context:
space:
mode:
Diffstat (limited to 'logo/src')
-rw-r--r--logo/src/xlogo/AppSettings.java177
-rw-r--r--logo/src/xlogo/Application.java197
-rw-r--r--logo/src/xlogo/Logo.java8
-rw-r--r--logo/src/xlogo/StyledDocument/DocumentLogo.java29
-rw-r--r--logo/src/xlogo/gui/Editor.java2
-rw-r--r--logo/src/xlogo/gui/HistoryPanel.java2
-rw-r--r--logo/src/xlogo/gui/ZoneCommande.java2
-rw-r--r--logo/src/xlogo/gui/components/ColorStyleSelectionPanel.java10
-rw-r--r--logo/src/xlogo/gui/components/X4SGui.java21
-rw-r--r--logo/src/xlogo/gui/preferences/AbstractPanelColor.java225
-rw-r--r--logo/src/xlogo/gui/welcome/WelcomeScreen.java91
-rw-r--r--logo/src/xlogo/gui/welcome/WorkspaceSettings.java3
-rw-r--r--logo/src/xlogo/gui/welcome/settings/tabs/AbstractWorkspacePanel.java79
-rw-r--r--logo/src/xlogo/gui/welcome/settings/tabs/ContestTab.java44
-rw-r--r--logo/src/xlogo/gui/welcome/settings/tabs/GlobalTab.java6
-rw-r--r--logo/src/xlogo/gui/welcome/settings/tabs/SyntaxHighlightingTab.java72
-rw-r--r--logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceCreationPanel.java4
-rw-r--r--logo/src/xlogo/gui/welcome/settings/tabs/WorkspaceTab.java397
-rw-r--r--logo/src/xlogo/interfaces/Observable.java31
-rw-r--r--logo/src/xlogo/interfaces/PropertyChangePublisher.java67
-rw-r--r--logo/src/xlogo/kernel/userspace/context/LogoContext.java2
-rw-r--r--logo/src/xlogo/kernel/userspace/context/UserContext.java17
-rw-r--r--logo/src/xlogo/kernel/userspace/files/LogoFile.java30
-rw-r--r--logo/src/xlogo/kernel/userspace/files/RecordFile.java10
-rw-r--r--logo/src/xlogo/kernel/userspace/procedures/Procedure.java12
-rw-r--r--logo/src/xlogo/storage/JSONSerializer.java21
-rw-r--r--logo/src/xlogo/storage/Storable.java91
-rw-r--r--logo/src/xlogo/storage/StorableDocument.java91
-rw-r--r--logo/src/xlogo/storage/StorableObject.java334
-rw-r--r--logo/src/xlogo/storage/WSManager.java592
-rw-r--r--logo/src/xlogo/storage/global/GlobalConfig.java474
-rw-r--r--logo/src/xlogo/storage/user/UserConfig.java729
-rw-r--r--logo/src/xlogo/storage/workspace/Serializer.java6
-rw-r--r--logo/src/xlogo/storage/workspace/SyntaxHighlightConfig.java8
-rw-r--r--logo/src/xlogo/storage/workspace/WorkspaceConfig.java740
-rw-r--r--logo/src/xlogo/storage/workspace/WorkspaceSettingJSONMapper.java165
-rw-r--r--logo/src/xlogo/utils/Utils.java617
37 files changed, 2862 insertions, 2544 deletions
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<AppSettings.AppProperty>{
+ 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<ActionListener> languageChangeListeners = new ArrayList<ActionListener>();
-
- 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<ActionListener> fontChangeListeners = new ArrayList<ActionListener>();
-
- 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<ActionListener> syntaxHighlightStyleChangeListeners = new ArrayList<ActionListener>();
- 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<AppProperty> 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<String> pile_historique;
- private int index_historique;
+ private static Stack<String> 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<args.length;i++){
gc.getPath().add(args[i]);
@@ -110,7 +110,11 @@ public class Logo {
/**Builds Application with the valid Config*/
public Logo() {
- Language lang = WSManager.getInstance().getWorkspaceConfigInstance().getLanguage();
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+ Language lang = Language.LANGUAGE_ENGLISH;
+ if (wc != null){
+ lang = WSManager.getInstance().getWorkspaceConfigInstance().getLanguage();
+ }
generateLanguage(lang);
showWelcomeScreen();
diff --git a/logo/src/xlogo/StyledDocument/DocumentLogo.java b/logo/src/xlogo/StyledDocument/DocumentLogo.java
index 2676817..83e2c81 100644
--- a/logo/src/xlogo/StyledDocument/DocumentLogo.java
+++ b/logo/src/xlogo/StyledDocument/DocumentLogo.java
@@ -36,6 +36,7 @@ import javax.swing.text.*;
import xlogo.kernel.Primitive;
import xlogo.storage.WSManager;
+import xlogo.storage.workspace.SyntaxHighlightConfig;
import xlogo.storage.workspace.WorkspaceConfig;
/**
@@ -60,7 +61,7 @@ public class DocumentLogo extends DefaultStyledDocument {
private MutableAttributeSet keyword;
private MutableAttributeSet comment;
private MutableAttributeSet quote;
- private boolean coloration_activee = WSManager.getWorkspaceConfig().isSyntaxHighlightingEnabled();
+ private boolean coloration_activee = true;
private boolean colore_parenthese = false;
public void setColoration(boolean b) {
@@ -68,21 +69,35 @@ public class DocumentLogo extends DefaultStyledDocument {
}
public DocumentLogo() {
- WorkspaceConfig uc = WSManager.getWorkspaceConfig();
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
doc = this;
rootElement = doc.getDefaultRootElement();
putProperty(DefaultEditorKit.EndOfLineStringProperty, "\n");
- initStyles(uc.getCommentColor(), uc.getCommentStyle(),
- uc.getPrimitiveColor(), uc.getPrimitiveStyle(),
- uc.getBraceColor(), uc.getBraceStyle(),
- uc.getOperatorColor(), uc.getOperatorStyle());
+
+ SyntaxHighlightConfig syntaxHighlight = new SyntaxHighlightConfig();
+ if (wc != null){
+ coloration_activee = wc.isSyntaxHighlightingEnabled();
+ syntaxHighlight = wc.getSyntaxHighlightStyles();
+ }
+
+ initStyles(syntaxHighlight.getCommentColor(), syntaxHighlight.getCommentStyle(),
+ syntaxHighlight.getPrimitiveColor(), syntaxHighlight.getPrimitiveStyle(),
+ syntaxHighlight.getBraceColor(), syntaxHighlight.getBraceStyle(),
+ syntaxHighlight.getOperandColor(), syntaxHighlight.getOperandStyle());
}
public void initStyles(int c_comment, int sty_comment, int c_primitive,
int sty_primitive, int c_parenthese, int sty_parenthese,
int c_operande, int sty_operande) {
- Font font = WSManager.getWorkspaceConfig().getFont();
+ WorkspaceConfig wc = WSManager.getWorkspaceConfig();
+ Font font;
+ if (wc != null){
+ font = WSManager.getWorkspaceConfig().getFont();
+ } else {
+ font = WorkspaceConfig.DEFAULT_FONT;
+ }
+
normal = new SimpleAttributeSet();
StyleConstants.setFontFamily(normal, font.getFamily());
diff --git a/logo/src/xlogo/gui/Editor.java b/logo/src/xlogo/gui/Editor.java
index e6f0e3b..6a5158a 100644
--- a/logo/src/xlogo/gui/Editor.java
+++ b/logo/src/xlogo/gui/Editor.java
@@ -261,7 +261,7 @@ public class Editor implements ActionListener
{
((EditorTextPane) textZone).getDsd().initStyles(wc.getCommentColor(), wc.getCommentStyle(),
wc.getPrimitiveColor(), wc.getPrimitiveStyle(), wc.getBraceColor(),
- wc.getBraceStyle(), wc.getOperatorColor(), wc.getOperatorStyle());
+ wc.getBraceStyle(), wc.getOperandColor(), wc.getOperandStyle());
}
}
diff --git a/logo/src/xlogo/gui/HistoryPanel.java b/logo/src/xlogo/gui/HistoryPanel.java
index 530fc68..e1d462b 100644
--- a/logo/src/xlogo/gui/HistoryPanel.java
+++ b/logo/src/xlogo/gui/HistoryPanel.java
@@ -242,7 +242,7 @@ public class HistoryPanel extends JPanel implements HistoryWriter
{
WorkspaceConfig wc = WSManager.getWorkspaceConfig();
dsd.initStyles(wc.getCommentColor(), wc.getCommentStyle(), wc.getPrimitiveColor(), wc.getPrimitiveStyle(),
- wc.getBraceColor(), wc.getBraceStyle(), wc.getOperatorColor(), wc.getOperatorStyle());
+ wc.getBraceColor(), wc.getBraceStyle(), wc.getOperandColor(), wc.getOperandStyle());
}
// Enable or disable Syntax Highlighting
diff --git a/logo/src/xlogo/gui/ZoneCommande.java b/logo/src/xlogo/gui/ZoneCommande.java
index cd99211..84c7026 100644
--- a/logo/src/xlogo/gui/ZoneCommande.java
+++ b/logo/src/xlogo/gui/ZoneCommande.java
@@ -254,7 +254,7 @@ public class ZoneCommande extends JTextPane implements CaretListener
{
WorkspaceConfig wc = WSManager.getWorkspaceConfig();
dlc.initStyles(wc.getCommentColor(), wc.getCommentStyle(), wc.getPrimitiveColor(), wc.getPrimitiveStyle(),
- wc.getBraceColor(), wc.getBraceStyle(), wc.getOperatorColor(), wc.getOperatorStyle());
+ wc.getBraceColor(), wc.getBraceStyle(), wc.getOperandColor(), wc.getOperandStyle());
}
// Enable or disable Syntax Highlighting
diff --git a/logo/src/xlogo/gui/components/ColorStyleSelectionPanel.java b/logo/src/xlogo/gui/components/ColorStyleSelectionPanel.java
index bd5e039..7d9b024 100644
--- a/logo/src/xlogo/gui/components/ColorStyleSelectionPanel.java
+++ b/logo/src/xlogo/gui/components/ColorStyleSelectionPanel.java
@@ -55,13 +55,13 @@ public class ColorStyleSelectionPanel {
private Integer[] intArray = new Integer[17];
private JButton bchoisir = new JButton(
Logo.messages.getString("pref.highlight.other"));
- private JComboBox combo_couleur;
+ private JComboBox<Integer> 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<String> 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<Object> {
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<Integer> 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<String> workspaceSelection;
+ private JComboBox<String> 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<String> 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<String>(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<String> workspaceSelection;
JLabel nOfFilesLabel;
JSpinner nOfFileSpinner;
JLabel nOfBonusFilesLabel;
@@ -58,29 +58,42 @@ public class ContestTab extends AbstractWorkspacePanel {
}
@Override
- protected JComboBox getWorkspaceSelection() {
+ protected JComboBox<String> 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<String> 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<String> 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<String> workspaceSelection;
+ JComboBox<String> userSelection;
+ JLabel wsLocation;
+ JFileChooser wsLocationChooser;
+ JComboBox<Language> languageSelection;
+ JComboBox<NumberOfBackups> nOfBackupsSelecteion;
+ JCheckBox userCreatable;
public WorkspaceTab() {
super();
}
@Override
- public JComponent getComponent()
- {
+ public JComponent getComponent() {
return component;
}
-
+
@Override
- protected JComboBox getWorkspaceSelection() {
+ protected JComboBox<String> 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<String>(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 <br><br>
+ <code>
+private transient HashMap<String, List<PropertyChangeListener>> listeners = new HashMap<>(); <br><br>
+
+public void addPropertyChangeListener(String property, PropertyChangeListener listener){ <br>
+ List<PropertyChangeListener> list = null; <br>
+ if (listeners.containsKey(property)){ <br>
+ list = listeners.get(property); <br>
+ } else { <br>
+ list = new ArrayList<AbstractObservable.PropertyChangeListener>(); <br>
+ listeners.put(property, list); <br>
+ } <br>
+ list.add(listener); <br>
+} <br>
+</code>
+ */
+public interface Observable<E extends Enum<E>> {
+
+ /**
+ * @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<E extends Enum<E>> implements Observable<E>, Serializable {
+
+ private static final long serialVersionUID = -1042512850782413412L;
+
+ private boolean enableEvents = true;
+
+ private final HashMap<E, List<PropertyChangeListener>> listeners = new HashMap<>();
+ private final List<PropertyChangeListener> 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<PropertyChangeListener> 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<PropertyChangeListener> 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> procedureMapListener = new ArrayList<ProcedureMapListener>();
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.
* <p>
- * 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.
* <p>
* 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<String, Procedure>();
allProcedures = new ArrayList<Procedure>();
}
@@ -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<T> implements Serializer<T>{
+ 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. <p>
* 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.<br>
* Setting location has no effect if this is virtual.<br>
- * @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);
}
/**
@@ -236,6 +275,18 @@ public abstract class Storable implements Serializable {
}
/*
+ * 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)} <br>
@@ -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<T extends Observable<E>, E extends Enum<E>> 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<T> targetClass = null;
+ private T object = null;
+ private Serializer<T> serializer = null;
+ private Initializer<T> creationInitilizer = null;
+ private Initializer<T> 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<T> 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<T> 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<T> 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<T> 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<T> c, File dir, String plainName, boolean isDeferred) {
+ this.targetClass = c;
+ setLocation(dir);
+ setPlainName(plainName);
+ setStoringDeferred(isDeferred);
}
/*
- * Store & Load
+ * Create, Store & Load
*/
+
+ public StorableObject<T, E> 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<T, E> 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<T, E> 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> T loadObject(File file, Class<T> 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> T loadObject(File file, Serializer<T> 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. <p>
+ * 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<T> getTargetClass(){
+ return targetClass;
+ }
+
+ public Serializer<T> getSerializer() {
+ return this.serializer;
+ }
+
+ public void setSerializer(Serializer<T> 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<T> getCreationInitilizer() {
+ return creationInitilizer;
+ }
+
+ public void setCreationInitilizer(Initializer<T> firstTimeInitilizer) {
+ this.creationInitilizer = firstTimeInitilizer;
+ }
+
+ public Initializer<T> getLoadInitilizer() {
+ return loadInitilizer;
+ }
+
+ public void setLoadInitilizer(Initializer<T> 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<T, E> withSerializer(Serializer <T> 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<T, E> withCreationInitializer(Initializer<T> 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<T, E> withLoadInitializer(Initializer<T> loadTimeInitializer){
+ setLoadInitilizer(loadTimeInitializer);
+ return this;
+ }
+
+
+ /*
+ * Interfaces
+ */
+
+ public interface Initializer<T> {
+ 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()}
* <p> 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, GlobalProperty> 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.
+ * <p>
+ * 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<WorkspaceConfig, WorkspaceProperty> 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<WorkspaceConfig, WorkspaceProperty> 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<USBStorageDevice> 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<WorkspaceConfig, WorkspaceConfig.WorkspaceProperty> 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<WorkspaceConfig> serializer = new WorkspaceSettingJSONMapper();
+ StorableObject<WorkspaceConfig, WorkspaceConfig.WorkspaceProperty> 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<UserConfig, UserProperty> 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, UserProperty> 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<WorkspaceConfig, WorkspaceProperty> createWorkspace(GlobalConfig gc, File wsDir){
+ StorableObject<WorkspaceConfig, WorkspaceConfig.WorkspaceProperty> 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<WorkspaceConfig, WorkspaceProperty> 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<UserConfig, UserConfig.UserProperty> 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.
+ * <p> 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<WorkspaceConfig, WorkspaceConfig.WorkspaceProperty> swc = globalConfig.get().getCurrentWorkspace();
+ if (swc == null) { return; }
+ swc.store();
+ StorableObject<UserConfig, UserProperty> 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<GlobalConfig.GlobalProperty> {
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<String, String>();
- 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<String, String> existingWorkspaces = new TreeMap<String, String>();
Map<String, String> lostWorkspaces = new TreeMap<String, String>();
for (Entry<String, String> 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<String, String> 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<WorkspaceConfig, WorkspaceProperty> retrieveWorkspace(String workspaceName) throws IOException {
+ StorableObject<WorkspaceConfig, WorkspaceProperty> 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<String, WorkspaceConfig> cachedWorkspaces;
+ private transient Map<String, StorableObject<WorkspaceConfig, WorkspaceProperty>> cachedWorkspaces;
- private WorkspaceConfig getCachedWorkspace(String workspaceName) {
+ private StorableObject<WorkspaceConfig, WorkspaceProperty> getCachedWorkspace(String workspaceName) {
if (cachedWorkspaces == null) {
- cachedWorkspaces = new TreeMap<String, WorkspaceConfig>();
+ cachedWorkspaces = new TreeMap<>();
}
return cachedWorkspaces.get(workspaceName);
}
- private void cacheWorkspace(String workspaceName, WorkspaceConfig wsc) {
+ private void cacheWorkspace(String workspaceName, StorableObject<WorkspaceConfig, WorkspaceProperty> wsc) {
+ if (cachedWorkspaces == null) {
+ cachedWorkspaces = new TreeMap<>();
+ }
cachedWorkspaces.put(workspaceName, wsc);
}
-
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Workspaces
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
@@ -291,27 +176,32 @@ 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<WorkspaceConfig, WorkspaceProperty> 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);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
@@ -319,13 +209,6 @@ public class GlobalConfig extends StorableObject implements Serializable {
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/**
- * @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<WorkspaceConfig, WorkspaceProperty> currentWorkspace;
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Entering and Leaving Workspaces
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- public WorkspaceConfig getCurrentWorkspace() {
+ public StorableObject<WorkspaceConfig, WorkspaceProperty> getCurrentWorkspace() {
return currentWorkspace;
}
- /**
- * This is used to have a workspace ready at the beginning, without any user interaction.
- * <p>
- * 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<USBStorageDevice> 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<String> 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<ActionListener> workspaceListChangeListeners;
-
- public void addWorkspaceListChangeListener(ActionListener listener) {
- if (workspaceListChangeListeners == null)
- workspaceListChangeListeners = new ArrayList<ActionListener>();
- workspaceListChangeListeners.add(listener);
- }
-
- public void removeWorkspaceListChangeListener(ActionListener listener) {
- workspaceListChangeListeners.remove(listener);
- }
-
- private void notifyWorkspaceListChanged() {
- if (workspaceListChangeListeners == null)
- workspaceListChangeListeners = new ArrayList<ActionListener>();
- ActionEvent event = new ActionEvent(this, 0, "workspaceListChanged");
- for (ActionListener listener : workspaceListChangeListeners)
- listener.actionPerformed(event);
- }
-
- // enter workspace event
-
- private transient ArrayList<ActionListener> enterWorkspaceListeners;
-
- public void addEnterWorkspaceListener(ActionListener listener) {
- if (enterWorkspaceListeners == null)
- enterWorkspaceListeners = new ArrayList<ActionListener>();
- enterWorkspaceListeners.add(listener);
+
+ public enum GlobalProperty {
+ PATH, PASSWORD, LAST_USED_WORKSPACE, CURRENT_WORKSPACE, WORKSPACES;
}
+
+ private transient PropertyChangePublisher<GlobalProperty> 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<UserConfig.UserProperty> {
- 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.
- * <p>
- * 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<String> fileOrder = new ArrayList<String>();
- 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<String> fileOrder = new ArrayList<String>();
-
- 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<String> getFileOrder()
- {
+ public ArrayList<String> 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 <br>
* Slow: 100
* Fast: 0
*/
- private int turtleSpeed=0;
+ private int turtleSpeed = 0;
/**
* This String contains the command to execute on Startup. <br>
* 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 <br>
* Loic:
* The default folder for the user when the application starts.<br>
* 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<String> borderExternalImage=new ArrayList<String>();
+ private ArrayList<String> borderExternalImage = new ArrayList<String>();
/**
* 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<String> getBorderExternalImage() {
return borderExternalImage;
}
-
-
+
public void setBorderExternalImage(ArrayList<String> 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<UserProperty> 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<T> {
+ 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<WorkspaceConfig.WorkspaceProperty> {
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<String>();
- 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<String> userList = new ArrayList<String>();
+ 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<String>();
- 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<String> getPhysicalUserList() {
+
+ ArrayList<String> users = new ArrayList<String>();
+
+ 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<UserConfig, UserProperty> activeUser;
- @Override
- protected void makeDirty(){
- super.makeDirty();
- setStoringDeferred(false);
+ public StorableObject<UserConfig, UserProperty> 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, UserProperty> 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<String> 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<UserConfig, UserProperty> retrieveUserSpace(String username){
+ StorableObject<UserConfig, UserProperty> 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.
- * <p> 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<String, StorableObject<UserConfig, UserProperty>> cachedUserSpaces;
+
+ private StorableObject<UserConfig, UserProperty> getCachedUserSpace(String username) {
+ if (cachedUserSpaces == null){
+ cachedUserSpaces= new TreeMap<String, StorableObject<UserConfig, UserProperty>>();
}
- if (!existsUserPhysically(username)){
- UserConfig.createNewUser(this, username);
+ return cachedUserSpaces.get(username);
+ }
+
+ private void cacheUserSpace(String username, StorableObject<UserConfig, UserProperty> wsc){
+ if (cachedUserSpaces == null){
+ cachedUserSpaces= new TreeMap<String, StorableObject<UserConfig, UserProperty>>();
}
- 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.
- * <p> 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<UserConfig, UserConfig.UserProperty> 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<String> getPhysicalUserList() {
- if (isVirtual())
- return new ArrayList<String>();
-
- ArrayList<String> users = new ArrayList<String>();
-
- 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<String, UserConfig> cachedUserSpaces;
-
- private UserConfig getCachedUserSpace(String username) {
- if (cachedUserSpaces == null){
- cachedUserSpaces= new TreeMap<String, UserConfig>();
- }
- 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<WorkspaceProperty> 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<WorkspaceConfig> {
+
+ 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<ub; ++j)
- recursivelySetFonts(cont.getComponent(j), font);
- }
+ comp.setFont(font);
+ if (comp instanceof Container) {
+ Container cont = (Container) comp;
+ for (int j = 0, ub = cont.getComponentCount(); j < ub; ++j)
+ recursivelySetFonts(cont.getComponent(j), font);
+ }
}
- public static String rajoute_backslash(String st){
- StringBuffer tampon=new StringBuffer();
- for(int j=0;j<st.length();j++){
- char c=st.charAt(j);
- if (c=='\\') tampon.append("\\\\");
- else if (c==' ') tampon.append("\\e");
- else if ("()[]#".indexOf(c)!=-1) tampon.append("\\"+c);
- else tampon.append(c);
+
+ public static String rajoute_backslash(String st) {
+ StringBuffer tampon = new StringBuffer();
+ for (int j = 0; j < st.length(); j++) {
+ char c = st.charAt(j);
+ if (c == '\\')
+ tampon.append("\\\\");
+ else if (c == ' ')
+ tampon.append("\\e");
+ else if ("()[]#".indexOf(c) != -1)
+ tampon.append("\\" + c);
+ else
+ tampon.append(c);
}
- return(new String(tampon));
- }
+ return (new String(tampon));
+ }
/**
* Escape string, backslash ...
* @param chaine
* @return
*/
- public static String SortieTexte(String chaine){ // Enlève les backslash
- StringBuffer buffer=new StringBuffer();
- boolean backslash=false;
- boolean ignore=false;
- for (int j=0;j<chaine.length();j++){
- char c=chaine.charAt(j);
+ public static String SortieTexte(String chaine) { // Enlève les backslash
+ StringBuffer buffer = new StringBuffer();
+ boolean backslash = false;
+ boolean ignore = false;
+ for (int j = 0; j < chaine.length(); j++) {
+ char c = chaine.charAt(j);
if (backslash) {
- if (c=='e') buffer.append(' ');
-// else if (c=='\\') buffer.append('\\');
- else if (c=='n') buffer.append("\n");
- else if(c=='v') buffer.append("");
- else if(c=='l') {
- ignore=true;
+ if (c == 'e')
+ buffer.append(' ');
+ // else if (c=='\\') buffer.append('\\');
+ else if (c == 'n')
+ buffer.append("\n");
+ else if (c == 'v')
+ buffer.append("");
+ else if (c == 'l') {
+ ignore = true;
}
- else if("[]()#\\".indexOf(c)>-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.<br>
* - Unused white spaces are deleted.<br>
* - The character \ is modified to \\ <br>
* - The sequence "\ " is modified to "\e"<br>
* - The sequence "\ " is modified to "\e"<br>
- * - The sequence "\ " is modified to "\e"<br>
- * - The sequence "\ " is modified to "\e"<br>
+ * - The sequence "\ " is modified to "\e"<br>
+ * - The sequence "\ " is modified to "\e"<br>
* @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<st.length();i++){
- char c=st.charAt(i);
- if (c==' ') {
- if (!espace&&buffer.length()!=0) {
- if (backslash) buffer.append("\\e");
+ boolean execution_lancee = Affichage.execution_lancee;
+ for (int i = 0; i < st.length(); i++) {
+ char c = st.charAt(i);
+ if (c == ' ') {
+ if (!espace && buffer.length() != 0) {
+ if (backslash)
+ buffer.append("\\e");
else {
buffer.append(c);
- espace=true;
- mot=false;
- // variable=false;
+ espace = true;
+ mot = false;
+ // variable=false;
}
- backslash=false;
+ backslash = false;
}
}
- else if(c=='\\'&&!backslash) {
- espace=false;
- backslash=true;
+ else if (c == '\\' && !backslash) {
+ espace = false;
+ backslash = true;
}
- else if(c=='\"'){
- if (espace&&crochet_liste<=0){
- mot=true;
+ else if (c == '\"') {
+ if (espace && crochet_liste <= 0) {
+ mot = true;
}
buffer.append(c);
- espace=false;
- backslash=false;
+ espace = false;
+ backslash = false;
}
- else if (c==':'){
- /* if (espace&&crochet_liste<=0){
- variable=true;
- }*/
+ else if (c == ':') {
+ /* if (espace&&crochet_liste<=0){
+ variable=true;
+ }*/
buffer.append(c);
- espace=false;
- backslash=false;
+ espace = false;
+ backslash = false;
}
- else if (c=='['||c==']'||c=='('||c==')'){
+ else if (c == '[' || c == ']' || c == '(' || c == ')') {
//Modifications apportées
if (backslash) {
- buffer.append("\\"+c);
- backslash=false;
+ buffer.append("\\" + c);
+ backslash = false;
}
else {
- if (c=='[') crochet_liste++;
- else if (c==']') crochet_liste--;
- if (espace||buffer.length()==0) {buffer.append(c+" ");espace=true;}
+ if (c == '[')
+ crochet_liste++;
+ else if (c == ']')
+ crochet_liste--;
+ if (espace || buffer.length() == 0) {
+ buffer.append(c + " ");
+ espace = true;
+ }
else {
- buffer.append(" "+c+" ");
- mot=false;
- espace=true;
+ buffer.append(" " + c + " ");
+ mot = false;
+ espace = true;
}
}
}
- else if (c=='+'||c=='-'||c=='*'||c=='/'||c=='='||c=='<'||c=='>'||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<st.length()){
- if (st.charAt(i+1)=='='){
- op+="=";
+ if (c == '<' || c == '>') {
+ 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("&","&amp;");
- st=st.replaceAll("<","&lt;");
- st=st.replaceAll("\"","&quot;");
- st=st.replaceAll(">","&gt;");
- st=st.replaceAll("'","&apos;");
+ public static String specialCharacterXML(String st) {
+ st = st.replaceAll("&", "&amp;");
+ st = st.replaceAll("<", "&lt;");
+ st = st.replaceAll("\"", "&quot;");
+ st = st.replaceAll(">", "&gt;");
+ st = st.replaceAll("'", "&apos;");
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
* <p> If destination exists, it will be replaced
@@ -416,40 +459,126 @@ 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> T readObject(File file, Class<T> 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
* @param dest
* @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));
+ }
}