OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Compare Revisions

Regard whitespace Rev 141 → Rev 142

/trunk/OpenConcerto/.classpath
38,5 → 38,7
<classpathentry kind="lib" path="lib/json-smart-1.3.1.jar"/>
<classpathentry kind="lib" path="lib/icu4j-56_1-module_format+calendar.jar"/>
<classpathentry kind="lib" path="lib/icu4j-56-data-western_europe.jar"/>
<classpathentry kind="lib" path="lib/DS_Desktop_Notify.jar"/>
<classpathentry kind="lib" path="lib/mime_util.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
/trunk/OpenConcerto/lib/mysql-connector-java-5.1.40-bin.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/mysql-connector-java-5.1.40-bin.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/lib/jOpenCalendar.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/accessors-smart-1.1.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/accessors-smart-1.1.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/lib/jOpenDocument-1.4rc2.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/DS_Desktop_Notify.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/DS_Desktop_Notify.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/lib/json-smart-2.2.1.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/json-smart-2.2.1.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/lib/icudata_56.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/icudata_56.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/lib/mime_util.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/mime_util.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/src/org/jopendocument/link/OOConnexion.java
117,15 → 117,33
}
 
public static void main(String[] args) throws IOException {
if (args.length == 0) {
System.out.println("Usage : " + OOConnexion.class.getName() + " officeFile");
if (args.length == 0 || args.length > 2) {
System.out.println("Usage : " + OOConnexion.class.getName() + " officeFile | --type param");
System.out.println("Open officeFile in the default installation of LibreOffice");
System.out.println("--type is either file or url");
System.exit(1);
}
final OOConnexion conn = OOConnexion.create();
if (conn == null)
throw new IllegalStateException("No Office found");
conn.loadDocument(new File(args[0]), false);
final boolean file;
final String arg;
if (args.length == 1) {
file = true;
arg = args[0];
} else if (args[0].equals("--file")) {
file = true;
arg = args[1];
} else if (args[0].equals("--url")) {
file = false;
arg = args[1];
} else {
throw new IllegalArgumentException("Type not valid : " + args[0]);
}
if (file)
conn.loadDocument(new File(arg), false);
else
conn.loadDocumentFromURLAsync(arg, false);
conn.closeConnexion();
}
 
/trunk/OpenConcerto/src/org/jopenchart/sample/devguide/PieChartSample.java
63,14 → 63,13
 
private static void chart3() {
PieChartWithSeparatedLabels c = new PieChartWithSeparatedLabels();
c.setInnerDimension(50, 50);
c.addLabel(new Label("AAAAAA"), Color.red);
c.addLabel(new Label("BBBB"));
c.addLabel(new Label("CCCCCCCCCCCCCCCCCCCCCCCCCCCC"));
c.addLabel(new Label("D"));
c.addLabel(new Label("EEE"));
c.addLabel(new Label("FFF"));
c.addLabel(new Label("GG"));
c.addLabel(new Label("HHH"));
 
c.setDimension(new Dimension(400, 200));
 
ArrayList<Number> l = new ArrayList<Number>();
/trunk/OpenConcerto/src/org/openconcerto/map/model/Ville.java
230,7 → 230,7
}
 
public static synchronized Ville getVilleContaining(String string, String codepostal) {
if (codepostal.length() < 0 && string.length() <= 2) {
if (codepostal.length() == 0 && string.length() <= 2) {
return null;
}
 
/trunk/OpenConcerto/src/org/openconcerto/ui/JDateTime.java
99,6 → 99,7
this.add(this.time, c);
 
this.resetValue();
updateValue();
}
 
protected void updateValue() {
/trunk/OpenConcerto/src/org/openconcerto/ui/translation/messages_en.properties
6,6 → 6,8
saveWindowState=Error while saving size and position of the window
prefCreationError=Error while creating the preference panel
 
missingFile=The file doesn\u2019t exist : {0}
 
userExit.question=Do you really want to quit ?
userExit.title=Quit
 
/trunk/OpenConcerto/src/org/openconcerto/ui/translation/messages_fr.properties
6,6 → 6,8
saveWindowState=Impossible de sauvegarder la taille et la position de la fenêtre
prefCreationError=Impossible de créer le panneau de préférence
 
missingFile=Le fichier n\u2019existe pas : {0}
 
userExit.question=Voulez-vous vraiment quitter ?
userExit.title=Quitter
 
/trunk/OpenConcerto/src/org/openconcerto/ui/component/text/TextBehaviour.java
13,22 → 13,34
package org.openconcerto.ui.component.text;
 
import java.awt.event.ActionEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
 
import javax.swing.AbstractAction;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.text.JTextComponent;
 
/**
* Listens to its passed text component to select all on focus gained and revert when pressing ESC.
* To control the revert action use {@link #REVERT_ACTION_KEY}.
*
* @author Sylvain
*/
public class TextBehaviour {
 
/**
* Use this key to register the revert action in {@link JComponent#getActionMap()}. If none is
* set, this class will simply set an action that {@link JTextComponent#setText(String) restore}
* the text that was on {@link FocusListener#focusGained(FocusEvent) focus gained}.
*/
public static final String REVERT_ACTION_KEY = "action map key to revert value";
 
// MAYBE faire un stopManage() qui enlèverait tous les listeners
public static final TextBehaviour manage(JTextComponent comp) {
return new TextBehaviour(comp);
48,6 → 60,7
// select all on focus gained
// except if the user is selecting with the mouse
this.comp.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
TextBehaviour.this.gained = true;
TextBehaviour.this.initialText = TextBehaviour.this.comp.getText();
57,10 → 70,12
}
});
this.comp.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
TextBehaviour.this.mousePressed = true;
}
 
@Override
public void mouseReleased(MouseEvent e) {
// don't override the user selection
if (TextBehaviour.this.gained && TextBehaviour.this.comp.getSelectedText() == null) {
71,15 → 86,16
TextBehaviour.this.mousePressed = false;
}
});
this.comp.addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent keyEvent) {
// Sert a annuler une saisie
if (keyEvent.getKeyChar() == KeyEvent.VK_ESCAPE) {
this.comp.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), REVERT_ACTION_KEY);
if (this.comp.getActionMap().get(REVERT_ACTION_KEY) == null) {
this.comp.getActionMap().put(REVERT_ACTION_KEY, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
TextBehaviour.this.comp.setText(TextBehaviour.this.initialText);
TextBehaviour.this.comp.selectAll();
}
}
});
}
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/ui/component/DialogCallbackHandler.java
New file
0,0 → 1,307
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.ui.component;
 
/* Java imports */
import java.awt.Component;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RunnableFuture;
 
/* JAAS imports */
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.ConfirmationCallback;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.TextOutputCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.swing.Box;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;
 
/**
* <p>
* Uses a Swing dialog window to query the user for answers to authentication questions. This can be
* used by a JAAS application to instantiate a CallbackHandler
*
* @see javax.security.auth.callback
*/
public class DialogCallbackHandler implements CallbackHandler {
 
/* -- Fields -- */
 
/* The parent window, or null if using the default parent */
private final Component parentComponent;
private static final int JPasswordFieldLen = 8;
private static final int JTextFieldLen = 8;
 
/* -- Methods -- */
 
/**
* Creates a callback dialog with the default parent window.
*/
public DialogCallbackHandler() {
this(null);
}
 
/**
* Creates a callback dialog and specify the parent window.
*
* @param parentComponent the parent window -- specify <code>null</code> for the default parent
*/
public DialogCallbackHandler(final Component parentComponent) {
this.parentComponent = parentComponent;
}
 
/*
* An interface for recording actions to carry out if the user clicks OK for the dialog.
*/
private static interface Action {
void perform();
}
 
/**
* Handles the specified set of callbacks.
*
* @param callbacks the callbacks to handle
* @throws UnsupportedCallbackException if the callback is not an instance of NameCallback or
* PasswordCallback
*/
@Override
public void handle(final Callback[] callbacks) throws UnsupportedCallbackException {
/* Collect messages to display in the dialog */
final List<Object> messages = new ArrayList<Object>(3);
 
/* Collection actions to perform if the user clicks OK */
final List<Action> okActions = new ArrayList<Action>(2);
 
final ConfirmationInfo confirmation = new ConfirmationInfo();
final List<JTextComponent> fields = new ArrayList<JTextComponent>();
 
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof TextOutputCallback) {
final TextOutputCallback tc = (TextOutputCallback) callbacks[i];
 
switch (tc.getMessageType()) {
case TextOutputCallback.INFORMATION:
confirmation.messageType = JOptionPane.INFORMATION_MESSAGE;
break;
case TextOutputCallback.WARNING:
confirmation.messageType = JOptionPane.WARNING_MESSAGE;
break;
case TextOutputCallback.ERROR:
confirmation.messageType = JOptionPane.ERROR_MESSAGE;
break;
default:
throw new UnsupportedCallbackException(callbacks[i], "Unrecognized message type");
}
 
messages.add(tc.getMessage());
 
} else if (callbacks[i] instanceof NameCallback) {
final NameCallback nc = (NameCallback) callbacks[i];
 
final JLabel prompt = new JLabel(nc.getPrompt());
 
final JTextField name = new JTextField(JTextFieldLen);
final String defaultName = nc.getDefaultName();
if (defaultName != null) {
name.setText(defaultName);
}
 
/*
* Put the prompt and name in a horizontal box, and add that to the set of messages.
*/
final Box namePanel = Box.createHorizontalBox();
namePanel.add(prompt);
namePanel.add(name);
messages.add(namePanel);
 
/* Store the name back into the callback if OK */
okActions.add(new Action() {
@Override
public void perform() {
nc.setName(name.getText());
}
});
fields.add(name);
} else if (callbacks[i] instanceof PasswordCallback) {
final PasswordCallback pc = (PasswordCallback) callbacks[i];
 
final JLabel prompt = new JLabel(pc.getPrompt());
 
final JPasswordField password = new JPasswordField(JPasswordFieldLen);
if (!pc.isEchoOn()) {
password.setEchoChar('*');
}
 
final Box passwordPanel = Box.createHorizontalBox();
passwordPanel.add(prompt);
passwordPanel.add(password);
messages.add(passwordPanel);
 
okActions.add(new Action() {
@Override
public void perform() {
pc.setPassword(password.getPassword());
}
});
fields.add(password);
} else if (callbacks[i] instanceof ConfirmationCallback) {
final ConfirmationCallback cc = (ConfirmationCallback) callbacks[i];
 
confirmation.setCallback(cc);
if (cc.getPrompt() != null) {
messages.add(cc.getPrompt());
}
 
} else {
throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
}
}
 
// TODO also create dialog in the EDT
final RunnableFuture<Integer> f = new FutureTask<Integer>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
if (!fields.isEmpty()) {
fields.get(0).addHierarchyListener(new HierarchyListener() {
@Override
public void hierarchyChanged(final HierarchyEvent e) {
if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0 && e.getComponent().isVisible()) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
e.getComponent().requestFocus();
}
});
}
}
});
}
 
/* Display the dialog */
return JOptionPane.showOptionDialog(DialogCallbackHandler.this.parentComponent, messages.toArray(), "Confirmation", /* title */
confirmation.optionType, confirmation.messageType, null, /* icon */
confirmation.options, /* options */
confirmation.initialValue); /* initialValue */
 
}
});
SwingUtilities.invokeLater(f);
final int result;
try {
result = f.get();
} catch (Exception e) {
throw new IllegalStateException(e);
}
/* Perform the OK actions */
if (result == JOptionPane.OK_OPTION || result == JOptionPane.YES_OPTION) {
final Iterator<Action> iterator = okActions.iterator();
while (iterator.hasNext()) {
iterator.next().perform();
}
}
confirmation.handleResult(result);
}
 
/*
* Provides assistance with translating between JAAS and Swing confirmation dialogs.
*/
private static class ConfirmationInfo {
 
private int[] translations;
 
int optionType = JOptionPane.OK_CANCEL_OPTION;
Object[] options = null;
Object initialValue = null;
 
int messageType = JOptionPane.QUESTION_MESSAGE;
 
private ConfirmationCallback callback;
 
/* Set the confirmation callback handler */
void setCallback(final ConfirmationCallback callback) throws UnsupportedCallbackException {
this.callback = callback;
 
final int confirmationOptionType = callback.getOptionType();
switch (confirmationOptionType) {
case ConfirmationCallback.YES_NO_OPTION:
this.optionType = JOptionPane.YES_NO_OPTION;
this.translations = new int[] { JOptionPane.YES_OPTION, ConfirmationCallback.YES, JOptionPane.NO_OPTION, ConfirmationCallback.NO, JOptionPane.CLOSED_OPTION, ConfirmationCallback.NO };
break;
case ConfirmationCallback.YES_NO_CANCEL_OPTION:
this.optionType = JOptionPane.YES_NO_CANCEL_OPTION;
this.translations = new int[] { JOptionPane.YES_OPTION, ConfirmationCallback.YES, JOptionPane.NO_OPTION, ConfirmationCallback.NO, JOptionPane.CANCEL_OPTION,
ConfirmationCallback.CANCEL, JOptionPane.CLOSED_OPTION, ConfirmationCallback.CANCEL };
break;
case ConfirmationCallback.OK_CANCEL_OPTION:
this.optionType = JOptionPane.OK_CANCEL_OPTION;
this.translations = new int[] { JOptionPane.OK_OPTION, ConfirmationCallback.OK, JOptionPane.CANCEL_OPTION, ConfirmationCallback.CANCEL, JOptionPane.CLOSED_OPTION,
ConfirmationCallback.CANCEL };
break;
case ConfirmationCallback.UNSPECIFIED_OPTION:
this.options = callback.getOptions();
/*
* There's no way to know if the default option means to cancel the login, but there
* isn't a better way to guess this.
*/
this.translations = new int[] { JOptionPane.CLOSED_OPTION, callback.getDefaultOption() };
break;
default:
throw new UnsupportedCallbackException(callback, "Unrecognized option type: " + confirmationOptionType);
}
 
final int confirmationMessageType = callback.getMessageType();
switch (confirmationMessageType) {
case ConfirmationCallback.WARNING:
this.messageType = JOptionPane.WARNING_MESSAGE;
break;
case ConfirmationCallback.ERROR:
this.messageType = JOptionPane.ERROR_MESSAGE;
break;
case ConfirmationCallback.INFORMATION:
this.messageType = JOptionPane.INFORMATION_MESSAGE;
break;
default:
throw new UnsupportedCallbackException(callback, "Unrecognized message type: " + confirmationMessageType);
}
}
 
/* Process the result returned by the Swing dialog */
void handleResult(int result) {
if (this.callback == null) {
return;
}
 
for (int i = 0; i < this.translations.length; i += 2) {
if (this.translations[i] == result) {
result = this.translations[i + 1];
break;
}
}
this.callback.setSelectedIndex(result);
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/component/HTMLTextField.java
13,9 → 13,14
package org.openconcerto.ui.component;
 
import org.openconcerto.ui.TM;
import org.openconcerto.utils.ExceptionHandler;
 
import java.awt.Component;
import java.awt.Desktop;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
25,6 → 30,7
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkEvent.EventType;
74,12 → 80,22
}
 
protected void linkActivated(final HyperlinkEvent e, final JComponent src) {
final URI uri = getURI(e);
if (uri == null)
return;
if ("file".equals(uri.getScheme())) {
final File f = new File(uri.getPath());
if (!f.exists()) {
JOptionPane.showMessageDialog((Component) e.getSource(), TM.tr("missingFile", f), null, JOptionPane.WARNING_MESSAGE);
return;
}
}
// this ties into the OS and can block
exec.submit(new Runnable() {
@Override
public void run() {
try {
Desktop.getDesktop().browse(e.getURL().toURI());
Desktop.getDesktop().browse(uri);
} catch (Exception exn) {
ExceptionHandler.handle(src, org.openconcerto.utils.i18n.TM.tr("linkOpenError", e.getURL()), exn);
}
86,4 → 102,13
}
});
}
 
private URI getURI(final HyperlinkEvent e) {
try {
return e.getURL().toURI();
} catch (URISyntaxException exn) {
ExceptionHandler.handle((Component) e.getSource(), "Couldn't get URI", exn);
return null;
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/component/ITextCombo.java
18,6 → 18,7
 
import static org.openconcerto.ui.component.ComboLockedMode.LOCKED;
import static org.openconcerto.ui.component.ComboLockedMode.UNLOCKED;
 
import org.openconcerto.ui.component.InteractionMode.InteractionComponent;
import org.openconcerto.ui.component.combo.ISearchableComboPopup;
import org.openconcerto.ui.component.text.TextComponent;
492,12 → 493,20
* @return <code>true</code> if s is really added.
*/
private final boolean addToCache(String s) {
if (s != null && s.length() > 0 && this.getListModel().getList().indexOf(s) < 0) {
this.addItem(makeObj(s));
return true;
} else
return false;
final boolean added = s != null && s.length() > 0 && this.getListModel().getList().indexOf(s) < 0;
if (added) {
final Object obj = makeObj(s);
// BasicComboBoxUI$Handler.intervalAdded(ListDataEvent) calls contentsChanged() which
// calls JComboBox.configureEditor() with the current selection. But the selection is
// only set by BasicComboBoxUI.Handler.focusLost() so this.addItem() replaces the
// current editor value by the current selection.
this.completing = true;
this.setSelectedItem(obj);
this.completing = false;
this.addItem(obj);
}
return added;
}
 
private final void removeCurrentText() {
final String t = this.getTextComp().getText();
/trunk/OpenConcerto/src/org/openconcerto/ui/component/combo/ISearchableCombo.java
16,6 → 16,7
import static org.openconcerto.ui.component.ComboLockedMode.ITEMS_LOCKED;
import static org.openconcerto.ui.component.ComboLockedMode.LOCKED;
import static org.openconcerto.ui.component.ComboLockedMode.UNLOCKED;
 
import org.openconcerto.laf.LAFUtils;
import org.openconcerto.ui.TM;
import org.openconcerto.ui.component.ComboLockedMode;
25,19 → 26,21
import org.openconcerto.ui.component.MutableListCombo;
import org.openconcerto.ui.component.MutableListComboPopupListener;
import org.openconcerto.ui.component.text.DocumentComponent;
import org.openconcerto.ui.component.text.TextBehaviour;
import org.openconcerto.ui.component.text.TextComponent;
import org.openconcerto.ui.valuewrapper.ValueChangeSupport;
import org.openconcerto.ui.valuewrapper.ValueWrapper;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.CompareUtils;
import org.openconcerto.utils.ListMap;
import org.openconcerto.utils.Value;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.utils.cc.IdentityHashSet;
import org.openconcerto.utils.checks.ValidListener;
import org.openconcerto.utils.checks.ValidState;
import org.openconcerto.utils.model.DefaultIMutableListModel;
import org.openconcerto.utils.model.IListModel;
import org.openconcerto.utils.model.IMutableListModel;
import org.openconcerto.utils.model.ISearchable;
import org.openconcerto.utils.model.ListComboBoxModel;
import org.openconcerto.utils.model.NewSelection;
import org.openconcerto.utils.model.Reloadable;
56,6 → 59,7
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.color.ColorSpace;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusAdapter;
72,11 → 76,9
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
 
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.ImageIcon;
127,6 → 129,7
protected final ISearchableComboPopup<T> popupCompletion;
// fullList
private final DefaultIMutableListModel<ISearchableComboItem<T>> model;
protected ISearchableComboItem<T> initialValue;
// list for the popup
private final ListComboBoxModel listModel;
private final ISearchableComboItem<T> emptyItem;
160,7 → 163,7
private int minimumSearch = 1;
private int maximumResult = 300;
 
private final Map<T, ISearchableComboItem<T>> itemsByOriginalItem;
private final ListMap<T, ISearchableComboItem<T>> itemsByOriginalItem;
protected boolean updating = false;
private ValidState parsedValidState = ValidState.getTrueInstance();
private ValidState validState = ValidState.createCached(false, "Not initialised");
205,7 → 208,7
// items
this.actions = new ArrayList<Action>();
this.cache = null;
this.itemsByOriginalItem = new HashMap<T, ISearchableComboItem<T>>();
this.itemsByOriginalItem = new ListMap<T, ISearchableComboItem<T>>();
this.model = new DefaultIMutableListModel<ISearchableComboItem<T>>();
this.getModel().setSelectOnAdd(false);
this.setOnRemovingOrReplacingSelection(NewSelection.NONE);
610,12 → 613,10
for (final T originalItem : originalItems) {
final ISearchableComboItem<T> textSelectorItem;
if (this.itemsByOriginalItem.containsKey(originalItem)) {
// allow another item with the same original : add another item to our model, but
// keep the first one in itemsByOriginalItem (this map is only used in setValue() to
// quickly find the ISearchableComboItem)
// allow another item with the same original since our source is a list
textSelectorItem = createItem(originalItem);
// see ISearchableComboPopup.validateSelection()
assert !textSelectorItem.equals(this.itemsByOriginalItem.get(originalItem)) : "Have to not be equal to be able to choose one or the other";
assert !this.itemsByOriginalItem.get(originalItem).contains(textSelectorItem) : "Have to not be equal to be able to choose one or the other";
} else {
// reuse the selected value, otherwise the popup will select nothing
if (sel != null && CompareUtils.equals(selOriginal, originalItem))
622,19 → 623,25
textSelectorItem = sel;
else
textSelectorItem = createItem(originalItem);
this.itemsByOriginalItem.put(originalItem, textSelectorItem);
}
this.itemsByOriginalItem.add(originalItem, textSelectorItem);
toAdd.add(textSelectorItem);
}
// only 1 fire
this.getModel().addAll(index, toAdd);
assert this.itemsByOriginalItem.allValues().size() == getModel().getSize();
}
 
private void rmItemsFromModel(final int index0, final int index1) {
if (index0 == 0 && index1 == getModel().getSize() - 1) {
this.itemsByOriginalItem.clear();
} else {
for (final ISearchableComboItem<T> item : getModel().getList().subList(index0, index1 + 1)) {
this.itemsByOriginalItem.remove(item.getOriginal(), item);
}
}
getModel().removeElementsAt(index0, index1);
// remove from our map
// ATTN for ~35000 items, new HashSet() got us from 6000ms to 8ms !
this.itemsByOriginalItem.keySet().retainAll(new HashSet<T>(getCache().getList()));
assert this.itemsByOriginalItem.allValues().size() == getModel().getSize();
}
 
// conversion
744,11 → 751,12
final ISearchableComboItem<T> comboItem;
// as in docChanged(), "" means empty (otherwise an ISearchableComboItem will be created and
// it won't be contained in the list)
if (val == null || "".equals(val))
if (val == null || "".equals(val) || !valid.isValid()) {
comboItem = null;
else if (this.itemsByOriginalItem.containsKey(val)) {
comboItem = this.itemsByOriginalItem.get(val);
} else if (this.itemsByOriginalItem.containsKey(val)) {
comboItem = this.itemsByOriginalItem.get(val).get(0);
} else {
log("in " + this.getClass().getSimpleName() + ".setValue createItem() since not found");
// always set the passed value, even if it's not valid (e.g.
// getMode().valueMustBeInList())
comboItem = createItem(val);
758,8 → 766,7
 
final void setValue(final ISearchableComboItem<T> val) {
log("entering " + this.getClass().getSimpleName() + ".setValue(ISearchableComboItem) " + val);
assert this.isEmptyItem(val) || new IdentityHashSet<ISearchableComboItem<T>>(this.getModelValues()).contains(val) : "Item not in model, perhaps use setValue(T)";
// valid since val is in our model
// valid since we didn't try to parse a string
this.setComboItemValue(val, ValidState.getTrueInstance());
}
 
801,7 → 808,23
 
this.updateValidState();
this.supp.fireValueChange();
 
// the selection has changed, this is where we reset the search
// some caches force the inclusion of the selection, so clear the search after setting the
// value to avoid :
// 1. clear search
// 2. cache reloads without the selection
// 3. fireValueChange()
// 4. cache reloads again to include the selection
// instead we want :
// 1. fireValueChange()
// 2. no reload since in general the value was in the cache
// 3. clear search
// 4. cache reloads with the selection
if (this.getCache() instanceof ISearchable) {
((ISearchable) this.getCache()).setSearch(null, null);
}
}
 
private final void setText(final String newText) {
if (!this.text.getText().equals(newText)) {
832,9 → 855,11
protected final void docChanged(final DocumentEvent e) {
if (!this.updating) {
final String text = SimpleDocumentListener.getText(e.getDocument());
final T val;
ValidState valid = ValidState.getTrueInstance();
if (text.length() == 0) {
// "" means empty
this.setValue(null, ValidState.getTrueInstance());
val = null;
} else if (this.getMode().valueMustBeInList() && !this.getMode().isListMutable()) {
// value can only be set by the popup (or setMatchingCompletions()) ; the list
// cannot be modified. Thus don't call stringToT() as it might not be implemented
842,14 → 867,18
// this avoids having to decide between 2 different values with the same label, or
// worse this is locked and one of those 2 values are not in us. In that case
// setting the invalid one will in fact select the other.
this.setValue(null, ValidState.createCached(false, "la valeur n'a pas été sélectionnée dans la liste"));
val = null;
valid = ValidState.createCached(false, "la valeur n'a pas été sélectionnée dans la liste");
} else {
T parsed = null;
try {
this.setValue(stringToT(text));
parsed = stringToT(text);
} catch (Exception exn) {
this.setValue(null, ValidState.createCached(false, "la valeur n'est pas correcte: " + exn.getLocalizedMessage()));
valid = ValidState.createCached(false, "la valeur n'est pas correcte: " + exn.getLocalizedMessage());
}
val = valid.isValid() ? parsed : null;
}
this.setValue(val, valid);
if (this.isSearchable())
this.updateAutoCompletion(text);
}
1202,6 → 1231,8
@Override
public void focusGained(FocusEvent e) {
firePropertyChange("textCompFocused", false, true);
// set on focus gained like TextBehaviour
ISearchableCombo.this.initialValue = getSelection();
}
 
@Override
1211,6 → 1242,20
firePropertyChange("textCompFocused", true, false);
}
});
this.text.getActionMap().put(TextBehaviour.REVERT_ACTION_KEY, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
final ISearchableComboItem<T> v = ISearchableCombo.this.initialValue;
if (v == null || getModel().getList().contains(v)) {
// quicker and allow to tell apart original items that are equal
setValue(v);
} else {
// the combo might have been refreshed several times and the initial item gone.
// But there might be a new one with the same value.
setValue(v.getOriginal());
}
}
});
this.text.addMouseListener(this.clickL);
this.text.addMouseMotionListener(this.dragL);
}
/trunk/OpenConcerto/src/org/openconcerto/ui/component/combo/ISearchableComboCompletionThread.java
87,6 → 87,9
final String aText = this.t.trim();
final String normalizedText = aText.length() < minimumSearch ? "" : aText;
 
// null : not searchable
// true : search changed
// false : search didn't change
Boolean searched = null;
// If there was a search and now the text is below minimum, we must unset the search
// Efficient since setSearch() only carry out its action if the search changes
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIFilesWithSelectionContext.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/ui/light/IUserControl.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/ui/light/IUserControlContainer.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/ui/light/NotSpecifedConvertor.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIListRow.java
New file
0,0 → 1,69
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.ui.light;
 
import org.openconcerto.utils.io.JSONConverter;
 
import net.minidev.json.JSONObject;
 
public class LightUIListRow extends LightUIPanel {
private Number rowId;
 
// Init from json constructor
public LightUIListRow(final JSONObject json) {
super(json);
}
 
// Clone constructor
public LightUIListRow(final LightUIListRow listItem) {
super(listItem);
this.rowId = listItem.rowId;
}
 
public LightUIListRow(final LightUIList parent, final Number rowId) {
super(parent.getId() + ".item.panel." + rowId.toString());
this.rowId = rowId;
this.setType(TYPE_LIST_ROW);
this.setFillHeight(false);
}
 
public LightUIListRow(final LightUIList parent, final Number id, final String label) {
this(parent, id);
 
final LightUILine line = new LightUILine();
line.addChild(new LightUILabel(this.getId() + ".label", label));
this.addChild(line);
}
 
public Number getRowId() {
return this.rowId;
}
 
public LightUIListRow clone() {
return new LightUIListRow(this);
}
 
@Override
public JSONObject toJSON() {
final JSONObject json = super.toJSON();
json.put("row-id", this.getRowId());
return json;
}
 
@Override
public void fromJSON(final JSONObject json) {
super.fromJSON(json);
this.rowId = JSONConverter.getParameterFromJSON(json, "row-id", Number.class);
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/ColumnsSpec.java
13,6 → 13,7
package org.openconcerto.ui.light;
 
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.io.JSONConverter;
import org.openconcerto.utils.io.Transferable;
 
63,9 → 64,6
if (columns == null) {
throw new IllegalArgumentException("null columns");
}
if (columns.isEmpty()) {
throw new IllegalArgumentException("empty columns");
}
this.columns = columns;
 
// Possible checks
72,9 → 70,6
if (possibleColumnIds == null) {
throw new IllegalArgumentException("null possible column ids");
}
if (possibleColumnIds.isEmpty()) {
throw new IllegalArgumentException("empty possible column ids");
}
this.possibleColumnIds = possibleColumnIds;
 
// Sort assign
82,62 → 77,62
 
}
 
public String getId() {
public final String getId() {
return this.id;
}
 
public List<String> getPossibleColumnIds() {
public final List<String> getPossibleColumnIds() {
return this.possibleColumnIds;
}
 
public List<String> getSortedIds() {
public final List<String> getSortedIds() {
return this.sortedIds;
}
 
public int getFixedColumns() {
public final int getFixedColumns() {
return this.fixedColumns;
 
}
 
public int getColumnCount() {
public final int getColumnCount() {
return this.columns.size();
 
}
 
public ColumnSpec getColumn(int i) {
public final ColumnSpec getColumn(int i) {
return this.columns.get(i);
}
 
public ColumnSpec setColumn(int i, final ColumnSpec column) {
public final ColumnSpec setColumn(int i, final ColumnSpec column) {
return this.columns.set(i, column);
}
 
public Boolean isAdaptWidth() {
public final Boolean isAdaptWidth() {
return this.adaptWidth;
}
 
public void setAdaptWidth(final boolean adaptWidth) {
public final void setAdaptWidth(final boolean adaptWidth) {
this.adaptWidth = adaptWidth;
}
 
public Boolean isAllowMove() {
public final Boolean isAllowMove() {
return this.allowMove;
}
 
public void setAllowMove(final boolean allowMove) {
public final void setAllowMove(final boolean allowMove) {
this.allowMove = allowMove;
}
 
public Boolean isAllowResize() {
public final Boolean isAllowResize() {
return this.allowResize;
}
 
public void setAllowResize(final boolean allowResize) {
public final void setAllowResize(final boolean allowResize) {
this.allowResize = allowResize;
}
 
public List<String> getColumnsIds() {
ArrayList<String> result = new ArrayList<String>(this.columns.size());
final ArrayList<String> result = new ArrayList<String>(this.columns.size());
for (ColumnSpec c : this.columns) {
result.add(c.getId());
}
144,8 → 139,7
return result;
}
 
public void setUserPrefs(final Document columnsPrefs) {
 
public final boolean setUserPrefs(final Document columnsPrefs) {
if (columnsPrefs != null) {
// user preferences application
final Element rootElement = columnsPrefs.getRootElement();
173,9 → 167,9
throw new IllegalArgumentException("ColumnSpec setPrefs - Invalid column node for " + columnId + ", it must have attribute width, min-width, max-width");
}
 
final int width = Integer.parseInt(xmlColumn.getAttribute("width").getValue());
final int maxWidth = Integer.parseInt(xmlColumn.getAttribute("max-width").getValue());
final int minWidth = Integer.parseInt(xmlColumn.getAttribute("min-width").getValue());
final double width = Double.parseDouble(xmlColumn.getAttribute("width").getValue());
final double maxWidth = Double.parseDouble(xmlColumn.getAttribute("max-width").getValue());
final double minWidth = Double.parseDouble(xmlColumn.getAttribute("min-width").getValue());
 
columnSpec.setPrefs(width, maxWidth, minWidth);
if (i != j) {
182,6 → 176,8
final ColumnSpec swap = this.columns.get(i);
this.columns.set(i, this.columns.get(j));
this.columns.set(j, swap);
i--;
}
find = true;
break;
188,27 → 184,16
}
}
if (!find) {
System.out.println("XML columns preferences does'nt contain this column: " + columnId);
return false;
}
}
} else {
System.out.println("ColumnsSpec.setUserPrefs() - Incorrect columns count in XML for ColumnsSpec: " + this.id);
return false;
}
}
return true;
}
 
public Document createDefaultXmlPref() {
final Element rootElement = new Element("list");
 
for (final ColumnSpec column : this.columns) {
final Element columnElement = column.createXmlColumnPref();
rootElement.addContent(columnElement);
}
final Document xmlConf = new Document(rootElement);
 
return xmlConf;
}
 
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(this.id);
225,15 → 210,15
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.id = in.readUTF();
this.fixedColumns = in.readInt();
this.columns = (List<ColumnSpec>) in.readObject();
this.possibleColumnIds = (List<String>) in.readObject();
this.sortedIds = (List<String>) in.readObject();
this.columns = CollectionUtils.castList((List<?>) in.readObject(), ColumnSpec.class);
this.possibleColumnIds = CollectionUtils.castList((List<?>) in.readObject(), String.class);
this.sortedIds = CollectionUtils.castList((List<?>) in.readObject(), String.class);
this.allowMove = in.readBoolean();
this.allowResize = in.readBoolean();
this.adaptWidth = in.readBoolean();
}
 
public List<Object> getDefaultValues() {
public final List<Object> getDefaultValues() {
final List<Object> l = new ArrayList<Object>();
for (ColumnSpec column : this.columns) {
final Object v = column.getDefaultValue();
242,7 → 227,7
return l;
}
 
public ColumnSpec getColumn(String id) {
public final ColumnSpec getColumn(String id) {
for (ColumnSpec c : this.columns) {
if (c.getId().equals(id)) {
return c;
251,7 → 236,7
return null;
}
 
public ColumnSpec getColumnWithEditor(String id) {
public final ColumnSpec getColumnWithEditor(String id) {
for (ColumnSpec c : this.columns) {
LightUIElement editor = c.getEditor();
if (editor != null && c.getEditor().getId().equals(id)) {
290,30 → 275,19
 
@Override
public void fromJSON(final JSONObject json) {
this.id = (String) JSONConverter.getParameterFromJSON(json, "id", String.class);
this.fixedColumns = (Integer) JSONConverter.getParameterFromJSON(json, "fixed-columns", Integer.class);
this.adaptWidth = (Boolean) JSONConverter.getParameterFromJSON(json, "adapt-width", Boolean.class, false);
this.allowMove = (Boolean) JSONConverter.getParameterFromJSON(json, "allow-move", Boolean.class, false);
this.allowResize = (Boolean) JSONConverter.getParameterFromJSON(json, "allow-resize", Boolean.class, false);
final JSONArray jsonSortedIds = (JSONArray) JSONConverter.getParameterFromJSON(json, "sorted-ids", JSONArray.class, null);
if (jsonSortedIds != null) {
this.sortedIds = (List<String>) (List<?>) jsonSortedIds;
}
final JSONArray jsonPossibleColumnIds = (JSONArray) JSONConverter.getParameterFromJSON(json, "possible-column-ids", JSONArray.class, null);
if (jsonPossibleColumnIds != null) {
this.possibleColumnIds = (List<String>) (List<?>) jsonPossibleColumnIds;
}
this.id = JSONConverter.getParameterFromJSON(json, "id", String.class);
this.fixedColumns = JSONConverter.getParameterFromJSON(json, "fixed-columns", Integer.class);
this.adaptWidth = JSONConverter.getParameterFromJSON(json, "adapt-width", Boolean.class, false);
this.allowMove = JSONConverter.getParameterFromJSON(json, "allow-move", Boolean.class, false);
this.allowResize = JSONConverter.getParameterFromJSON(json, "allow-resize", Boolean.class, false);
this.sortedIds = CollectionUtils.castList(JSONConverter.getParameterFromJSON(json, "sorted-ids", List.class, new ArrayList<String>()), String.class);
this.possibleColumnIds = CollectionUtils.castList(JSONConverter.getParameterFromJSON(json, "possible-column-ids", List.class, new ArrayList<String>()), String.class);
 
final JSONArray jsonColumns = (JSONArray) JSONConverter.getParameterFromJSON(json, "columns", JSONArray.class, null);
final List<JSONObject> jsonColumns = CollectionUtils.castList(JSONConverter.getParameterFromJSON(json, "columns", JSONArray.class, null), JSONObject.class);
if (jsonColumns != null) {
final int columnsSize = jsonColumns.size();
for (int i = 0; i < columnsSize; i++) {
final Object objColumnSpec = jsonColumns.get(i);
if (!(objColumnSpec instanceof JSONObject)) {
throw new IllegalArgumentException("invalid value for 'columns', List<ColumnSpec> expected");
for (final JSONObject jsonColumn : jsonColumns) {
this.columns.add(new ColumnSpec(jsonColumn));
}
this.columns.add(new ColumnSpec((JSONObject) objColumnSpec));
}
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/CustomEditorProvider.java
13,8 → 13,6
package org.openconcerto.ui.light;
 
public interface CustomEditorProvider {
 
public LightUIElement createUIElement(String id);
 
public abstract class CustomEditorProvider {
public abstract LightUIElement createUIElement(final String id);
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIButton.java
58,4 → 58,10
listener.actionPerformed(new ActionEvent(this, 1, "click"));
}
}
 
@Override
public void destroy() {
super.destroy();
this.clickListeners.clear();
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUITextArea.java
17,7 → 17,7
 
import net.minidev.json.JSONObject;
 
public class LightUITextArea extends LightUIElement implements IUserControl {
public class LightUITextArea extends LightUserControl {
 
private int nbLine = 4;
 
70,13 → 70,13
}
 
@Override
public void setValueFromContext(final Object value) {
public void _setValueFromContext(final Object value) {
this.setValue((String) JSONConverter.getObjectFromJSON(value, String.class));
}
 
@Override
public Object getValueFromContext() {
return this.getValue();
public Object getValueForContext() {
return (this.getValue() == null) ? null : ((this.getValue().trim().equals("")) ? null : this.getValue());
}
 
@Override
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUILabel.java
40,21 → 40,15
}
 
public LightUILabel(final String id) {
super(id);
this.setType(TYPE_LABEL);
this(id, "", false);
}
 
public LightUILabel(final String id, final boolean isBold) {
super(id);
this.setType(TYPE_LABEL);
this.setFontBold(isBold);
this(id, "", isBold);
}
 
public LightUILabel(final String id, final String label) {
super(id);
this.setType(TYPE_LABEL);
 
this.setLabel(label);
this(id, label, false);
}
 
public LightUILabel(final String id, final String label, final boolean isBold) {
/trunk/OpenConcerto/src/org/openconcerto/ui/light/StringValueConvertor.java
14,6 → 14,7
package org.openconcerto.ui.light;
 
import java.util.List;
import java.util.Map.Entry;
 
public abstract class StringValueConvertor extends ComboValueConvertor<String> {
 
30,9 → 31,11
/**
* @param index: index of listId.
*
* @return id store at index in listId.
* @return id store at index in listId or null if index is under 1.
*
* @throws IllegalArgumentException when index is out out bounds or if list of ids is not initialized
*/
public String getIdFromIndex(final Integer index) {
public String getIdFromIndex(final Integer index) throws IllegalArgumentException {
int realIndex = index;
// values start at 1.
realIndex = index - 1;
42,13 → 45,18
if (realIndex >= this.listId.size()) {
throw new IllegalArgumentException("index is out of bounds");
}
if(realIndex == -1){
return null;
} else {
return this.listId.get(realIndex);
}
}
 
/**
* @param id: id store in listId.
*
* @return index of id in listId.
* @return index of id in listId or null if not found.
*/
public Integer getIndexFromId(final String id) {
if (this.listId == null) {
66,12 → 74,16
}
@Override
public void fillComboWithValue(final LightUIComboBox combo) {
public void fillComboWithValue(final LightUIComboBox combo, final String selectedValue) {
int i = 1;
for(final String value : this.values.values()) {
for (final Entry<String, String> entry : this.values.entrySet()) {
final LightUIComboBoxElement comboElement = new LightUIComboBoxElement(i);
comboElement.setValue1(value);
comboElement.setValue1(entry.getValue());
combo.addValue(comboElement);
 
if (entry.getKey().equals(selectedValue)) {
combo.setSelectedValue(comboElement);
}
i++;
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUITabbed.java
17,7 → 17,7
 
import net.minidev.json.JSONObject;
 
public abstract class LightUITabbed extends LightUIContainer implements IUserControlContainer {
public abstract class LightUITabbed extends LightUserControlContainer {
// Init from json constructor
public LightUITabbed(final JSONObject json) {
super(json);
50,13 → 50,16
}
 
@Override
public void setValueFromContext(final Object value) {
public void _setValueFromContext(final Object value) {
final JSONObject jsonContext = (JSONObject) JSONConverter.getObjectFromJSON(value, JSONObject.class);
if (jsonContext == null) {
System.err.println("LightUITabbed.setValueFromContext() - json is null for this panel: " + this.getId());
} else {
final int childCount = this.getChildrenCount();
for (int i = 0; i < childCount; i++) {
final LightUITab child = this.getChild(i, LightUITab.class);
if (jsonContext.containsKey(child.getUUID())) {
child.setValueFromContext(jsonContext.get(child.getUUID()));
child._setValueFromContext(jsonContext.get(child.getUUID()));
} else {
System.out.println("LightUITabbed.setValueFromContext() - Context doesn't contains value for UUID: " + child.getUUID() + " ID: " + child.getId());
}
63,3 → 66,4
}
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUserControl.java
New file
0,0 → 1,46
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.ui.light;
 
import net.minidev.json.JSONObject;
 
public abstract class LightUserControl extends LightUIElement {
public LightUserControl(final JSONObject json) {
super(json);
}
 
public LightUserControl(final String id) {
super(id);
}
 
public LightUserControl(final LightUserControl userControl) {
super(userControl);
}
 
public void setValueFromContext(final Object value) {
if (this.isEnabled()) {
this._setValueFromContext(value);
}
}
 
/**
* Set value for all elements send by client with buttons TYPE_BUTTON_WITH_SELECTION_CONTEXT and
* TYPE_BUTTON_WITH_CONTEXT
*
* @param value to set for element
*/
protected abstract void _setValueFromContext(final Object value);
 
public abstract Object getValueForContext();
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUISlider.java
New file
0,0 → 1,102
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.ui.light;
 
import org.openconcerto.utils.io.JSONConverter;
 
import net.minidev.json.JSONObject;
 
public class LightUISlider extends LightUserControl {
Integer maxValue = 1;
Integer minValue = 0;
Integer increment = 1;
 
public LightUISlider(final String id) {
super(id);
this.setType(TYPE_SLIDER);
this.setValueType(VALUE_TYPE_INTEGER);
this.setValue("0");
}
 
public LightUISlider(final JSONObject json) {
super(json);
}
 
public LightUISlider(final LightUISlider slider) {
super(slider);
this.maxValue = slider.maxValue;
this.minValue = slider.minValue;
this.increment = slider.increment;
}
 
public Integer getMaxValue() {
return this.maxValue;
}
 
public void setMaxValue(final int maxValue) {
this.maxValue = maxValue;
}
 
public Integer getMinValue() {
return this.minValue;
}
 
public void setMinValue(final int minValue) {
this.minValue = minValue;
}
 
public Integer getIncrement() {
return this.increment;
}
 
public void setIncrement(final int increment) {
this.increment = increment;
}
 
@Override
public JSONObject toJSON() {
final JSONObject json = super.toJSON();
json.put("max-value", this.maxValue);
json.put("min-value", this.minValue);
json.put("increment", this.increment);
 
return json;
}
 
@Override
public void fromJSON(JSONObject json) {
super.fromJSON(json);
this.maxValue = JSONConverter.getParameterFromJSON(json, "max-value", Integer.class, 1);
this.minValue = JSONConverter.getParameterFromJSON(json, "min-value", Integer.class, 0);
this.increment = JSONConverter.getParameterFromJSON(json, "increment", Integer.class, 1);
}
 
@Override
public void _setValueFromContext(Object value) {
if (value instanceof Number) {
this.setValue(value.toString());
} else {
throw new IllegalArgumentException("Incorrect value " + value + "type for ui element: " + this.getId());
}
}
 
@Override
public Object getValueForContext() {
if (this.getValue() == null) {
return null;
} else {
return Integer.parseInt(this.getValue());
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIContainer.java
22,7 → 22,7
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
 
public abstract class LightUIContainer extends LightUIElement {
public class LightUIContainer extends LightUIElement {
private List<LightUIElement> children;
 
public LightUIContainer(final String id) {
47,6 → 47,15
this.children.add(child);
}
 
public void addChildren(final List<LightUIElement> children) {
if (children == null) {
throw new IllegalArgumentException("List null, id:" + this.getId());
}
for(final LightUIElement child: children){
this.addChild(child);
}
}
 
public void insertChild(final int index, final LightUIElement child) {
if (child == null) {
throw new IllegalArgumentException("Attempt to put null child in container, id:" + this.getId());
72,7 → 81,7
 
public <T extends LightUIElement> T getChild(final int index, final Class<T> expectedClass) {
if (this.children != null)
return (T) this.children.get(index);
return expectedClass.cast(this.children.get(index));
else
return null;
}
79,7 → 88,7
 
public <T extends LightUIElement> T getFirstChild(final Class<T> expectedClass) {
if (this.getChildrenCount() > 0)
return (T) this.children.get(0);
return expectedClass.cast(this.children.get(0));
else
return null;
}
104,7 → 113,7
final LightUIElement child = this.children.get(i);
if (child.getId().equals(pChild.getId())) {
this.children.set(i, pChild);
child.setParent(this);
pChild.setParent(this);
return true;
}
if (child instanceof LightUIContainer) {
125,7 → 134,7
return this.findChild(searchParam, byUUID, LightUIElement.class);
}
 
public <T extends LightUIElement> T findChild(final String searchParam, final boolean byUUID, final Class<T> childClass) {
public <T extends LightUIElement> T findChild(final String searchParam, final boolean byUUID, final Class<T> expectedClass) {
final int childCount = this.getChildrenCount();
LightUIElement result = null;
for (int i = 0; i < childCount; i++) {
143,7 → 152,7
}
 
if (child instanceof LightUIContainer) {
result = ((LightUIContainer) child).findChild(searchParam, byUUID, childClass);
result = ((LightUIContainer) child).findChild(searchParam, byUUID, expectedClass);
if (result != null) {
break;
}
150,7 → 159,7
}
 
if (child instanceof LightUITable) {
result = ((LightUITable) child).findElement(searchParam, byUUID, childClass);
result = ((LightUITable) child).findElement(searchParam, byUUID, expectedClass);
if (result != null) {
break;
}
158,15 → 167,39
}
 
if (result != null) {
if (childClass.isAssignableFrom(result.getClass())) {
return (T) result;
if (expectedClass.isAssignableFrom(result.getClass())) {
return expectedClass.cast(result);
} else {
throw new InvalidClassException(childClass.getName(), result.getClass().getName(), result.getId());
throw new InvalidClassException(expectedClass.getName(), result.getClass().getName(), result.getId());
}
}
return null;
}
 
public <T extends LightUIElement> List<T> findChildren(final Class<T> expectedClass, final boolean recursively) {
final List<T> result = new ArrayList<T>();
final int childCount = this.getChildrenCount();
for (int i = 0; i < childCount; i++) {
final LightUIElement child = this.getChild(i);
 
if (recursively) {
if (child instanceof LightUIContainer) {
result.addAll(((LightUIContainer) child).findChildren(expectedClass, true));
}
 
if (child instanceof LightUITable) {
result.addAll(((LightUITable) child).findChildren(expectedClass, true));
}
}
 
if (expectedClass.isAssignableFrom(child.getClass())) {
result.add(expectedClass.cast(child));
}
}
 
return result;
}
 
@Override
protected void copy(LightUIElement element) {
super.copy(element);
230,4 → 263,12
}
}
}
 
@Override
public void destroy() {
super.destroy();
for (final LightUIElement child : this.children) {
child.destroy();
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/RowsBulk.java
31,6 → 31,7
private List<Row> rows;
private int offset;
private int total;
private boolean remove;
 
public RowsBulk() {// Serialization
}
39,10 → 40,11
this.fromJSON(json);
}
 
public RowsBulk(List<Row> rows, int offset, int total) {
public RowsBulk(List<Row> rows, int offset, int total, final boolean remove) {
this.rows = rows;
this.offset = offset;
this.total = total;
this.remove = remove;
}
 
// Sending by column : size gain is 5%
56,7 → 58,7
int columnCount = in.readByte();
// id
for (int j = 0; j < rowCount; j++) {
Row row = new Row(in.readLong(), columnCount);
Row row = new Row((Number) in.readObject(), columnCount);
this.rows.add(row);
}
 
71,6 → 73,7
}
this.offset = in.readInt();
this.total = in.readInt();
this.remove = in.readBoolean();
}
 
@Override
86,7 → 89,7
// ids
for (int j = 0; j < rowCount; j++) {
Row row = this.rows.get(j);
out.writeLong(row.getId());
out.writeObject(row.getId());
 
}
 
104,20 → 107,25
}
out.writeInt(this.offset);
out.writeInt(this.total);
out.writeBoolean(this.remove);
}
 
public List<Row> getRows() {
public final List<Row> getRows() {
return this.rows;
}
 
public int getOffset() {
public final int getOffset() {
return this.offset;
}
 
public int getTotal() {
public final int getTotal() {
return this.total;
}
 
public final boolean isRemove() {
return this.remove;
}
 
@Override
public JSONObject toJSON() {
final JSONObject result = new JSONObject();
125,20 → 133,26
result.put("rows", JSONConverter.getJSON(this.rows));
result.put("offset", this.offset);
result.put("total", this.total);
result.put("remove", this.remove);
return result;
}
 
@Override
public void fromJSON(final JSONObject json) {
this.offset = (Integer) JSONConverter.getParameterFromJSON(json, "offset", Integer.class);
this.total = (Integer) JSONConverter.getParameterFromJSON(json, "total", Integer.class);
this.offset = JSONConverter.getParameterFromJSON(json, "offset", Integer.class);
this.total = JSONConverter.getParameterFromJSON(json, "total", Integer.class);
 
final JSONArray jsonRows = (JSONArray) JSONConverter.getParameterFromJSON(json, "rows", JSONArray.class);
final JSONArray jsonRows = JSONConverter.getParameterFromJSON(json, "rows", JSONArray.class);
this.rows = new ArrayList<Row>();
if (jsonRows != null) {
for (final Object o : jsonRows) {
this.rows.add(new Row((JSONObject) JSONConverter.getObjectFromJSON(o, JSONObject.class)));
this.rows.add(new Row(JSONConverter.getObjectFromJSON(o, JSONObject.class)));
}
}
}
 
@Override
public String toString() {
return this.rows.size() + " rows at offset " + offset + " (total:" + total + ") remove:" + this.remove;
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIHourEditor.java
New file
0,0 → 1,85
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.ui.light;
 
import org.openconcerto.utils.io.JSONConverter;
 
import net.minidev.json.JSONObject;
 
public class LightUIHourEditor extends LightUserControl {
 
private int hour;
private int minute;
 
public LightUIHourEditor(final JSONObject json) {
super(json);
}
 
public LightUIHourEditor(final String id, int hour, int minute) {
super(id);
this.hour = hour;
this.minute = minute;
this.setType(TYPE_HOUR_EDITOR);
this.setValueType(LightUIElement.VALUE_TYPE_INTEGER);
}
 
public LightUIHourEditor(LightUIHourEditor lightUIHourEditor) {
super(lightUIHourEditor);
this.hour = lightUIHourEditor.hour;
this.minute = lightUIHourEditor.minute;
}
 
public int getHour() {
return hour;
}
 
public void setHour(int hour) {
this.hour = hour;
}
 
public int getMinute() {
return minute;
}
 
public void setMinute(int minute) {
this.minute = minute;
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIHourEditor(json);
}
};
}
 
@Override
public Object getValueForContext() {
return this.hour + 60 * this.minute;
}
 
@Override
public void _setValueFromContext(Object value) {
final Integer strValue = (Integer) JSONConverter.getObjectFromJSON(value, Integer.class);
this.hour = strValue / 60;
this.minute = strValue % 60;
}
 
@Override
public LightUIElement clone() {
return new LightUIHourEditor(this);
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUICheckBox.java
15,7 → 15,7
 
import net.minidev.json.JSONObject;
 
public class LightUICheckBox extends LightUIElement implements IUserControl {
public class LightUICheckBox extends LightUserControl {
 
public LightUICheckBox(final JSONObject json) {
super(json);
64,12 → 64,12
}
 
@Override
public Object getValueFromContext() {
public Object getValueForContext() {
return this.isChecked();
}
 
@Override
public void setValueFromContext(Object value) {
public void _setValueFromContext(Object value) {
boolean bValue = false;
if (value != null) {
if (value.equals("true")) {
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIFileUploadWithSelection.java
New file
0,0 → 1,80
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.ui.light;
 
import org.openconcerto.utils.io.JSONConverter;
 
import net.minidev.json.JSONObject;
 
public class LightUIFileUploadWithSelection extends LightUIFileUpload {
 
private static final String TABLE_ID_JSON_KEY = "table-id";
private String tableId;
 
public LightUIFileUploadWithSelection(final JSONObject json) {
super(json);
}
 
public LightUIFileUploadWithSelection(final LightUIFileUploadWithSelection file) {
super(file);
}
 
public LightUIFileUploadWithSelection(final String id, final String tableId, final String sendFileUrl) {
super(id, sendFileUrl);
this.setType(LightUIElement.TYPE_FILE_UPLOAD_WITH_SELECTION);
this.setGridWidth(1);
 
this.tableId = tableId;
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIFileUploadWithSelection(json);
}
};
}
 
@Override
public LightUIElement clone() {
return new LightUIFileUploadWithSelection(this);
}
 
@Override
protected void copy(LightUIElement element) {
super.copy(element);
if (!(element instanceof LightUIFileUploadWithSelection)) {
throw new InvalidClassException(this.getClassName(), element.getClassName(), element.getId());
}
 
final LightUIFileUploadWithSelection files = (LightUIFileUploadWithSelection) element;
this.tableId = files.tableId;
}
 
@Override
public void fromJSON(final JSONObject json) {
super.fromJSON(json);
 
this.tableId = JSONConverter.getParameterFromJSON(json, TABLE_ID_JSON_KEY, String.class);
}
 
@Override
public JSONObject toJSON() {
final JSONObject json = super.toJSON();
json.put(TABLE_ID_JSON_KEY, this.tableId);
return json;
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUITextField.java
17,7 → 17,7
 
import net.minidev.json.JSONObject;
 
public class LightUITextField extends LightUIElement implements IUserControl {
public class LightUITextField extends LightUserControl {
public LightUITextField(final String id) {
super(id);
this.setType(TYPE_TEXT_FIELD);
48,13 → 48,13
}
 
@Override
public Object getValueFromContext() {
return this.getValue();
public Object getValueForContext() {
return (this.getValue() == null) ? null : ((this.getValue().trim().equals("")) ? null : this.getValue());
}
 
@Override
public void setValueFromContext(final Object value) {
final String strValue = (String) JSONConverter.getObjectFromJSON(value, String.class);
public void _setValueFromContext(final Object value) {
final String strValue = JSONConverter.getObjectFromJSON(value, String.class);
if (strValue != null && !strValue.trim().isEmpty()) {
this.setValue(strValue);
} else {
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIButtonLink.java
13,6 → 13,7
package org.openconcerto.ui.light;
 
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.io.JSONConverter;
 
import java.util.HashMap;
107,6 → 108,6
public void fromJSON(JSONObject json) {
super.fromJSON(json);
this.openNewFrame = JSONConverter.getParameterFromJSON(json, OPEN_NEW_FRAME_KEY, Boolean.class, false);
this.urlParameters = JSONConverter.getParameterFromJSON(json, URL_PARAMETER_KEY, Map.class);
this.urlParameters = CollectionUtils.castMap(JSONConverter.getParameterFromJSON(json, URL_PARAMETER_KEY, Map.class), String.class, String.class);
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIElement.java
18,13 → 18,16
 
import java.awt.Color;
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;
 
import net.minidev.json.JSONObject;
 
public class LightUIElement implements Transferable {
 
private static final String HORIZONTALLY_RESIZABLE = "horizontally-resizable";
private static final String VERTICALLY_SCROLLABLE = "vertically-scrollable";
private static final String VERTICALLY_RESIZABLE = "vertically-resizable";
private static final String HORIZONTALLY_SCROLLABLE = "horizontally-scrollable";
 
public enum GlyphIcon {
WARNING("#warning"), PLUS("#plus"), PENCIL("#pencil"), SEARCH("#search"), REMOVE("#remove"), STAR("#star"), STAR_EMPTY("#star-empty"), USER("#user"), LOCK("#lock"), UNLOCK(
"#unlock"), DOWNLOAD("#download"), UPLOAD("#upload");
60,9 → 63,11
public static final int TYPE_DROPDOWN_BUTTON = 12;
public static final int TYPE_FRAME = 13;
public static final int TYPE_IMAGE = 14;
public static final int TYPE_FILE = 15;
public static final int TYPE_FILE_UPLOAD_WITH_SELECTION = 15;
public static final int TYPE_PANEL_LINE = 16;
public static final int TYPE_TAB_ELEMENT = 17;
public static final int TYPE_SLIDER = 18;
public static final int TYPE_PICTURE_UPLOAD = 19;
public static final int TYPE_BUTTON = 20;
public static final int TYPE_BUTTON_WITH_CONTEXT = 21;
public static final int TYPE_BUTTON_CANCEL = 22;
71,6 → 76,12
public static final int TYPE_BUTTON_LINK = 25;
public static final int TYPE_RAW_HTML = 26;
public static final int TYPE_TEXT_AREA = 27;
public static final int TYPE_FILE_UPLOAD = 28;
public static final int TYPE_LIST_ROW = 29;
public static final int TYPE_BADGE = 30;
public static final int TYPE_AUTOCOMPLETE_COMBOBOX = 31;
public static final int TYPE_COLOR_PICKER = 32;
public static final int TYPE_HOUR_EDITOR = 33;
// valueType
public static final int VALUE_TYPE_STRING = 0;
public static final int VALUE_TYPE_INTEGER = 1;
148,6 → 159,9
private boolean required = false;
private boolean verticallyResizable = false;
private boolean visible = true;
private boolean notSaved = false;
private boolean verticallyScrollable = false;
private boolean horizontallyScrollable = false;
 
private String displayPrecision;// "(1,2)" means that 0.159 is shown as 0.16
private String icon;
167,8 → 181,6
 
private LightUIElement parent;
 
private Map<Integer, String> actions = new HashMap<Integer, String>();
 
public LightUIElement(final String id) {
this.id = id;
this.UUID = java.util.UUID.randomUUID().toString();
431,6 → 443,14
this.readOnly = readOnly;
}
 
public boolean isNotSaved() {
return this.notSaved;
}
 
public void setNotSaved(boolean notSaved) {
this.notSaved = notSaved;
}
 
public Color getBackgroundColor() {
return this.backgroundColor;
}
563,28 → 583,44
return this.verticallyResizable;
}
 
public final void setVerticalyResizable(boolean verticallyResizable) {
public final void setVerticallyResizable(boolean verticallyResizable) {
this.verticallyResizable = verticallyResizable;
}
 
public final boolean isHorizontalyResizable() {
public final boolean isHorizontallyResizable() {
return this.horizontallyResizable;
}
 
public final void setHorizontalyResizable(boolean horizontallyResizable) {
public final void setHorizontallyResizable(boolean horizontallyResizable) {
this.horizontallyResizable = horizontallyResizable;
}
 
public final boolean isHorizontallyScrollable() {
return this.horizontallyScrollable;
}
 
public final void setHorizontallyScrollable(boolean horizontallyScrollable) {
this.horizontallyScrollable = horizontallyScrollable;
}
 
public final boolean isVerticallyScrollable() {
return this.verticallyScrollable;
}
 
public final void setVerticallyScrollable(boolean verticallyScrollable) {
this.verticallyScrollable = verticallyScrollable;
}
 
public final LightUIElement getParent() {
return this.parent;
}
 
public final <T extends LightUIContainer> T getParent(final Class<T> classExpected) {
public final <T extends LightUIElement> T getParent(final Class<T> expectedClass) {
LightUIElement parent = this.parent;
while (parent != null && !classExpected.isAssignableFrom(parent.getClass())) {
while (parent != null && !expectedClass.isAssignableFrom(parent.getClass())) {
parent = parent.getParent();
}
return (T) parent;
return expectedClass.cast(parent);
}
 
public void setParent(LightUIElement parent) {
591,14 → 627,6
this.parent = parent;
}
 
public final Map<Integer, String> getActions() {
return this.actions;
}
 
public final void addAction(final Integer actionType, final String actionId) {
this.actions.put(actionType, actionId);
}
 
public void dump(PrintStream out, final int depth) {
final String type = this.getTypeAsString();
String valueType = "?";
689,13 → 717,13
 
}
 
protected void addSpacer(PrintStream out, int depth) {
protected final void addSpacer(PrintStream out, int depth) {
for (int i = 0; i < depth; i++) {
out.print(" ");
}
}
 
public String getTypeAsString() {
public final String getTypeAsString() {
String type = "?";
if (this.type == TYPE_CHECKBOX) {
type = "checkbox";
708,7 → 736,7
} else if (this.type == TYPE_TEXT_FIELD) {
type = "textfield";
} else if (this.type == TYPE_TABLE) {
type = "list";
type = "table";
} else if (this.type == TYPE_TABBED_UI) {
type = "tabs";
} else if (this.type == TYPE_TREE) {
723,8 → 751,8
type = "combo element";
} else if (this.type == TYPE_BUTTON_WITH_SELECTION_CONTEXT) {
type = "button with selection context";
} else if (this.type == TYPE_FILE) {
type = "file";
} else if (this.type == TYPE_FILE_UPLOAD_WITH_SELECTION) {
type = "file upload with selection";
} else if (this.type == TYPE_FRAME) {
type = "frame";
} else if (this.type == TYPE_DROPDOWN_BUTTON) {
735,6 → 763,12
type = "list";
} else if (this.type == TYPE_RAW_HTML) {
type = "raw html";
} else if (this.type == TYPE_SLIDER) {
type = "slider";
} else if (this.type == TYPE_PICTURE_UPLOAD) {
type = "picture upload";
} else if (this.type == TYPE_FILE_UPLOAD) {
type = "file upload";
}
 
return type;
784,15 → 818,19
 
this.enabled = element.enabled;
this.fillWidth = element.fillWidth;
this.fillHeight = element.fillHeight;
this.foldable = element.foldable;
this.folded = element.folded;
this.fontBold = element.fontBold;
this.fontItalic = element.fontItalic;
this.horizontallyResizable = element.horizontallyResizable;
this.horizontallyScrollable = element.horizontallyScrollable;
this.required = element.required;
this.readOnly = element.readOnly;
this.verticallyResizable = element.verticallyResizable;
this.verticallyScrollable = element.verticallyScrollable;
this.visible = element.visible;
this.notSaved = element.notSaved;
 
this.displayPrecision = element.displayPrecision;
this.icon = element.icon;
806,9 → 844,6
this.borderColor = element.borderColor;
this.cellBackgroundColor = element.cellBackgroundColor;
this.foreColor = element.foreColor;
 
this.actions = element.actions;
 
}
 
@Override
905,6 → 940,9
if (this.fillWidth) {
result.put("fill-width", true);
}
if (this.fillHeight) {
result.put("fill-height", true);
}
if (this.foldable) {
result.put("foldable", true);
}
918,8 → 956,11
result.put("italic", true);
}
if (this.horizontallyResizable) {
result.put("horizontally-resizable", true);
result.put(HORIZONTALLY_RESIZABLE, true);
}
if (this.horizontallyScrollable) {
result.put(HORIZONTALLY_SCROLLABLE, true);
}
if (this.required) {
result.put("required", true);
}
927,11 → 968,17
result.put("read-only", true);
}
if (this.verticallyResizable) {
result.put("vertically-resizable", true);
result.put(VERTICALLY_RESIZABLE, true);
}
if (this.verticallyScrollable) {
result.put(VERTICALLY_SCROLLABLE, true);
}
if (!this.visible) {
result.put("visible", false);
}
if (this.notSaved) {
result.put("not-saved", true);
}
 
if (this.displayPrecision != null) {
result.put("display-precision", this.displayPrecision);
970,10 → 1017,6
result.put("fore-color", JSONConverter.getJSON(this.foreColor));
}
 
if (!this.actions.isEmpty()) {
result.put("actions", this.actions);
}
 
return result;
}
 
1021,11 → 1064,14
this.fillWidth = JSONConverter.getParameterFromJSON(json, "fill-width", Boolean.class, false);
this.fontBold = JSONConverter.getParameterFromJSON(json, "bold", Boolean.class, false);
this.fontItalic = JSONConverter.getParameterFromJSON(json, "italic", Boolean.class, false);
this.horizontallyResizable = JSONConverter.getParameterFromJSON(json, "horizontally-resizable", Boolean.class, false);
this.verticallyResizable = JSONConverter.getParameterFromJSON(json, "vertically-resizable", Boolean.class, false);
this.horizontallyResizable = JSONConverter.getParameterFromJSON(json, HORIZONTALLY_RESIZABLE, Boolean.class, false);
this.horizontallyScrollable = JSONConverter.getParameterFromJSON(json, HORIZONTALLY_SCROLLABLE, Boolean.class, false);
this.verticallyResizable = JSONConverter.getParameterFromJSON(json, VERTICALLY_RESIZABLE, Boolean.class, false);
this.verticallyScrollable = JSONConverter.getParameterFromJSON(json, VERTICALLY_SCROLLABLE, Boolean.class, false);
this.required = JSONConverter.getParameterFromJSON(json, "required", Boolean.class, false);
this.readOnly = JSONConverter.getParameterFromJSON(json, "read-only", Boolean.class, false);
this.visible = JSONConverter.getParameterFromJSON(json, "visible", Boolean.class, true);
this.notSaved = JSONConverter.getParameterFromJSON(json, "not-saved", Boolean.class, false);
 
this.backgroundColor = JSONConverter.getParameterFromJSON(json, "background-color", Color.class);
this.borderColor = JSONConverter.getParameterFromJSON(json, "border-color", Color.class);
1032,4 → 1078,8
this.cellBackgroundColor = JSONConverter.getParameterFromJSON(json, "cell-background-color", Color.class);
this.foreColor = JSONConverter.getParameterFromJSON(json, "fore-color", Color.class);
}
 
public void destroy() {
this.setValue(null);
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIFrame.java
23,10 → 23,14
import net.minidev.json.JSONObject;
 
public class LightUIFrame extends LightUIContainer {
private static final String ACTIVE = "active";
private static final String TITLE_PANEL = "title-panel";
private static final String FOOTER_PANEL = "footer-panel";
private static final String CHILDREN_FRAME = "children-frame";
 
private Boolean active = false;
private String title;
private final LightUIPanel footerPanel = new LightUIPanel(this.getId() + ".footer.panel");
private LightUIPanel titlePanel = null;
private LightUIPanel footerPanel = new LightUIPanel(this.getId() + ".footer.panel");
 
private List<LightUIFrame> childrenFrame;
 
39,9 → 43,10
public LightUIFrame(final LightUIFrame frame) {
super(frame);
this.active = frame.active;
this.title = frame.title;
this.footerPanel.copy(frame.footerPanel);
this.titlePanel = frame.titlePanel;
this.childrenFrame = frame.childrenFrame;
 
this.setFooterPanel(frame.footerPanel);
}
 
/**
56,20 → 61,49
this.childrenFrame = new ArrayList<LightUIFrame>();
this.addChild(new LightUIPanel(this.getId() + ".main.panel"));
this.footerPanel.setParent(this);
this.footerPanel.setFillHeight(false);
this.footerPanel.setHeight(50);
 
this.createTitlePanel();
}
 
public String getTitle() {
return this.title;
public LightUIPanel createTitlePanel(final String title) {
this.createTitlePanel();
final LightUILabel titleLabel = new LightUILabel(this.titlePanel.getId() + ".label", title, true);
titleLabel.setVerticalAlignement(VALIGN_CENTER);
this.titlePanel.getLastLine().addChild(titleLabel);
return this.titlePanel;
}
 
public void setTitle(final String title) {
this.title = title;
public LightUIPanel createTitlePanel() {
this.titlePanel = new LightUIPanel(this.getId() + ".title.panel");
this.titlePanel.setFillHeight(false);
this.titlePanel.setHeight(50);
return this.titlePanel;
}
 
public LightUIPanel getTitlePanel() {
return this.titlePanel;
}
 
public LightUIPanel getFooterPanel() {
return this.footerPanel;
}
 
/**
* Set the footer panel of the frame, be careful the ID is automatically replace by
* <code>this.getId() + ".footer.panel"</code>.
*
* @param footerPanel - The new footer panel of this frame.
*/
public void setFooterPanel(final LightUIPanel footerPanel) {
footerPanel.setId(this.getId() + ".footer.panel");
this.footerPanel = footerPanel;
this.footerPanel.setFillHeight(false);
this.footerPanel.setParent(this);
this.footerPanel.setHeight(50);
}
 
public void updateFooterPanel(final LightUIPanel footerPanel) {
if (footerPanel != null) {
this.footerPanel.copy(footerPanel);
207,12 → 241,12
public JSONObject toJSON() {
final JSONObject json = super.toJSON();
if (this.active) {
json.put("active", true);
json.put(ACTIVE, true);
}
if (this.title != null) {
json.put("title", this.title);
if (this.titlePanel != null) {
json.put(TITLE_PANEL, this.titlePanel.toJSON());
}
json.put("footer-panel", this.footerPanel.toJSON());
json.put(FOOTER_PANEL, this.footerPanel.toJSON());
return json;
}
 
219,15 → 253,19
@Override
public void fromJSON(final JSONObject json) {
super.fromJSON(json);
this.active = (Boolean) JSONConverter.getParameterFromJSON(json, "active", Boolean.class, false);
this.title = (String) JSONConverter.getParameterFromJSON(json, "title", String.class);
this.active = JSONConverter.getParameterFromJSON(json, ACTIVE, Boolean.class, false);
 
final JSONObject jsonFooterPanel = (JSONObject) JSONConverter.getParameterFromJSON(json, "footer-panel", JSONObject.class, null);
final JSONObject jsonTitlePanel = JSONConverter.getParameterFromJSON(json, TITLE_PANEL, JSONObject.class);
if (jsonTitlePanel != null) {
this.titlePanel.fromJSON(jsonTitlePanel);
}
 
final JSONObject jsonFooterPanel = (JSONObject) JSONConverter.getParameterFromJSON(json, FOOTER_PANEL, JSONObject.class, null);
if (jsonFooterPanel != null) {
this.footerPanel.fromJSON(jsonFooterPanel);
}
 
final JSONArray jsonChildrenFrame = (JSONArray) JSONConverter.getParameterFromJSON(json, "children-frame", JSONArray.class, null);
final JSONArray jsonChildrenFrame = (JSONArray) JSONConverter.getParameterFromJSON(json, CHILDREN_FRAME, JSONArray.class, null);
this.childrenFrame = new ArrayList<LightUIFrame>();
if (jsonChildrenFrame != null) {
for (final Object objJsonFrame : jsonChildrenFrame) {
/trunk/OpenConcerto/src/org/openconcerto/ui/light/RowSpec.java
19,6 → 19,7
import java.io.ObjectOutput;
import java.util.List;
 
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.io.JSONAble;
import org.openconcerto.utils.io.JSONConverter;
import net.minidev.json.JSONArray;
105,7 → 106,7
if (jsonColumnIds != null) {
try {
this.columnIds = new String[jsonColumnIds.size()];
this.columnIds = ((List<String>) (List<?>) jsonColumnIds).toArray(this.columnIds);
this.columnIds = CollectionUtils.castList((List<?>) jsonColumnIds, String.class).toArray(this.columnIds);
} catch (final Exception ex) {
throw new IllegalArgumentException("invalid value for 'possible-column-ids', List<String> expected");
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIPanel.java
24,11 → 24,10
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
 
public class LightUIPanel extends LightUIContainer implements Transferable, IUserControlContainer {
public class LightUIPanel extends LightUserControlContainer implements Transferable {
private static final long serialVersionUID = -3399395824294128572L;
 
private String title;
private boolean verticallyScrollable = false;
 
private Color titleColor;
private Color titleBackgroundColor;
49,6 → 48,7
this.setType(TYPE_PANEL);
this.setWeightX(1);
this.setFillWidth(true);
this.setFillHeight(true);
}
 
public final LightUILine getLastLine() {
70,14 → 70,6
this.title = title;
}
 
public boolean isVerticallyScrollable() {
return this.verticallyScrollable;
}
 
public void setVerticallyScrollable(final boolean verticallyScrollable) {
this.verticallyScrollable = verticallyScrollable;
}
 
public void setTitleColor(final Color c) {
this.titleColor = c;
}
130,7 → 122,6
}
final LightUIPanel panelElement = (LightUIPanel) element;
this.title = panelElement.title;
this.verticallyScrollable = panelElement.verticallyScrollable;
this.titleColor = panelElement.titleColor;
this.titleBackgroundColor = panelElement.titleBackgroundColor;
this.controlers.addAll(panelElement.controlers);
184,7 → 175,7
this.addSpacer(out, depth);
out.println("Title : " + this.title);
this.addSpacer(out, depth);
out.println("v-scroll : " + this.verticallyScrollable + " title-color: " + this.titleColor.toString() + " bg-title-color: " + this.titleBackgroundColor.toString());
out.println("v-scroll : " + this.isVerticallyScrollable() + " title-color: " + this.titleColor.toString() + " bg-title-color: " + this.titleBackgroundColor.toString());
super.dump(out, depth);
this.addSpacer(out, depth);
out.println("------------------------");
191,8 → 182,12
}
 
@Override
public void setValueFromContext(final Object value) {
public void _setValueFromContext(final Object value) {
final JSONObject jsonContext = (JSONObject) JSONConverter.getObjectFromJSON(value, JSONObject.class);
 
if (jsonContext == null) {
System.err.println("LightUIPanel.setValueFromContext() - json is null for this panel: " + this.getId());
} else {
final int childCount = this.getChildrenCount();
for (int i = 0; i < childCount; i++) {
final LightUILine line = this.getChild(i, LightUILine.class);
199,20 → 194,21
final int lineChildCount = line.getChildrenCount();
for (int j = 0; j < lineChildCount; j++) {
final LightUIElement lineChild = line.getChild(j);
if (lineChild instanceof IUserControlContainer) {
if (lineChild instanceof LightUserControlContainer) {
if (!jsonContext.containsKey(lineChild.getUUID())) {
throw new IllegalArgumentException("Impossible to find key " + lineChild.getUUID() + " in context, LightUIElement id: " + lineChild.getId());
System.err.println("LightUIPanel.setValueFromContext() - Impossible to find key " + lineChild.getUUID() + " in context, LightUIElement id: " + lineChild.getId());
}
((IUserControlContainer) lineChild).setValueFromContext(jsonContext.get(lineChild.getUUID()));
} else if (lineChild instanceof IUserControl) {
((LightUserControlContainer) lineChild).setValueFromContext(jsonContext.get(lineChild.getUUID()));
} else if (lineChild instanceof LightUserControl) {
if (!jsonContext.containsKey(lineChild.getUUID())) {
throw new IllegalArgumentException("Impossible to find key " + lineChild.getUUID() + " in context, LightUIElement id: " + lineChild.getId());
System.err.println("LightUIPanel.setValueFromContext() - Impossible to find key " + lineChild.getUUID() + " in context, LightUIElement id: " + lineChild.getId());
}
((IUserControl) lineChild).setValueFromContext(jsonContext.get(lineChild.getUUID()));
((LightUserControl) lineChild).setValueFromContext(jsonContext.get(lineChild.getUUID()));
}
}
}
}
}
 
@Override
public LightUIElement clone() {
231,9 → 227,6
if (this.titleBackgroundColor != null) {
result.put("title-bgcolor", JSONConverter.getJSON(this.titleBackgroundColor));
}
if (this.verticallyScrollable) {
result.put("vertically-scrollable", true);
}
if (!this.controlers.isEmpty()) {
result.put("controlers", JSONConverter.getJSON(this.controlers));
}
245,7 → 238,6
public void fromJSON(final JSONObject json) {
super.fromJSON(json);
this.title = (String) JSONConverter.getParameterFromJSON(json, "title", String.class, null);
this.verticallyScrollable = (Boolean) JSONConverter.getParameterFromJSON(json, "vertically-scrollable", Boolean.class, false);
this.titleColor = (Color) JSONConverter.getParameterFromJSON(json, "title-color", Color.class, null);
this.titleBackgroundColor = (Color) JSONConverter.getParameterFromJSON(json, "title-bgcolor", Color.class, null);
 
/trunk/OpenConcerto/src/org/openconcerto/ui/light/RowSelectionSpec.java
20,6 → 20,7
import java.util.ArrayList;
import java.util.List;
 
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.io.JSONAble;
import org.openconcerto.utils.io.JSONConverter;
import net.minidev.json.JSONArray;
28,7 → 29,7
public class RowSelectionSpec implements Externalizable, JSONAble {
private String tableId;
 
private List<Long> ids;
private List<Number> ids;
 
/**
* Define selected ids of a table. ids are ids from selected lines
41,31 → 42,31
this.fromJSON(json);
}
 
public RowSelectionSpec(String tableId, List<Long> ids) {
this.init(tableId, ids);
}
 
public RowSelectionSpec(String tableId) {
this.init(tableId, null);
this(tableId, new ArrayList<Number>());
}
 
private void init(final String tableId, final List<Long> ids) {
public RowSelectionSpec(final String tableId, final List<Number> selectedIds) {
this.tableId = tableId;
if (ids != null) {
this.ids = ids;
} else {
this.ids = new ArrayList<Long>();
this.ids = selectedIds;
}
}
 
public List<Long> getIds() {
public final List<Number> getIds() {
return this.ids;
}
 
public final void setIds(List<Number> ids) {
this.ids = ids;
}
 
public String getTableId() {
return this.tableId;
}
 
public boolean hasSelectedId(){
return this.getIds() != null && !this.getIds().isEmpty();
}
 
@Override
public String toString() {
final StringBuilder r = new StringBuilder("RowSelectionSpec: ").append(this.tableId).append(" : ");
95,7 → 96,7
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.tableId = in.readUTF();
this.ids = (List<Long>) in.readObject();
this.ids = CollectionUtils.castList((List<?>) in.readObject(), Number.class);
 
}
 
110,11 → 111,11
 
@Override
public void fromJSON(JSONObject json) {
this.tableId = (String) JSONConverter.getParameterFromJSON(json, "table-id", String.class);
final JSONArray jsonIds = (JSONArray) JSONConverter.getParameterFromJSON(json, "ids", JSONArray.class);
this.ids = new ArrayList<Long>();
this.tableId = JSONConverter.getParameterFromJSON(json, "table-id", String.class);
final JSONArray jsonIds = JSONConverter.getParameterFromJSON(json, "ids", JSONArray.class);
this.ids = new ArrayList<Number>();
for (final Object jsonId : jsonIds) {
this.ids.add((Long) JSONConverter.getObjectFromJSON(jsonId, Long.class));
this.ids.add(JSONConverter.getObjectFromJSON(jsonId, Number.class));
}
}
 
/trunk/OpenConcerto/src/org/openconcerto/ui/light/IntValueConvertor.java
25,11 → 25,15
}
@Override
public void fillComboWithValue(final LightUIComboBox combo) {
public void fillComboWithValue(final LightUIComboBox combo, final Integer selectedValue) {
for(final Entry<Integer, String> entry : this.values.entrySet()) {
final LightUIComboBoxElement comboElement = new LightUIComboBoxElement(entry.getKey());
comboElement.setValue1(entry.getValue());
combo.addValue(comboElement);
 
if (entry.getKey().equals(selectedValue)) {
combo.setSelectedValue(comboElement);
}
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIComboBox.java
22,24 → 22,19
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
 
public class LightUIComboBox extends LightUIElement implements IUserControl {
public static final int TYPE_COMBO_STANDARD = 1;
public static final int TYPE_COMBO_AUTOCOMPLETE = 2;
public static final int TYPE_COMBO_AUTOCOMPLETE_WITH_ACTION = 3;
public class LightUIComboBox extends LightUserControl {
private static final String HAS_NOT_SPECIFIED_LINE = "has-not-specified-line";
private static final String VALUES = "values";
private static final String SELECTED_VALUE = "selected-value";
private static final String ALREADY_FILLED = "already-filled";
 
public static final int COMBO_ACTION_SEARCH = 1;
public static final int COMBO_ACTION_ADD = 2;
public static final int COMBO_ACTION_MODIFY = 3;
private boolean alreadyFilled = false;
private boolean hasNotSpecifedLine = false;
 
private int comboType = TYPE_COMBO_STANDARD;
private boolean fillFromConvertor = false;
private LightUIComboBoxElement selectedValue = null;
 
private List<LightUIComboBoxElement> values = new ArrayList<LightUIComboBoxElement>();
 
private LightUIComboBoxElement selectedValue = null;
 
private List<Integer> comboActions = new ArrayList<Integer>();
 
// Init from json constructor
public LightUIComboBox(final JSONObject json) {
super(json);
55,14 → 50,6
this.setType(TYPE_COMBOBOX);
}
 
public int getComboType() {
return this.comboType;
}
 
public void setComboType(final int comboType) {
this.comboType = comboType;
}
 
public void addValue(final LightUIComboBoxElement values) {
this.values.add(values);
}
71,14 → 58,6
this.values.addAll(values);
}
 
public List<Integer> getComboActions() {
return this.comboActions;
}
 
public void addComboAction(final Integer comboAction) {
this.comboActions.add(comboAction);
}
 
public static LightUIComboBoxElement getDefaultValue() {
final String defaultLabelKey = "not.specified.label";
final String defaultLabel = TranslationManager.getInstance().getTranslationForItem(defaultLabelKey);
97,8 → 76,9
return this.values;
}
 
// if id=0 means this is the not specifed line
public boolean hasSelectedValue() {
return this.selectedValue != null && this.selectedValue.getId() != 0;
return this.selectedValue != null && ((this.hasNotSpecifedLine && this.selectedValue.getId() != 0) || !this.hasNotSpecifedLine);
}
 
public LightUIComboBoxElement getSelectedValue() {
114,14 → 94,35
this.values.clear();
}
 
public void setFillFromConvertor(final boolean fillFromConvertor) {
this.fillFromConvertor = fillFromConvertor;
public void setAlreadyFilled(final boolean alreadyFilled) {
this.alreadyFilled = alreadyFilled;
}
 
public boolean isFillFromConvertor() {
return this.fillFromConvertor;
public boolean isAlreadyFilled() {
return this.alreadyFilled;
}
 
public void setHasNotSpecifedLine(final boolean hasNotSpecifedLine) {
this.hasNotSpecifedLine = hasNotSpecifedLine;
}
 
public boolean hasNotSpecifedLine() {
return this.hasNotSpecifedLine;
}
 
public void setSelectedId(final Integer id) {
if (id == null) {
this.setSelectedValue(null);
} else {
for (final LightUIComboBoxElement value : this.values) {
if (value.getId() == id) {
this.setSelectedValue(value);
break;
}
}
}
}
 
@Override
protected void copy(LightUIElement element) {
super.copy(element);
131,11 → 132,9
}
 
final LightUIComboBox combo = (LightUIComboBox) element;
this.comboType = combo.comboType;
this.fillFromConvertor = combo.fillFromConvertor;
this.alreadyFilled = combo.alreadyFilled;
this.values = combo.values;
this.selectedValue = combo.selectedValue;
this.comboActions = combo.comboActions;
}
 
@Override
162,17 → 161,17
for (final LightUIComboBoxElement value : this.values) {
jsonValues.add(value.toJSON());
}
json.put("values", jsonValues);
json.put(VALUES, jsonValues);
}
 
if (this.comboType != TYPE_COMBO_STANDARD) {
json.put("combo-type", this.comboType);
if (this.alreadyFilled) {
json.put(ALREADY_FILLED, true);
}
if (this.fillFromConvertor) {
json.put("fill-from-convertor", true);
if (this.hasNotSpecifedLine) {
json.put(HAS_NOT_SPECIFIED_LINE, true);
}
if (this.selectedValue != null) {
json.put("selected-value", this.selectedValue.toJSON());
json.put(SELECTED_VALUE, this.selectedValue.toJSON());
}
 
return json;
182,27 → 181,25
public void fromJSON(final JSONObject json) {
super.fromJSON(json);
 
this.comboType = (Integer) JSONConverter.getParameterFromJSON(json, "combo-type", Integer.class, TYPE_COMBO_STANDARD);
this.fillFromConvertor = (Boolean) JSONConverter.getParameterFromJSON(json, "fill-from-convertor", Boolean.class, false);
final JSONObject jsonSelectedValue = (JSONObject) JSONConverter.getParameterFromJSON(json, "", JSONObject.class);
this.alreadyFilled = JSONConverter.getParameterFromJSON(json, ALREADY_FILLED, Boolean.class, false);
this.hasNotSpecifedLine = JSONConverter.getParameterFromJSON(json, HAS_NOT_SPECIFIED_LINE, Boolean.class, false);
 
final JSONObject jsonSelectedValue = JSONConverter.getParameterFromJSON(json, "", JSONObject.class);
if (jsonSelectedValue != null) {
this.selectedValue = new LightUIComboBoxElement(jsonSelectedValue);
}
 
final JSONArray jsonValues = (JSONArray) JSONConverter.getParameterFromJSON(json, "values", JSONArray.class);
final JSONArray jsonValues = JSONConverter.getParameterFromJSON(json, VALUES, JSONArray.class);
this.values = new ArrayList<LightUIComboBoxElement>();
if (jsonValues != null) {
for (final Object jsonValue : jsonValues) {
if (!(jsonValue instanceof JSONObject)) {
throw new IllegalArgumentException("value for 'values' is invalid");
this.values.add(new LightUIComboBoxElement(JSONConverter.getObjectFromJSON(jsonValue, JSONObject.class)));
}
this.values.add(new LightUIComboBoxElement((JSONObject) jsonValue));
}
}
}
 
@Override
public Object getValueFromContext() {
public Object getValueForContext() {
if (this.hasSelectedValue()) {
return this.getSelectedValue();
} else {
211,12 → 208,18
}
 
@Override
public void setValueFromContext(Object value) {
public void _setValueFromContext(Object value) {
if (value != null) {
final JSONObject jsonSelectedValue = (JSONObject) JSONConverter.getObjectFromJSON(value, JSONObject.class);
final JSONObject jsonSelectedValue = JSONConverter.getObjectFromJSON(value, JSONObject.class);
this.selectedValue = new LightUIComboBoxElement(jsonSelectedValue);
} else {
this.selectedValue = null;
}
}
 
@Override
public void destroy() {
super.destroy();
this.clearValues();
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/ComboValueConvertor.java
36,20 → 36,32
 
protected abstract void init();
 
public boolean hasNotSpecifiedLine() {
public final boolean hasNotSpecifiedLine() {
return this.hasNotSpecifedLine;
}
 
public void fillCombo(final LightUIComboBox combo) {
public final void fillCombo(final LightUIComboBox combo, final K selectedValue) {
if (this.hasNotSpecifedLine) {
combo.addValue(LightUIComboBox.getDefaultValue());
}
this.fillComboWithValue(combo);
combo.setFillFromConvertor(true);
this.fillComboWithValue(combo, selectedValue);
if (selectedValue == null) {
combo.setSelectedValue(null);
}
 
protected abstract void fillComboWithValue(final LightUIComboBox combo);
if(!combo.hasSelectedValue()){
if(combo.hasNotSpecifedLine()){
combo.setSelectedValue(LightUIComboBox.getDefaultValue());
} else if(!combo.getValues().isEmpty()) {
combo.setSelectedValue(combo.getValues().get(0));
}
}
 
combo.setAlreadyFilled(true);
}
 
protected abstract void fillComboWithValue(final LightUIComboBox combo, final K selectedValue);
 
@Override
public String convert(final K key) {
if (key == null) {
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIList.java
11,31 → 11,13
* When distributing the software, include this License Header Notice in each file.
*/
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
 
package org.openconcerto.ui.light;
 
import org.openconcerto.utils.io.JSONConverter;
import org.openconcerto.utils.ui.StringWithId;
 
import java.util.ArrayList;
 
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
 
public class LightUIList extends LightUIElement {
private ArrayList<StringWithId> values = new ArrayList<StringWithId>();
public class LightUIList extends LightUIContainer {
 
// Init from json constructor
public LightUIList(final JSONObject json) {
45,65 → 27,19
// Clone constructor
public LightUIList(final LightUIList listElement) {
super(listElement);
this.values = listElement.values;
}
 
public LightUIList(final String id, final ArrayList<StringWithId> values) {
public LightUIList(final String id) {
super(id);
this.setType(TYPE_LIST);
 
this.values = values;
}
 
public ArrayList<StringWithId> getValues() {
return (ArrayList<StringWithId>) this.values.clone();
}
public LightUIList(final String id, final ArrayList<LightUIListRow> rows) {
super(id);
this.setType(TYPE_LIST);
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIList(json);
for (final LightUIListRow row : rows) {
this.addChild(row);
}
};
}
 
@Override
public LightUIElement clone() {
return new LightUIList(this);
}
 
@Override
protected void copy(LightUIElement element) {
super.copy(element);
if (!(element instanceof LightUIList)) {
throw new InvalidClassException(this.getClassName(), element.getClassName(), element.getId());
}
}
 
@Override
public JSONObject toJSON() {
final JSONObject json = super.toJSON();
json.put("class", "LightUIList");
if (this.values != null && this.values.size() > 0) {
json.put("values", this.values);
}
return json;
}
 
@Override
public void fromJSON(final JSONObject json) {
super.fromJSON(json);
final JSONArray jsonValues = (JSONArray) JSONConverter.getParameterFromJSON(json, "values", JSONArray.class);
this.values = new ArrayList<StringWithId>();
if (jsonValues != null) {
for (final Object jsonValue : jsonValues) {
if (!(jsonValue instanceof JSONObject)) {
throw new IllegalArgumentException("values of list must be json of StringWithId");
}
this.values.add(new StringWithId((JSONObject) jsonValue));
}
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIColorPicker.java
New file
0,0 → 1,57
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.ui.light;
 
import java.awt.Color;
 
import net.minidev.json.JSONObject;
 
public class LightUIColorPicker extends LightUserControl {
 
public LightUIColorPicker(final String id) {
super(id);
this.setType(TYPE_COLOR_PICKER);
this.setBorderColor(Color.BLACK);
}
 
public LightUIColorPicker(final JSONObject json) {
super(json);
}
 
public LightUIColorPicker(final LightUIColorPicker element) {
super(element);
}
 
public void setSelectedColor(final Color color) {
this.setValue(String.valueOf(color.getRGB()));
}
 
public Color getSelectedColor() {
if (this.getValue() == null || this.getValue().isEmpty()) {
return null;
} else {
return Color.decode(this.getValue());
}
}
 
@Override
protected void _setValueFromContext(final Object value) {
this.setValue((String) value);
}
 
@Override
public Object getValueForContext() {
return this.getValue();
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIDate.java
21,7 → 21,7
 
import net.minidev.json.JSONObject;
 
public class LightUIDate extends LightUIElement implements IUserControl {
public class LightUIDate extends LightUserControl {
 
public LightUIDate(final JSONObject json) {
super(json);
60,12 → 60,12
}
 
@Override
public Object getValueFromContext() {
public Object getValueForContext() {
return this.getValueAsDate();
}
 
@Override
public void setValueFromContext(Object value) {
public void _setValueFromContext(Object value) {
final String strValue = (String) JSONConverter.getObjectFromJSON(value, String.class);
this.setValue(strValue);
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIPictureUpload.java
New file
0,0 → 1,53
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.ui.light;
 
import org.openconcerto.utils.io.JSONConverter;
 
import net.minidev.json.JSONObject;
 
public class LightUIPictureUpload extends LightUIFileUpload {
 
private static final String FILE_PATH = "file-path";
 
private String filePath = null;
 
public LightUIPictureUpload(final String id, final String sendFileUrl) {
super(id, sendFileUrl);
this.setType(TYPE_PICTURE_UPLOAD);
}
 
public String getFilePath() {
return this.filePath;
}
 
public void setFilePath(final String filePath) {
this.filePath = filePath;
}
 
@Override
public JSONObject toJSON() {
final JSONObject json = super.toJSON();
if (this.filePath != null) {
json.put(FILE_PATH, this.filePath);
}
return json;
}
 
@Override
public void fromJSON(JSONObject json) {
super.fromJSON(json);
this.filePath = JSONConverter.getParameterFromJSON(json, FILE_PATH, String.class);
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/Row.java
13,9 → 13,9
package org.openconcerto.ui.light;
 
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.io.JSONAble;
import org.openconcerto.utils.io.JSONConverter;
import org.openconcerto.utils.ui.StringWithId;
 
import java.io.Externalizable;
import java.io.IOException;
29,7 → 29,7
 
public class Row implements Externalizable, JSONAble {
 
private long id;
private Number id;
private String extendId;
private List<Object> values;
45,7 → 45,7
this.fromJSON(json);
}
 
public Row(long id, int valueCount) {
public Row(final Number id, int valueCount) {
this.id = id;
this.values = new ArrayList<Object>();
if (valueCount > 0) {
55,69 → 55,65
}
}
 
public Row(long id) {
public Row(final Number id) {
this.id = id;
}
public void setId(final long id) {
this.id = id;
}
 
public void setValues(List<Object> values) {
public final void setValues(List<Object> values) {
this.values = values;
}
 
public List<Object> getValues() {
public final List<Object> getValues() {
return this.values;
}
 
public long getId() {
public final Number getId() {
return this.id;
}
public String getExtendId() {
public final String getExtendId() {
return this.extendId;
}
public void setExtendId(final String extendId) {
public final void setExtendId(final String extendId) {
this.extendId = extendId;
}
public void addValue(Object v) {
public final void addValue(Object v) {
this.values.add(v);
}
 
public void setValue(int index, Object v) {
public final void setValue(int index, Object v) {
this.values.set(index, v);
}
public Boolean isFillWidth() {
public final Boolean isFillWidth() {
return this.fillWidth;
}
 
public void setFillWidth(final Boolean fillWidth) {
public final void setFillWidth(final Boolean fillWidth) {
this.fillWidth = fillWidth;
}
public Boolean isToggleable() {
public final Boolean isToggleable() {
return this.toggleable;
}
 
public void setToggleable(final Boolean toggleable) {
public final void setToggleable(final Boolean toggleable) {
this.toggleable = toggleable;
}
public Boolean isVisible() {
public final Boolean isVisible() {
return this.visible;
}
 
public void setVisible(final Boolean visible) {
public final void setVisible(final Boolean visible) {
this.visible = visible;
}
 
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeLong(this.id);
out.writeObject(this.id);
out.writeUTF(this.extendId);
out.writeObject(this.values);
out.writeBoolean(this.fillWidth);
127,9 → 123,9
 
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.id = in.readLong();
this.id = (Number) in.readObject();
this.extendId = in.readUTF();
this.values = (List<Object>) in.readObject();
this.values = CollectionUtils.castList((List<?>) in.readObject(), Object.class);
this.fillWidth = in.readBoolean();
this.toggleable = in.readBoolean();
this.visible = in.readBoolean();
162,11 → 158,11
 
@Override
public void fromJSON(final JSONObject json) {
this.id = (Long) JSONConverter.getParameterFromJSON(json, "id", Long.class);
this.extendId = (String) JSONConverter.getParameterFromJSON(json, "extend-id", String.class);
this.fillWidth = (Boolean) JSONConverter.getParameterFromJSON(json, "fill-width", Boolean.class, false);
this.toggleable = (Boolean) JSONConverter.getParameterFromJSON(json, "toggleable", Boolean.class, false);
this.visible = (Boolean) JSONConverter.getParameterFromJSON(json, "visible", Boolean.class, true);
this.id = JSONConverter.getParameterFromJSON(json, "id", Number.class);
this.extendId = JSONConverter.getParameterFromJSON(json, "extend-id", String.class);
this.fillWidth = JSONConverter.getParameterFromJSON(json, "fill-width", Boolean.class, false);
this.toggleable = JSONConverter.getParameterFromJSON(json, "toggleable", Boolean.class, false);
this.visible = JSONConverter.getParameterFromJSON(json, "visible", Boolean.class, true);
 
final JSONArray jsonValues = (JSONArray) JSONConverter.getParameterFromJSON(json, "values", JSONArray.class);
if (jsonValues != null) {
176,18 → 172,8
Object objValue = jsonValues.get(i);
if (objValue instanceof JSONObject) {
final JSONObject jsonValue = (JSONObject) objValue;
final String valueClassName = (String) JSONConverter.getParameterFromJSON(jsonValue, "class", String.class);
if (valueClassName == null){
throw new IllegalArgumentException("null value store in ghost");
}
if(valueClassName.equals(StringWithId.class.getSimpleName())) {
objValue = new StringWithId(jsonValue);
} else if (valueClassName.equals(LightUIElement.class.getSimpleName())) {
objValue = JSONToLightUIConvertorManager.getInstance().createUIElementFromJSON(jsonValue);
} else {
throw new IllegalArgumentException("invalid value for 'values', StringWithId or LightUIElement expected");
}
} else {
if(objValue instanceof String) {
objValue = JSONConverter.getObjectFromJSON(objValue, String.class);
} else if(objValue instanceof Integer) {
207,6 → 193,6
@Override
public String toString() {
return "Row id: " + this.id + " values: " + this.values;
return "Row index: " + this.id + " values: " + this.values;
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIComboBoxElement.java
15,6 → 15,7
 
import org.openconcerto.utils.io.JSONConverter;
import org.openconcerto.utils.io.Transferable;
 
import net.minidev.json.JSONObject;
 
public class LightUIComboBoxElement implements Transferable {
36,6 → 37,24
this.id = id;
}
 
public LightUIComboBoxElement(final int id, final String value1) {
this.id = id;
this.value1 = value1;
}
public LightUIComboBoxElement(final int id, final String value1, final String value2) {
this.id = id;
this.value1 = value1;
this.value2 = value2;
}
 
public LightUIComboBoxElement(final LightUIComboBoxElement comboElement) {
this.id = comboElement.id;
this.value1 = comboElement.value1;
this.value2 = comboElement.value2;
this.icon = comboElement.icon;
}
 
public int getId() {
return this.id;
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/ColumnSpec.java
15,7 → 15,6
 
import org.openconcerto.utils.io.JSONConverter;
import org.openconcerto.utils.io.Transferable;
import org.openconcerto.utils.ui.StringWithId;
 
import java.io.Externalizable;
import java.io.IOException;
27,17 → 26,33
import net.minidev.json.JSONObject;
 
public class ColumnSpec implements Externalizable, Transferable {
private static final String DEFAULT_VALUE = "default-value";
private static final String EDITORS = "editors";
private static final String VALUE_CLASS = "value-class";
private static final String ID = "id";
private static final String COLUMN_NAME = "column-name";
private static final String WIDTH = "width";
private static final String MAX_WIDTH = "max-width";
private static final String MIN_WIDTH = "min-width";
private static final String EDITABLE = "editable";
private static final String HORIZONTAL_ALIGNMENT = "h-align";
// Must stay immutable
private String id;
private String columnName;
 
private String id;
private Class<?> valueClass;
private String columnName;
 
// Default value (to add a new line)
private Object defaultValue;
private int width;
private int maxWidth;
private int minWidth;
 
private Integer horizontalAlignment = null;
 
private double width;
private double maxWidth;
private double minWidth;
 
private boolean editable;
 
private LightUIElement editors;
 
public ColumnSpec() {
48,12 → 63,12
this.fromJSON(json);
}
 
public ColumnSpec(final String id, final Class<?> valueClass, final String columnName, final Object defaultValue, final int width, final boolean editable, final LightUIElement editors) {
public ColumnSpec(final String id, final Class<?> valueClass, final String columnName, final Object defaultValue, final double width, final boolean editable, final LightUIElement editors) {
this.init(id, valueClass, columnName, defaultValue, editable, editors);
this.width = width;
 
final int minWidth = width - 200;
final int maxWidth = width + 200;
final double minWidth = width - 200;
final double maxWidth = width + 200;
 
this.minWidth = (minWidth < 10) ? 10 : minWidth;
this.maxWidth = maxWidth;
73,7 → 88,7
this.editors = editors;
}
 
public void setPrefs(final int width, final int maxWidth, final int minWidth) {
public void setPrefs(final double width, final double maxWidth, final double minWidth) {
this.width = width;
this.maxWidth = maxWidth;
this.minWidth = minWidth;
95,6 → 110,14
this.valueClass = valueClass;
}
 
public int getHorizontalAlignment() {
return this.horizontalAlignment;
}
 
public void setHorizontalAlignment(final int horizontalAlignment) {
this.horizontalAlignment = horizontalAlignment;
}
 
public String getColumnName() {
return this.columnName;
}
111,27 → 134,27
this.defaultValue = defaultValue;
}
 
public int getMaxWidth() {
public double getMaxWidth() {
return this.maxWidth;
}
 
public void setMaxWidth(final int maxWidth) {
public void setMaxWidth(final double maxWidth) {
this.maxWidth = maxWidth;
}
 
public int getMinWidth() {
public double getMinWidth() {
return this.minWidth;
}
 
public void setMinWidth(final int minWidth) {
public void setMinWidth(final double minWidth) {
this.minWidth = minWidth;
}
 
public int getWidth() {
public double getWidth() {
return this.width;
}
 
public void setWidth(int width) {
public void setWidth(double width) {
this.width = width;
}
 
160,10 → 183,10
 
public Element createXmlColumnPref() {
final Element columnElement = new Element("column");
columnElement.setAttribute("id", this.getId());
columnElement.setAttribute("max-width", String.valueOf(this.getMaxWidth()));
columnElement.setAttribute("min-width", String.valueOf(this.getMinWidth()));
columnElement.setAttribute("width", String.valueOf(this.getWidth()));
columnElement.setAttribute(ID, this.getId());
columnElement.setAttribute(MAX_WIDTH, String.valueOf(this.getMaxWidth()));
columnElement.setAttribute(MIN_WIDTH, String.valueOf(this.getMinWidth()));
columnElement.setAttribute(WIDTH, String.valueOf(this.getWidth()));
return columnElement;
}
 
171,13 → 194,14
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(this.id);
out.writeUTF(this.columnName);
out.writeInt(this.width);
out.writeInt(this.maxWidth);
out.writeInt(this.minWidth);
out.writeDouble(this.width);
out.writeDouble(this.maxWidth);
out.writeDouble(this.minWidth);
out.writeObject(this.defaultValue);
out.writeBoolean(this.editable);
out.writeObject(this.editors);
out.writeObject(this.valueClass);
out.writeInt(this.horizontalAlignment);
}
 
@Override
184,13 → 208,14
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.id = in.readUTF();
this.columnName = in.readUTF();
this.width = in.readInt();
this.maxWidth = in.readInt();
this.minWidth = in.readInt();
this.width = in.readDouble();
this.maxWidth = in.readDouble();
this.minWidth = in.readDouble();
this.defaultValue = in.readObject();
this.editable = in.readBoolean();
this.editors = (LightUIElement) in.readObject();
this.valueClass = (Class<?>) in.readObject();
this.horizontalAlignment = in.readInt();
}
 
@Override
197,45 → 222,60
public JSONObject toJSON() {
final JSONObject result = new JSONObject();
result.put("class", "ColumnSpec");
result.put("id", this.id);
result.put("column-name", this.columnName);
result.put("width", this.width);
result.put("max-width", this.maxWidth);
result.put("min-width", this.minWidth);
result.put(ID, this.id);
result.put(COLUMN_NAME, this.columnName);
result.put(WIDTH, this.width);
result.put(MAX_WIDTH, this.maxWidth);
result.put(MIN_WIDTH, this.minWidth);
if (this.defaultValue != null) {
result.put("default-value", JSONConverter.getJSON(this.defaultValue));
result.put(DEFAULT_VALUE, JSONConverter.getJSON(this.defaultValue));
}
if (this.editable) {
result.put("editable", true);
result.put(EDITABLE, true);
}
if (this.editors != null) {
result.put("editors", JSONConverter.getJSON(this.editors));
result.put(EDITORS, JSONConverter.getJSON(this.editors));
}
result.put("value-class", JSONConverter.getJSON(this.valueClass));
result.put(VALUE_CLASS, JSONConverter.getJSON(this.valueClass));
 
if (this.horizontalAlignment != null) {
result.put(HORIZONTAL_ALIGNMENT, this.horizontalAlignment);
}
return result;
}
 
@Override
public void fromJSON(final JSONObject json) {
this.id = (String) JSONConverter.getParameterFromJSON(json, "id", String.class);
this.columnName = (String) JSONConverter.getParameterFromJSON(json, "column-name", String.class);
this.width = (Integer) JSONConverter.getParameterFromJSON(json, "width", Integer.class);
this.maxWidth = (Integer) JSONConverter.getParameterFromJSON(json, "max-width", Integer.class);
this.minWidth = (Integer) JSONConverter.getParameterFromJSON(json, "min-width", Integer.class);
this.editable = (Boolean) JSONConverter.getParameterFromJSON(json, "editable", Boolean.class, Boolean.FALSE);
this.id = JSONConverter.getParameterFromJSON(json, ID, String.class);
this.columnName = JSONConverter.getParameterFromJSON(json, COLUMN_NAME, String.class);
 
final JSONObject jsonDefaultValue = (JSONObject) JSONConverter.getParameterFromJSON(json, "default-value", JSONObject.class);
if (jsonDefaultValue != null) {
final String defaultValueClassName = (String) JSONConverter.getParameterFromJSON(jsonDefaultValue, "class", String.class);
if (defaultValueClassName.equals(StringWithId.class.getSimpleName())) {
this.defaultValue = new StringWithId(jsonDefaultValue);
/** JavaScript convert Double to Long, this will fix that. **/
final Number numWidth = JSONConverter.getParameterFromJSON(json, WIDTH, Number.class);
final Number numMaxWidth = JSONConverter.getParameterFromJSON(json, MAX_WIDTH, Number.class);
final Number numMinWidth = JSONConverter.getParameterFromJSON(json, MIN_WIDTH, Number.class);
if (numWidth != null) {
this.width = numWidth.doubleValue();
}
if (numMaxWidth != null) {
this.maxWidth = numMaxWidth.doubleValue();
}
final JSONObject jsonEditors = (JSONObject) JSONConverter.getParameterFromJSON(json, "editors", JSONObject.class);
if (numMinWidth != null) {
this.minWidth = numMinWidth.doubleValue();
}
/************************************************************/
 
this.editable = JSONConverter.getParameterFromJSON(json, EDITABLE, Boolean.class, Boolean.FALSE);
this.horizontalAlignment = JSONConverter.getParameterFromJSON(json, HORIZONTAL_ALIGNMENT, Integer.class);
 
final JSONObject jsonDefaultValue = JSONConverter.getParameterFromJSON(json, DEFAULT_VALUE, JSONObject.class);
// TODO: implement default value
 
final JSONObject jsonEditors = JSONConverter.getParameterFromJSON(json, EDITORS, JSONObject.class);
if (jsonEditors != null) {
this.editors = JSONToLightUIConvertorManager.getInstance().createUIElementFromJSON(jsonEditors);
}
final String sValueClass = (String) JSONConverter.getParameterFromJSON(json, "value-class", String.class);
 
final String sValueClass = JSONConverter.getParameterFromJSON(json, VALUE_CLASS, String.class);
if (sValueClass != null) {
try {
this.valueClass = Class.forName(sValueClass);
/trunk/OpenConcerto/src/org/openconcerto/ui/light/TreeSpec.java
23,7 → 23,7
}
 
public String getId() {
return id;
return this.id;
}
 
public void setId(String id) {
31,7 → 31,7
}
 
public List<TreeItem> getChildren() {
return children;
return this.children;
}
 
public void setChildren(List<TreeItem> children) {
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIFileUpload.java
New file
0,0 → 1,90
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.ui.light;
 
import org.openconcerto.utils.io.JSONConverter;
 
import net.minidev.json.JSONObject;
 
public class LightUIFileUpload extends LightUserControl {
private static final String SEND_FILE_URL_JSON_KEY = "send-file-url";
 
private String sendFileUrl;
 
public LightUIFileUpload(final JSONObject json) {
super(json);
}
 
public LightUIFileUpload(final LightUIFileUpload file) {
super(file);
}
 
public LightUIFileUpload(final String id, final String sendFileUrl) {
super(id);
this.setType(LightUIElement.TYPE_FILE_UPLOAD);
 
this.sendFileUrl = sendFileUrl;
}
 
@Override
public JSONToLightUIConvertor getConvertor() {
return new JSONToLightUIConvertor() {
@Override
public LightUIElement convert(final JSONObject json) {
return new LightUIFileUpload(json);
}
};
}
 
@Override
public LightUIElement clone() {
return new LightUIFileUpload(this);
}
 
@Override
protected void copy(LightUIElement element) {
super.copy(element);
if (!(element instanceof LightUIFileUpload)) {
throw new InvalidClassException(this.getClassName(), element.getClassName(), element.getId());
}
 
final LightUIFileUpload files = (LightUIFileUpload) element;
this.sendFileUrl = files.sendFileUrl;
 
}
 
@Override
public void fromJSON(final JSONObject json) {
super.fromJSON(json);
 
this.sendFileUrl = JSONConverter.getParameterFromJSON(json, SEND_FILE_URL_JSON_KEY, String.class);
}
 
@Override
public JSONObject toJSON() {
final JSONObject json = super.toJSON();
json.put(SEND_FILE_URL_JSON_KEY, this.sendFileUrl);
return json;
}
 
@Override
public void _setValueFromContext(final Object value) {
this.setValue((String) value);
}
 
@Override
public Object getValueForContext() {
return this.getValue();
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUserControlContainer.java
New file
0,0 → 1,44
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.ui.light;
 
import net.minidev.json.JSONObject;
 
public abstract class LightUserControlContainer extends LightUIContainer {
public LightUserControlContainer(final String id) {
super(id);
}
 
public LightUserControlContainer(final JSONObject json) {
super(json);
}
 
public LightUserControlContainer(final LightUserControlContainer userControlContainer) {
super(userControlContainer);
}
 
public void setValueFromContext(final Object value) {
if (this.isEnabled()) {
this._setValueFromContext(value);
}
}
 
/**
* Set value for all elements send by client with buttons TYPE_BUTTON_WITH_SELECTION_CONTEXT and
* TYPE_BUTTON_WITH_CONTEXT
*
* @param value to set for element
*/
protected abstract void _setValueFromContext(final Object value);
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUITable.java
13,6 → 13,7
package org.openconcerto.ui.light;
 
import org.openconcerto.utils.NumberUtils;
import org.openconcerto.utils.io.JSONConverter;
 
import java.awt.event.ActionEvent;
20,15 → 21,29
import java.util.ArrayList;
import java.util.List;
 
import javax.xml.parsers.ParserConfigurationException;
 
import org.jdom2.Document;
import org.jdom2.Element;
 
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
 
public class LightUITable extends LightUIElement implements IUserControlContainer {
public class LightUITable extends LightUserControlContainer {
 
public static final int DEFAULT_LINE_HEIGHT = 40;
 
private static final String LINE_PER_ROW = "line-per-row";
private static final String TABLE_SPEC = "table-spec";
private static final String ALLOW_SELECTION = "allow-selection";
private static final String ALLOW_MULTI_SELECTION = "allow-multi-selection";
private static final String DYNAMIC_LOAD = "dynamic-load";
private static final String AUTO_SELECT_FIRST_LINE = "auto-select-first-line";
private Boolean dynamicLoad = false;
private Boolean verticallyScrollable = false;
private Boolean allowSelection = false;
private Boolean allowMultiSelection = false;
private Boolean autoSelectFirstLine = true;
private TableSpec tableSpec = null;
private String elementCode = null;
 
private List<ActionListener> selectionListeners = new ArrayList<ActionListener>();
 
35,6 → 50,8
// Nombre de ligne à afficher par Row
private int linePerRow = 1;
 
private int lineHeight = DEFAULT_LINE_HEIGHT;
 
// Init from json constructor
public LightUITable(final JSONObject json) {
super(json);
43,9 → 60,7
// Clone constructor
public LightUITable(final LightUITable tableElement) {
super(tableElement);
this.verticallyScrollable = tableElement.verticallyScrollable;
this.tableSpec = tableElement.tableSpec;
this.elementCode = tableElement.elementCode;
this.allowSelection = tableElement.allowSelection;
}
 
55,82 → 70,187
 
this.setWeightX(1);
this.setFillWidth(true);
}
 
public int getIndexfromRowID(int rowID) {
final TableContent content = this.getTableSpec().getContent();
final RowSelectionSpec selection = new RowSelectionSpec(this.getId());
final ColumnsSpec columnsSpec = new ColumnsSpec(this.getId(), new ArrayList<ColumnSpec>(), new ArrayList<String>(), new ArrayList<String>());
final TableSpec tableSpec = new TableSpec(this.getId(), selection, columnsSpec);
tableSpec.setContent(new TableContent(this.getId()));
 
for (int i = 0; i < content.getRows().size(); i++) {
Row r = content.getRows().get(i);
if (r.getId() == rowID) {
return i;
this.setTableSpec(tableSpec);
}
}
return -1;
}
 
@Override
public void setId(final String id) {
super.setId(id);
 
if (this.tableSpec != null) {
this.tableSpec.setId(id);
 
if (this.tableSpec.getSelection() != null) {
this.tableSpec.getSelection().setTableId(id);
}
 
public String getElementCode() {
return this.elementCode;
if (this.tableSpec.getContent() != null) {
this.tableSpec.getContent().setTableId(id);
}
 
public void setElementCode(final String elementCode) {
this.elementCode = elementCode;
}
}
 
public void setLinePerRow(int linePerRow) {
public final void setLinePerRow(int linePerRow) {
this.linePerRow = linePerRow;
}
 
public int getLinePerRow() {
public final int getLinePerRow() {
return this.linePerRow;
}
 
public TableSpec getTableSpec() {
public final void setLineHeight(int lineHeight) {
this.lineHeight = lineHeight;
}
 
public final int getLineHeight() {
return this.lineHeight;
}
 
public final TableSpec getTableSpec() {
return this.tableSpec;
}
 
public void setTableSpec(final TableSpec tableSpec) {
public final void setTableSpec(final TableSpec tableSpec) {
this.tableSpec = tableSpec;
}
 
public Boolean isAllowSelection() {
public final Boolean isAllowSelection() {
return this.allowSelection;
}
 
public void setAllowSelection(final boolean allowSelection) {
public final void setAllowSelection(final boolean allowSelection) {
this.allowSelection = allowSelection;
}
 
public Boolean isDynamicLoad() {
public final Boolean isAllowMultiSelection() {
return this.allowMultiSelection;
}
 
public final void setAllowMultiSelection(final boolean allowMultiSelection) {
this.allowMultiSelection = allowMultiSelection;
}
 
public final Boolean isDynamicLoad() {
return this.dynamicLoad;
}
 
public void setDynamicLoad(final boolean dynamicLoad) {
public final void setDynamicLoad(final boolean dynamicLoad) {
this.dynamicLoad = dynamicLoad;
}
 
public Boolean isVerticallyScrollable() {
return this.verticallyScrollable;
public final Boolean isAutoSelectFirstLine() {
return this.autoSelectFirstLine;
}
 
public void setVerticallyScrollable(final Boolean verticallyScrollable) {
this.verticallyScrollable = verticallyScrollable;
public final void setAutoSelectFirstLine(final boolean autoSelectFirstLine) {
this.autoSelectFirstLine = autoSelectFirstLine;
}
 
public boolean replaceChild(final LightUIElement pChild) {
final List<Row> tableRows = this.getTableSpec().getContent().getRows();
final int tableRowsCount = tableRows.size();
public final Row removeRow(final int index) {
return this.tableSpec.getContent().removeRow(index);
}
 
public final boolean removeRow(final Row row) {
final TableContent content = this.getTableSpec().getContent();
return content.removeRow(row);
}
 
public final boolean hasRow() {
return (this.tableSpec != null && this.tableSpec.getContent() != null && this.tableSpec.getContent().getRowsCount() > 0);
}
 
public final Row getRow(final int index) {
return this.getTableSpec().getContent().getRow(index);
}
 
public Row getRowById(final Number rowId) {
final int size = this.getTableSpec().getContent().getRowsCount();
for (int i = 0; i < size; i++) {
final Row row = this.getRow(i);
if (NumberUtils.areNumericallyEqual(row.getId(), rowId)) {
return row;
} else {
System.err.println("LightUITable.getSelectedRows() - Null selectedRow");
}
}
return null;
}
 
public final Row setRow(final int index, final Row row) {
return this.getTableSpec().getContent().setRow(index, row);
}
 
public final boolean addRow(final Row row) {
return this.getTableSpec().getContent().addRow(row);
}
 
public final int getRowsCount() {
return this.getTableSpec().getContent().getRowsCount();
}
 
public final void clearRows() {
this.getTableSpec().getContent().clearRows();
}
 
/**
* Get Ids of SQLRowAccessor store in selected rows
*
* @return The list of selected DB Ids
*/
public final List<Number> getSelectedIds() {
return this.getTableSpec().getSelection().getIds();
}
 
public final Number getFirstSelectedId() {
final List<Number> selectedIds = this.getTableSpec().getSelection().getIds();
if (selectedIds.isEmpty()) {
return null;
} else {
return selectedIds.get(0);
}
}
 
public final void setSelectedIds(final List<Number> selectedIds, final boolean fire) {
this.getTableSpec().getSelection().setIds(selectedIds);
if (fire) {
this.fireSelectionChange();
}
}
 
public final void clearSelection(final boolean fire) {
this.getTableSpec().getSelection().getIds().clear();
if (fire) {
this.fireSelectionChange();
}
}
 
public final List<Row> getSelectedRows() {
final List<Row> selectedRows = new ArrayList<Row>();
 
if (this.getTableSpec().getSelection() != null) {
final List<Number> selectedIds = this.getSelectedIds();
for (final Number selectedId : selectedIds) {
final Row selectedRow = this.getRowById(selectedId);
if (selectedRow != null) {
selectedRows.add(selectedRow);
}
}
}
 
return selectedRows;
}
 
public final boolean replaceChild(final LightUIElement pChild) {
pChild.setReadOnly(this.isReadOnly());
 
for (int i = 0; i < tableRowsCount; i++) {
final Row tableRow = tableRows.get(i);
for (int i = 0; i < this.getRowsCount(); i++) {
final Row tableRow = this.getTableSpec().getContent().getRow(i);
final List<Object> tableRowValues = tableRow.getValues();
final int tableRowValuesCount = tableRowValues.size();
 
160,17 → 280,15
return false;
}
 
public LightUIElement findElement(final String searchParam, final boolean byUUID) {
public final LightUIElement findElement(final String searchParam, final boolean byUUID) {
return this.findElement(searchParam, byUUID, LightUIElement.class);
}
 
public <T extends LightUIElement> T findElement(final String searchParam, final boolean byUUID, final Class<T> objectClass) {
if (this.tableSpec != null) {
final TableContent content = this.tableSpec.getContent();
if (content != null) {
final List<Row> listRows = content.getRows();
if (listRows != null) {
for (final Row row : listRows) {
public final <T extends LightUIElement> T findElement(final String searchParam, final boolean byUUID, final Class<T> objectClass) {
if (this.hasRow()) {
 
for (int i = 0; i < this.getRowsCount(); i++) {
final Row row = this.getRow(i);
final List<Object> rowValues = row.getValues();
for (final Object value : rowValues) {
if (value instanceof LightUIContainer) {
184,19 → 302,19
if (byUUID) {
if (element.getUUID().equals(searchParam)) {
if (objectClass.isAssignableFrom(element.getClass())) {
return (T) element;
return objectClass.cast(element);
} else {
throw new IllegalArgumentException("Element found at is not an instance of " + objectClass.getName() + ", element class: " + element.getClass().getName()
+ " element ID: " + element.getId());
throw new IllegalArgumentException(
"Element found at is not an instance of " + objectClass.getName() + ", element class: " + element.getClass().getName() + " element ID: " + element.getId());
}
}
} else {
if (element.getId().equals(searchParam)) {
if (objectClass.isAssignableFrom(element.getClass())) {
return (T) element;
return objectClass.cast(element);
} else {
throw new IllegalArgumentException("Element found at is not an instance of " + objectClass.getName() + ", element class: " + element.getClass().getName()
+ " element ID: " + element.getId());
throw new IllegalArgumentException(
"Element found at is not an instance of " + objectClass.getName() + ", element class: " + element.getClass().getName() + " element ID: " + element.getId());
}
}
}
213,37 → 331,128
} else {
System.out.println("LightUITable.getElementById() - No rows for table: " + this.getId());
}
} else {
System.out.println("LightUITable.getElementById() - Null TableContent for table: " + this.getId());
return null;
}
 
public <T extends LightUIElement> List<T> findChildren(final Class<T> expectedClass, final boolean recursively) {
final List<T> result = new ArrayList<T>();
 
if (this.hasRow()) {
final int size = this.getRowsCount();
for (int i = 0; i < size; i++) {
final Row row = this.getRow(i);
final List<Object> rowValues = row.getValues();
for (final Object value : rowValues) {
if (recursively) {
if (value instanceof LightUIContainer) {
result.addAll(((LightUIContainer) value).findChildren(expectedClass, recursively));
} else if (value instanceof LightUITable) {
result.addAll(((LightUITable) value).findChildren(expectedClass, recursively));
}
}
if (expectedClass.isAssignableFrom(value.getClass())) {
result.add(expectedClass.cast(value));
}
}
}
} else {
System.out.println("LightUITable.getElementById() - Null TableSpec for table: " + this.getId());
System.out.println("LightUITable.getElementById() - No rows for table: " + this.getId());
}
return null;
 
return result;
}
 
public void addSelectionListener(final ActionListener selectionListener) {
public final void addSelectionListener(final ActionListener selectionListener) {
this.selectionListeners.add(selectionListener);
}
 
public void removeSelectionListeners() {
public final void removeSelectionListeners() {
this.selectionListeners.clear();
}
 
public void fireSelectionChange() {
public final void fireSelectionChange() {
for (final ActionListener listener : this.selectionListeners) {
listener.actionPerformed(new ActionEvent(this, 1, "selection"));
}
}
 
// TODO: garder l'ordre des colonnes invisibles
/**
* Create columns preferences with the current ColumnsSpec
*
* @return XML document with columns preferences
*/
public final Document createXmlPreferences(final Document userPrefs, final ColumnsSpec columnsSpec) throws ParserConfigurationException {
 
final Element rootElement = new Element("list");
final Document xmlConf = new Document();
 
final int columnSpecCount = columnsSpec.getColumnCount();
final List<String> visibleIds = new ArrayList<String>();
for (int i = 0; i < columnSpecCount; i++) {
final ColumnSpec columnSpec = columnsSpec.getColumn(i);
final Element xmlColumn = this.createXmlColumn(columnSpec.getId(), columnSpec.getMaxWidth(), columnSpec.getMinWidth(), columnSpec.getWidth());
rootElement.addContent(xmlColumn);
visibleIds.add(columnSpec.getId());
}
 
final Element rootUserPrefs = userPrefs.getRootElement();
final List<Element> xmlColumns = rootUserPrefs.getChildren();
final int columnsSize = xmlColumns.size();
for (int i = 0; i < columnsSize; i++) {
final Element xmlColumn = xmlColumns.get(i);
final String columnId = xmlColumn.getAttribute("id").getValue();
if (!visibleIds.contains(columnId)) {
final int maxWidth = Integer.parseInt(xmlColumn.getAttribute("max-width").getValue());
final int minWidth = Integer.parseInt(xmlColumn.getAttribute("min-width").getValue());
final int width = Integer.parseInt(xmlColumn.getAttribute("width").getValue());
final Element newXmlColumn = this.createXmlColumn(columnId, maxWidth, minWidth, width);
rootElement.addContent(newXmlColumn);
}
}
xmlConf.setRootElement(rootElement);
return xmlConf;
}
 
/**
* Create default columns preferences from the SQLTableModelLinesSourceOnline
*
* @return XML document with columns preferences
*/
public Document createDefaultXmlPreferences() {
final Element rootElement = new Element("list");
 
if (this.getTableSpec() != null && this.getTableSpec().getColumns() != null) {
final int sqlColumnsCount = this.getTableSpec().getColumns().getColumnCount();
for (int i = 0; i < sqlColumnsCount; i++) {
final ColumnSpec column = this.getTableSpec().getColumns().getColumn(i);
final String columnId = column.getId();
final Element columnElement = this.createXmlColumn(columnId, column.getMaxWidth(), column.getMinWidth(), column.getWidth());
rootElement.addContent(columnElement);
}
}
final Document xmlConf = new Document(rootElement);
 
return xmlConf;
}
 
protected final Element createXmlColumn(final String columnId, final double maxWidth, final double minWidth, final double width) {
final Element columnElement = new Element("column");
columnElement.setAttribute("id", columnId);
columnElement.setAttribute("max-width", String.valueOf(maxWidth));
columnElement.setAttribute("min-width", String.valueOf(minWidth));
columnElement.setAttribute("width", String.valueOf(width));
return columnElement;
}
 
@Override
public void setReadOnly(final boolean readOnly) {
super.setReadOnly(readOnly);
final List<Row> rows = this.tableSpec.getContent().getRows();
if (rows != null) {
final int rowCount = rows.size();
for (int i = 0; i < rowCount; i++) {
final Row row = rows.get(i);
 
if (this.hasRow()) {
final int size = this.getRowsCount();
for (int i = 0; i < size; i++) {
final Row row = this.getRow(i);
final List<Object> values = row.getValues();
for (final Object value : values) {
if (value != null && value instanceof LightUIElement) {
265,7 → 474,7
}
 
@Override
public void setValueFromContext(final Object value) {
public void _setValueFromContext(final Object value) {
if (value != null) {
final JSONArray jsonContext = (JSONArray) JSONConverter.getObjectFromJSON(value, JSONArray.class);
final ColumnsSpec columnsSpec = this.getTableSpec().getColumns();
280,21 → 489,24
}
}
 
final TableContent tableContent = this.getTableSpec().getContent();
if (tableContent != null) {
final List<Row> rows = tableContent.getRows();
for (int i = 0; i < rows.size(); i++) {
final Row row = rows.get(i);
if (this.hasRow()) {
final int size = this.getRowsCount();
if (jsonContext.size() != size) {
System.err.println("LightUITable.setValueFromContext() - Incorrect line count in JSON");
} else {
 
for (int i = 0; i < size; i++) {
final Row row = this.getRow(i);
final JSONObject jsonLineContext = (JSONObject) JSONConverter.getObjectFromJSON(jsonContext.get(i), JSONObject.class);
final Long rowId = (Long) JSONConverter.getParameterFromJSON(jsonLineContext, "row.id", Long.class);
final Number rowId = JSONConverter.getParameterFromJSON(jsonLineContext, "row.id", Number.class);
final String rowExtendId = (String) JSONConverter.getParameterFromJSON(jsonLineContext, "row.extend.id", String.class);
if (rowId == row.getId() && (row.getExtendId() == null || (row.getExtendId() != null && rowExtendId.equals(row.getExtendId())))) {
if (NumberUtils.areNumericallyEqual(rowId, row.getId()) && (row.getExtendId() == null || (row.getExtendId() != null && rowExtendId.equals(row.getExtendId())))) {
if (row.isFillWidth()) {
if (!row.getValues().isEmpty() && row.getValues().get(0) instanceof IUserControl) {
if (!row.getValues().isEmpty() && row.getValues().get(0) instanceof LightUserControl) {
final LightUIElement element = (LightUIElement) row.getValues().get(0);
if (element instanceof IUserControl) {
if (element instanceof LightUserControl) {
if (jsonLineContext.containsKey(element.getUUID())) {
((IUserControl) element).setValueFromContext(jsonLineContext.get(element.getUUID()));
((LightUserControl) element)._setValueFromContext(jsonLineContext.get(element.getUUID()));
} else {
System.out.println("LightUITable.setValueFromContext() - Unable to find element : id - " + element.getId() + " uuid - " + element.getUUID());
System.out.println("LightUITable.setValueFromContext() - In JSON : " + jsonLineContext.toJSONString());
304,13 → 516,13
} else {
for (int k = 0; k < editorsIndex.size(); k++) {
final Object objEditor = row.getValues().get(editorsIndex.get(k));
if (!(objEditor instanceof IUserControl)) {
if (!(objEditor instanceof LightUserControl)) {
throw new IllegalArgumentException("Impossible to find editor for row: " + rowId.toString() + " at position: " + String.valueOf(k));
}
final LightUIElement editor = (LightUIElement) objEditor;
 
if (editor instanceof IUserControl && jsonLineContext.containsKey(editor.getUUID())) {
((IUserControl) editor).setValueFromContext(jsonLineContext.get(editor.getUUID()));
if (editor instanceof LightUserControl && jsonLineContext.containsKey(editor.getUUID())) {
((LightUserControl) editor)._setValueFromContext(jsonLineContext.get(editor.getUUID()));
} else {
throw new IllegalArgumentException(
"Impossible to find value for editor: " + editor.getId() + " for row: " + rowId.toString() + " at position: " + String.valueOf(k));
324,6 → 536,7
}
}
}
}
 
@Override
public LightUIElement clone() {
334,21 → 547,22
public JSONObject toJSON() {
final JSONObject json = super.toJSON();
if (this.allowSelection) {
json.put("allow-selection", true);
json.put(ALLOW_SELECTION, true);
}
if (this.allowMultiSelection) {
json.put(ALLOW_MULTI_SELECTION, true);
}
if (this.dynamicLoad) {
json.put("dynamic-load", true);
json.put(DYNAMIC_LOAD, true);
}
if (this.verticallyScrollable) {
json.put("vertically-scrollable", true);
if (!this.autoSelectFirstLine) {
json.put(AUTO_SELECT_FIRST_LINE, false);
}
if (this.tableSpec != null) {
json.put("table-spec", this.tableSpec.toJSON());
json.put(TABLE_SPEC, this.tableSpec.toJSON());
}
if (this.elementCode != null) {
json.put("element-code", this.elementCode);
}
json.put("line-per-row", this.linePerRow);
 
json.put(LINE_PER_ROW, this.linePerRow);
return json;
}
 
355,16 → 569,22
@Override
public void fromJSON(final JSONObject json) {
super.fromJSON(json);
this.allowSelection = JSONConverter.getParameterFromJSON(json, "allow-selection", Boolean.class, false);
this.dynamicLoad = JSONConverter.getParameterFromJSON(json, "dynamic-load", Boolean.class, false);
this.verticallyScrollable = JSONConverter.getParameterFromJSON(json, "vertically-scrollable", Boolean.class, false);
this.elementCode = JSONConverter.getParameterFromJSON(json, "element-code", String.class);
this.linePerRow = JSONConverter.getParameterFromJSON(json, "line-per-row", Integer.class);
this.allowSelection = JSONConverter.getParameterFromJSON(json, ALLOW_SELECTION, Boolean.class, false);
this.allowSelection = JSONConverter.getParameterFromJSON(json, ALLOW_MULTI_SELECTION, Boolean.class, false);
this.dynamicLoad = JSONConverter.getParameterFromJSON(json, DYNAMIC_LOAD, Boolean.class, false);
this.autoSelectFirstLine = JSONConverter.getParameterFromJSON(json, AUTO_SELECT_FIRST_LINE, Boolean.class, true);
this.linePerRow = JSONConverter.getParameterFromJSON(json, LINE_PER_ROW, Integer.class);
 
final JSONObject jsonRawContent = (JSONObject) JSONConverter.getParameterFromJSON(json, "table-spec", JSONObject.class);
final JSONObject jsonRawContent = (JSONObject) JSONConverter.getParameterFromJSON(json, TABLE_SPEC, JSONObject.class);
 
if (jsonRawContent != null) {
this.tableSpec = new TableSpec(jsonRawContent);
}
}
 
@Override
public void destroy() {
super.destroy();
this.selectionListeners.clear();
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUILine.java
68,7 → 68,7
this.gridAlignment = gridAlignment;
}
 
public Integer getHeight() {
public Integer getTotalHeight() {
Integer h = 0;
final int size = this.getChildrenCount();
for (int i = 0; i < size; i++) {
77,7 → 77,7
return h;
}
 
public Integer getWidth() {
public Integer getTotalGridWidth() {
Integer w = 0;
final int size = this.getChildrenCount();
for (int i = 0; i < size; i++) {
/trunk/OpenConcerto/src/org/openconcerto/ui/light/TreeItem.java
40,7 → 40,7
}
 
public final String getId() {
return id;
return this.id;
}
 
public final void setId(String id) {
48,7 → 48,7
}
 
public String getLabel() {
return label;
return this.label;
}
 
public void setLabel(String label) {
56,15 → 56,15
}
 
public TreeItem getChild(int i) {
return children.get(i);
return this.children.get(i);
}
 
public int getChildCount() {
return children.size();
return this.children.size();
}
 
public final List<TreeItem> getChildren() {
return children;
return this.children;
}
 
public final void addChild(TreeItem item) {
90,7 → 90,7
}
 
public final boolean isSelected() {
return isSelected;
return this.isSelected;
}
 
public final void setSelected(boolean isSelected) {
98,7 → 98,7
}
 
public final Color getColor() {
return color;
return this.color;
}
 
public final void setColor(Color color) {
106,7 → 106,7
}
 
public final boolean isExpanded() {
return expanded;
return this.expanded;
}
 
public final void setExpanded(boolean expanded) {
114,7 → 114,7
}
 
public final String getIconId() {
return iconId;
return this.iconId;
}
 
public final void setRightIconId(String iconId) {
/trunk/OpenConcerto/src/org/openconcerto/ui/light/SearchSpec.java
23,7 → 23,7
 
public class SearchSpec implements Transferable {
private String tableId;
private List<SearchContent> content;
private List<SearchContent> content = new ArrayList<SearchContent>();
 
public SearchSpec(final String tableId) {
this.tableId = tableId;
33,11 → 33,11
this.fromJSON(json);
}
 
public String getTableId() {
public final String getTableId() {
return this.tableId;
}
 
public List<SearchContent> getContent() {
public final List<SearchContent> getContent() {
return this.content;
}
 
/trunk/OpenConcerto/src/org/openconcerto/ui/light/TableContent.java
31,7 → 31,7
}
 
public TableContent(final String tableId) {
this.init(tableId, null);
this.init(tableId, new ArrayList<Row>());
}
 
public TableContent(final String tableId, final List<Row> rows) {
42,38 → 42,65
this.fromJSON(json);
}
 
private void init(final String tableId, final List<Row> rows) {
private final void init(final String tableId, final List<Row> rows) {
this.tableId = tableId;
if (rows != null) {
this.rows = rows;
} else {
this.rows = new ArrayList<Row>();
}
}
 
public String getTableId() {
public final String getTableId() {
return this.tableId;
}
 
public void setTableId(final String tableId) {
public final void setTableId(final String tableId) {
this.tableId = tableId;
}
 
public List<Row> getRows() {
return this.rows;
public final synchronized Row getRow(final int index) {
return this.rows.get(index);
}
 
public void setRows(List<Row> rows) {
public final synchronized int getRowsCount() {
return this.rows.size();
}
 
public final synchronized boolean addRow(final Row row) {
return this.rows.add(row);
}
 
public final synchronized Row setRow(final int index, final Row row) {
return this.rows.set(index, row);
}
 
public final synchronized Row removeRow(final int index) {
return this.rows.remove(index);
}
 
public final synchronized boolean removeRow(final Row row) {
return this.rows.remove(row);
}
 
public final synchronized void setRows(List<Row> rows) {
this.rows = rows;
}
 
public final synchronized void clearRows() {
this.rows.clear();
}
/**
* @return a copy of the list
*/
public final synchronized List<Row> getRows(){
return new ArrayList<Row>(this.rows);
}
 
@Override
public String toString() {
return "TableContent of " + this.tableId + " lines count : " + getRows().size();
public synchronized String toString() {
return "TableContent of " + this.tableId + " lines count : " + this.getRowsCount();
}
 
@Override
public JSONObject toJSON() {
public synchronized JSONObject toJSON() {
final JSONObject result = new JSONObject();
result.put("class", "TableContent");
result.put("table-id", this.tableId);
82,14 → 109,15
}
 
@Override
public void fromJSON(final JSONObject json) {
this.tableId = (String) JSONConverter.getParameterFromJSON(json, "table-id", String.class);
final JSONArray jsonRows = (JSONArray) JSONConverter.getParameterFromJSON(json, "rows", JSONArray.class);
public synchronized void fromJSON(final JSONObject json) {
this.tableId = JSONConverter.getParameterFromJSON(json, "table-id", String.class);
final JSONArray jsonRows = JSONConverter.getParameterFromJSON(json, "rows", JSONArray.class);
if (jsonRows != null) {
this.rows = new ArrayList<Row>();
final List<Row> listRows = new ArrayList<Row>();
for (final Object o : jsonRows) {
this.rows.add(new Row((JSONObject) JSONConverter.getObjectFromJSON(o, JSONObject.class)));
listRows.add(new Row(JSONConverter.getObjectFromJSON(o, JSONObject.class)));
}
this.setRows(listRows);
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/valuewrapper/ValueWrapperFactory.java
43,11 → 43,12
if (isValueWrapper(comp, c)) {
return (ValueWrapper<Z>) comp;
} else if (getConvertorVW(comp, c) != null) {
// e.g. comp is ValueWrapper<X> and there's a convertor between X and Z
return getConvertorVW(comp, c);
} else if (isValueObject(comp, c)) {
return new ValueWrapperFromVO<Z>((MutableValueObject) comp);
return new ValueWrapperFromVO<Z>((MutableValueObject<Z>) comp);
} else if (comp instanceof JFormattedTextField && JFormattedTextFieldValueWrapper.isCompatible((JFormattedTextField) comp, c)) {
return new JFormattedTextFieldValueWrapper((JFormattedTextField) comp, c);
return new JFormattedTextFieldValueWrapper<Z>((JFormattedTextField) comp, c);
} else if (Boolean.class.isAssignableFrom(c)) {
return (ValueWrapper<Z>) new BooleanValueWrapper((JToggleButton) comp);
} else if (String.class.isAssignableFrom(c))
61,7 → 62,7
private static <Z> boolean isValueWrapper(final JComponent comp, final Class<Z> c) {
if (!(comp instanceof ValueWrapper))
return false;
return ReflectUtils.isCastable((ValueWrapper) comp, ValueWrapper.class, c);
return ReflectUtils.isCastable((ValueWrapper<?>) comp, ValueWrapper.class, c);
}
 
@SuppressWarnings("unchecked")
72,6 → 73,7
final List<Class<?>> typeArguments = ReflectUtils.getTypeArguments(vw, ValueWrapper.class);
if (typeArguments.size() == 0)
throw new IllegalArgumentException("unable to find type arguments of " + vw + " \n(you should define a class that specify them, eg class C extends ValueWrapper<Integer>)");
assert typeArguments.size() == 1 : "Wrong number of arguments for ValueWrapper : " + typeArguments;
final Class<?> typeArgument = typeArguments.get(0);
return createCVW(vw, typeArgument, c);
}
87,7 → 89,7
private static <Z> boolean isValueObject(final JComponent comp, final Class<Z> c) {
if (!(comp instanceof MutableValueObject))
return false;
return ReflectUtils.isCastable((MutableValueObject) comp, ValueObject.class, c);
return ReflectUtils.isCastable((MutableValueObject<?>) comp, ValueObject.class, c);
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/EditPanel.java
26,7 → 26,6
import org.openconcerto.sql.users.rights.UserRights;
import org.openconcerto.sql.users.rights.UserRightsManager;
import org.openconcerto.sql.view.list.IListe;
import org.openconcerto.ui.component.InteractionMode;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.checks.ValidListener;
import org.openconcerto.utils.checks.ValidObject;
171,7 → 170,7
this.component = e;
try {
this.component.setMode(mode.getCompMode());
this.component.setNonExistantEditable(this.mode == CREATION);
this.component.setNonExistantEditable(this.getMode() == CREATION);
if (this.component instanceof BaseSQLComponent) {
for (int i = 0; i < hiddenFields.size(); i++) {
final SQLField hiddenField = hiddenFields.get(i);
179,7 → 178,7
}
}
 
if (this.mode != READONLY) {
if (this.getMode() != READONLY) {
// on écoute les changements de validation,
// avant component.uiInit() car il fait un fireValidChange()
this.component.addValidListener(new ValidListener() {
213,6 → 212,10
}
}
 
public final EditMode getMode() {
return this.mode;
}
 
private void updateBtns() {
updateBtn(this.jButtonAjouter, true, false, "noRightToAdd", TableAllRights.ADD_ROW_TABLE);
updateBtn(this.jButtonModifier, true, true, "noRightToModify", TableAllRights.MODIFY_ROW_TABLE);
300,7 → 303,7
c.gridheight = 1;
c.fill = GridBagConstraints.NONE;
this.keepOpen.setOpaque(false);
if (this.mode == CREATION) {
if (this.getMode() == CREATION) {
 
c.gridx = 1;
c.fill = GridBagConstraints.HORIZONTAL;
331,7 → 334,7
}
}
});
} else if (this.mode == MODIFICATION) {
} else if (this.getMode() == MODIFICATION) {
c.gridx = 1;
c.anchor = GridBagConstraints.EAST;
this.jButtonModifier = new JButton(TM.tr("saveModifications"));
347,7 → 350,7
c.weightx = 0;
c.gridx = 3;
c.anchor = GridBagConstraints.EAST;
if (this.mode == READONLY)
if (this.getMode() == READONLY)
this.jButtonAnnuler = new JButton(TM.tr("close"));
else
this.jButtonAnnuler = new JButton(TM.tr("cancel"));
390,7 → 393,7
// laisser passer les valides qui écrasent tout autant.
if (id < SQLRow.MIN_VALID_ID)
this.component.select(null);
else if (this.mode == CREATION) {
else if (this.getMode() == CREATION) {
this.component.select(this.element.createCopy(id));
} else {
this.component.select(id);
399,11 → 402,11
 
protected final void apply() {
final JButton b;
if (this.mode == CREATION)
if (this.getMode() == CREATION)
b = this.jButtonAjouter;
else if (this.mode == MODIFICATION)
else if (this.getMode() == MODIFICATION)
b = this.jButtonModifier;
else if (this.mode == READONLY)
else if (this.getMode() == READONLY)
b = this.jButtonAnnuler;
else
b = null;
610,7 → 613,7
}
 
public String getDocId() {
return this.mode + "_" + this.element.getTable().getName();
return this.getMode() + "_" + this.element.getTable().getName();
}
 
public String getGenericDoc() {
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelSourceStateOffline.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/ChangeFKRunnable.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelSourceStateOnline.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/TableAction.java
18,6 → 18,7
 
public class TableAction {
private String id = null;
private boolean showList = false;
private List<RowAction> actions = new ArrayList<RowAction>();
public TableAction(final RowAction action) {
48,7 → 49,15
return null;
}
public void setShowList(final boolean showList) {
this.showList = showList;
}
public boolean isShowList() {
return this.showList;
}
public int getActionsCount() {
return this.actions.size();
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/UpdateQueue.java
28,6 → 28,7
import org.openconcerto.sql.view.list.search.SearchOne.Mode;
import org.openconcerto.sql.view.list.search.SearchQueue;
import org.openconcerto.sql.view.list.search.SearchQueue.SetStateRunnable;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.ListMap;
import org.openconcerto.utils.RecursionType;
import org.openconcerto.utils.SleepingQueue;
50,8 → 51,6
 
import net.jcip.annotations.GuardedBy;
 
import org.apache.commons.collections.CollectionUtils;
 
public final class UpdateQueue extends SleepingQueue {
 
/**
132,7 → 131,7
@Override
protected void started() {
// savoir quand les tables qu'on affiche changent
addListeners();
addSourceListener();
stateChanged(null, this.getModel().getReq().createState());
// Only starts once there's something to search, that way the runnable passed to
// ITableModel.search() will be meaningful
222,7 → 221,7
if (id < SQLRow.MIN_VALID_ID)
throw new IllegalArgumentException("invalid ID: " + id);
if (!fullList.isEmpty()) {
final SQLRowValues proto = this.getModel().getLinesSource().getParent().getMaxGraph();
final SQLRowValues proto = this.getState().getReq().getGraphToFetch();
final List<Path> pathsToT = new ArrayList<Path>();
proto.getGraph().walk(proto, pathsToT, new ITransformer<State<List<Path>>, List<Path>>() {
@Override
286,7 → 285,7
this.tableModel.getSearchQueue().fullListChanged();
}
 
final void reorder(final List<Number> idsOrder) {
final void reorder(final List<Integer> idsOrder) {
final List<ListSQLLine> fullList = this.getFullList();
synchronized (fullList) {
for (final ListSQLLine l : fullList) {
383,7 → 382,7
put(new SetStateRunnable() {
@Override
public void run() {
UpdateQueue.this.state = afterState;
setState(afterState);
}
});
// TODO if request didn't change and the new graph is smaller, copy and prune the
393,6 → 392,14
});
}
 
protected final void setState(final SQLTableModelSourceState newState) {
if (this.state != null)
this.rmTableListener();
this.state = newState;
if (this.state != null)
this.addTableListener();
}
 
protected final SQLTableModelSourceState getState() {
assert this.currentlyInQueue();
if (this.state == null)
402,29 → 409,24
 
@Override
protected void willDie() {
this.removeListeners();
this.rmTableListener();
this.removeSourceListener();
super.willDie();
}
 
protected final void addTableListener() {
for (final SQLTable t : this.tableModel.getReq().getTables()) {
t.addTableModifiedListener(this.tableListener);
this.getState().getReq().addTableListener(this.tableListener);
}
}
 
private void addListeners() {
this.addTableListener();
private void addSourceListener() {
this.tableModel.getLinesSource().addListener(this.tableListener);
}
 
protected final void rmTableListener() {
for (final SQLTable t : this.tableModel.getReq().getTables()) {
t.removeTableModifiedListener(this.tableListener);
this.getState().getReq().removeTableListener(this.tableListener);
}
}
 
private void removeListeners() {
this.rmTableListener();
private void removeSourceListener() {
this.tableModel.getLinesSource().rmListener(this.tableListener);
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/IListe.java
15,7 → 15,6
 
import org.openconcerto.openoffice.XMLFormatVersion;
import org.openconcerto.openoffice.spreadsheet.SpreadSheet;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.Log;
import org.openconcerto.sql.TM;
import org.openconcerto.sql.element.SQLComponent;
136,6 → 135,8
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
 
import net.jcip.annotations.GuardedBy;
 
/**
* Une liste de lignes correspondant à une ListSQLRequest. Diagramme pour la sélection :
* <img src="doc-files/listSelection.png"/><br/>
272,11 → 273,14
private final JTable jTable;
private final JTextField filter;
private boolean debugFilter;
@GuardedBy("this")
private FilterWorker filterWorker;
// optional popup on the table
private final JPopupMenu popup;
private final TableSorter sorter;
// record the source when non-displayable (ie getModel() == null)
@GuardedBy("this")
// record the source when non-displayable (ie getModel() == null), also allow to be read outside
// of the EDT
private SQLTableModelSource src;
private boolean adjustVisible;
private ColumnSizeAdjustor tcsa;
306,23 → 310,11
 
private int retainCount = 0;
 
public IListe(final ListSQLRequest req) {
this(req, null);
}
 
public IListe(final ListSQLRequest req, File configFile) {
this((Object) req, configFile);
}
 
public IListe(final SQLTableModelSource req) {
this(req, null);
}
 
public IListe(final SQLTableModelSource req, File configFile) {
this((Object) req, configFile);
}
 
private IListe(final Object req, File configFile) {
public IListe(final SQLTableModelSource req, final File configFile) {
if (req == null)
throw new NullPointerException("Création d'une IListe avec une requete null");
 
441,7 → 433,6
this.filter = new JTextField();
this.filter.setEditable(false);
this.debugFilter = false;
this.filterWorker = null;
 
// do not handle F2, let our application use it :
// remove F2 keybinding, use space
529,10 → 520,7
// MAYBE only set this.src and let the model be null so that the mere creation of an IListe
// does not spawn several threads and access the db. But a lot of code assumes there's
// immediately a model.
if (req instanceof SQLTableModelSource)
this.setSource((SQLTableModelSource) req);
else
this.setRequest((ListSQLRequest) req);
this.setSource(req);
this.state = ListSelectionState.manage(this.jTable.getSelectionModel(), new TableListStateModel(this.sorter));
this.state.addPropertyChangeListener("selectedIndex", new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
736,6 → 724,9
}
});
FontUtils.setFontFor(this.filter, SEP);
// initially hide to limit modifications for instances which don't need the filter, see
// setFilter() comment
this.setFilter(null);
this.updateFilter();
 
// * JTable
981,10 → 972,15
* @param text the text to display, <code>null</code> to hide the label.
*/
private void setFilter(String text) {
final boolean currentVisible = this.filter.isVisible();
final boolean newVisible = text != null;
// limit modifications due to a bug in Swing that can bring the frame to the back.
if (newVisible || currentVisible) {
this.filter.setText(text == null ? "" : text);
this.filter.setVisible(text != null);
this.filter.setVisible(newVisible);
this.revalidate();
}
}
 
public void selectID(final int id) {
this.selectIDs(Collections.singleton(id));
1146,7 → 1142,7
return clazz.cast(toCast);
}
 
public SQLRow fetchRow(int id) {
private SQLRow fetchRow(int id) {
if (id < SQLRow.MIN_VALID_ID) {
return null;
} else
1419,32 → 1415,25
}
 
public final ListSQLRequest getRequest() {
// TODO a superclass of ListSQLRequest for use in SQLTableModelSource
// our clients always use either setWhere() or setSelTransf()
// also add the ability to Offline to respect the filter
return ((SQLTableModelSourceOnline) this.getSource()).getReq();
return this.getSource().getReq();
}
 
public final void setRequest(ListSQLRequest listReq) {
// a ListSQLRequest can be changed with setWhere()/setFilterEnable(), so copy it
this.setSource(new SQLTableModelSourceOnline(listReq));
}
 
public final void setSource(SQLTableModelSource src) {
if (src == null)
throw new NullPointerException();
// necessary to limit table model changes, since it recreates columns (and thus forget about
// customizations, eg renderers)
synchronized (this) {
// necessary to limit table model changes, since it recreates columns (and thus forget
// about customizations, eg renderers)
if (this.src == src)
return;
 
this.src = src;
}
this.setTableModel(new ITableModel(src));
}
 
public final SQLTableModelSource getSource() {
final ITableModel m = this.getModel();
return m == null ? null : m.getReq();
public synchronized final SQLTableModelSource getSource() {
return this.src;
}
 
public final File getConfigFile() {
1485,7 → 1474,7
if (!requiredToLive && !this.isDead()) {
this.setTableModel(null);
} else if (requiredToLive && this.isDead()) {
this.setTableModel(new ITableModel(this.src));
this.setTableModel(new ITableModel(this.getSource()));
}
}
 
1577,7 → 1566,7
Thread.sleep(60);
 
final List<String> ancestors = new ArrayList<String>();
final SQLElementDirectory dir = Configuration.getInstance().getDirectory();
final SQLElementDirectory dir = getSource().getElem().getDirectory();
// always put the description of getRows(), but only put their ancestor if they all have
// the same parent
Tuple2<SQLRow, String> parentAndDesc = getParent(this.getRows(), dir);
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/AutoCompletionManager.java
232,6 → 232,15
} else {
rowV = AutoCompletionManager.this.fillFrom.getTable().getRow(id);
}
// Test pour éviter de perdre la sélection d'un article ayant la même désignation
// mais un code différent (Problème remonté par Afhymat avec la tabulation)
if (fillFrom != null && fillFrom.getTable().getName().equals("ARTICLE") && fillFrom.getName().equals("NOM") && rowDest != null && !rowDest.isUndefined()) {
SQLRowAccessor rowArt = rowDest.getForeign("ID_ARTICLE");
if (rowArt != null && !rowArt.isUndefined() && rowArt.getString("NOM") != null && rowArt.getString("NOM").trim().equalsIgnoreCase(rowV.getString("NOM").trim())) {
return;
}
}
 
final Set<String> keys = AutoCompletionManager.this.fillBy.keySet();
// Fill the table model rowvalue with the selected item using the fields defined
// with 'fill'
250,7 → 259,8
|| !AutoCompletionManager.this.table.getRowValuesTableModel().getValueAt(rowE, column).equals(fromV)) {
AutoCompletionManager.this.table.getRowValuesTableModel().setValueAt(fromV, rowE, column);
// Test Only if not foreign --> Bug avec le
// sqltextcombocelleditor, si test edit cellAt -> fire idSelected
// sqltextcombocelleditor, si test edit cellAt -> fire
// idSelected
// -1 sur la combo ce qui entraine une déselection (Bug Remonté
// par SA Poulignier)
if (!AutoCompletionManager.this.foreign && AutoCompletionManager.this.table.getEditingColumn() == column
278,10 → 288,12
}).start();
}
 
public void fillRowValues(SQLRowAccessor from, SQLRowValues to) {
final Set<String> keys = AutoCompletionManager.this.fillBy.keySet();
for (Iterator<String> iter = keys.iterator(); iter.hasNext();) {
String fromField = iter.next();
public Set<String> getFieldsFrom() {
return this.fillBy.keySet();
}
 
public void fillRowValues(SQLRowAccessor from, Set<String> fields, SQLRowValues to) {
for (String fromField : fields) {
String toField = AutoCompletionManager.this.fillBy.get(fromField);
to.put(toField, getValueFrom(from.asRow(), fromField, to));
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/AbstractUpdateOneRunnable.java
19,7 → 19,6
import org.openconcerto.sql.model.SQLRowValues.CreateMode;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.request.BaseFillSQLRequest;
import org.openconcerto.sql.request.ListSQLRequest;
78,9 → 77,10
final List<ListSQLLine> lines = (List<ListSQLLine>) e.getValue();
// primary table already handled above
if (p.length() > 0 && !lines.isEmpty()) {
final ListSQLRequest updateQueueReq = getModel().getLinesSource().getUpdateQueueReq();
// deepCopy() instead of new SQLRowValues() otherwise the used line's graph will be
// modified (eg the new instance would be linked to it)
final SQLRowValues proto = getModel().getLinesSource().getParent().getMaxGraph().followPathToOne(p, CreateMode.CREATE_NONE, false).deepCopy();
final SQLRowValues proto = updateQueueReq.getGraphToFetch().followPathToOne(p, CreateMode.CREATE_NONE, false).deepCopy();
final String lastReferentField = SearchQueue.getLastReferentField(p);
// there's only one path from the graph start to proto, and we will graft the newly
// fetched values at the end of p, so remove other values
102,25 → 102,22
// reloading rows from the primary table and not just the changed rows)
final SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(proto);
BaseFillSQLRequest.setupForeign(fetcher);
final ITransformer<SQLSelect, SQLSelect> transf = new ITransformer<SQLSelect, SQLSelect>() {
if (updateQueueReq.isLockSelect()) {
fetcher.appendSelTransf(new ITransformer<SQLSelect, SQLSelect>() {
@Override
public SQLSelect transformChecked(SQLSelect input) {
if (ListSQLRequest.getDefaultLockSelect())
input.addLockedTable(getTable().getName());
return input.setWhere(getRow().getWhere());
return input;
}
};
fetcher.setSelTransf(transf);
final List<SQLRowValues> fetched = fetcher.fetch();
if (fetched.size() > 1)
throw new IllegalStateException("more than one row fetched for " + this + " with " + fetcher.getReq() + " :\n" + fetched);
});
}
final SQLRowValues soleFetched = fetcher.fetchOne(getRow().getIDNumber());
 
// OK if lastReferentField != null : a referent row has been deleted
if (fetched.size() == 0 && lastReferentField == null) {
if (soleFetched == null && lastReferentField == null) {
Log.get().fine("no row fetched for " + this + ", lines have been changed without the TableModel knowing : " + lines + " req :\n" + fetcher.getReq());
getModel().updateAll();
} else {
final SQLRowValues soleFetched = CollectionUtils.getSole(fetched);
// copy it to each affected lines
for (final ListSQLLine line : lines) {
// don't update a part of the line, if the whole has been be passed to
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTextComboTableCellEditor.java
129,13 → 129,11
}
this.comboBox.grabFocus();
 
this.comboBox.addValueListener(new PropertyChangeListener() {
this.comboBox.addModelListener("wantedID", new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (!SQLTextComboTableCellEditor.this.comboBox.isUpdating()) {
SQLTextComboTableCellEditor.this.val = SQLTextComboTableCellEditor.this.comboBox.getSelectedId();
public void propertyChange(final PropertyChangeEvent evt) {
SQLTextComboTableCellEditor.this.val = SQLTextComboTableCellEditor.this.comboBox.getWantedID();
}
}
});
// Filtre sur une valeur specifique
if (this.fieldWhere != null && table instanceof RowValuesTable) {
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelLinesSource.java
16,6 → 16,7
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.request.ListSQLRequest;
import org.openconcerto.utils.Value;
import org.openconcerto.utils.cc.IPredicate;
 
51,6 → 52,10
 
public abstract SQLTableModelSource getParent();
 
public final ListSQLRequest getUpdateQueueReq() {
return this.getModel().getUpdateQ().getState().getReq();
}
 
public abstract List<ListSQLLine> getAll();
 
/**
126,7 → 131,7
} else {
throw new IllegalArgumentException("No ID for " + v);
}
final ListSQLLine res = new ListSQLLine(this, v, id, this.getModel().getUpdateQ().getState().getAllColumns());
final ListSQLLine res = new ListSQLLine(this, v, id, this.getModel().getUpdateQ().getState());
this.lineCreated(res);
return res;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelSourceOnline.java
13,15 → 13,9
package org.openconcerto.sql.view.list;
 
import org.openconcerto.sql.model.IFieldPath;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.request.BaseFillSQLRequest;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.request.ListSQLRequest;
import org.openconcerto.utils.cc.IClosure;
import org.openconcerto.utils.change.ListChangeIndex;
 
import java.util.Collections;
 
/**
* A SQLTableModelSource directly tied to the database. Any changes to its lines are propagated to
* the database without any delay.
30,55 → 24,21
*/
public class SQLTableModelSourceOnline extends SQLTableModelSource {
 
private final ListSQLRequest req;
 
public SQLTableModelSourceOnline(ListSQLRequest req) {
super(req.getGraph());
this.req = req;
public SQLTableModelSourceOnline(final ListSQLRequest req, final SQLElement elem) {
super(req, elem);
}
 
public SQLTableModelSourceOnline(SQLTableModelSourceOnline src) {
super(src);
this.req = src.req;
}
 
public final ListSQLRequest getReq() {
return this.req;
}
 
@Override
protected SQLTableModelSourceState createState() {
return new SQLTableModelSourceStateOnline(this.getAllColumns(), this.getReq());
protected boolean allowBiggerGraph() {
return true;
}
 
@Override
protected void colsChanged(final ListChangeIndex<SQLTableModelColumn> change) {
super.colsChanged(change);
// add needed fields for each new column
this.getReq().changeGraphToFetch(new IClosure<SQLRowValues>() {
@Override
public void executeChecked(SQLRowValues g) {
for (final SQLTableModelColumn col : change.getItemsAdded()) {
// DebugRow should uses all *fetched* fields, but since it cannot know them
// getPaths() return all fields in the table. So don't fetch all fields just for
// this debug column
if (!(col instanceof DebugRow)) {
for (final IFieldPath p : col.getPaths()) {
BaseFillSQLRequest.addToFetch(g, p.getPath(), Collections.singleton(p.getFieldName()));
}
}
}
}
});
}
 
@Override
protected SQLTableModelLinesSourceOnline _createLinesSource(final ITableModel model) {
return new SQLTableModelLinesSourceOnline(this, model);
}
 
@Override
public SQLRowValues getMaxGraph() {
return this.getReq().getGraphToFetch();
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/ITableModel.java
16,6 → 16,7
import static org.openconcerto.sql.view.list.ITableModel.SleepState.AWAKE;
import static org.openconcerto.sql.view.list.ITableModel.SleepState.HIBERNATING;
import static org.openconcerto.sql.view.list.ITableModel.SleepState.SLEEPING;
 
import org.openconcerto.sql.Log;
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.sql.model.SQLRowAccessor;
936,5 → 937,4
}
}
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelColumnPath.java
14,6 → 14,7
package org.openconcerto.sql.view.list;
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.model.FieldPath;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowAccessor;
36,14 → 37,10
*/
public class SQLTableModelColumnPath extends SQLTableModelColumn {
 
// try to find a RowItemDesc, fall back to SQLFieldTranslator.getDefaultDesc()
private static final RowItemDesc getDescFor(SQLField field) {
// FIXME : remove Configuration.getInstance() need
return getDescFor(field, Configuration.getInstance());
}
 
public static final RowItemDesc getDescFor(SQLField field, Configuration conf) {
final RowItemDesc res = conf == null ? SQLFieldTranslator.NULL_DESC : conf.getTranslator().getDescFor(field.getTable(), field.getName());
public static final RowItemDesc getDescFor(final SQLField field, SQLElementDirectory dir) {
if (dir == null && Configuration.getInstance() != null)
dir = Configuration.getInstance().getDirectory();
final RowItemDesc res = dir == null ? SQLFieldTranslator.NULL_DESC : dir.getTranslator().getDescFor(field.getTable(), field.getName());
if (res.equals(SQLFieldTranslator.NULL_DESC))
return SQLFieldTranslator.getDefaultDesc(field);
else
50,15 → 47,12
return res;
}
 
private static final String getLabelFor(SQLField field) {
return getDescFor(field).getLabel();
}
 
private final FieldPath p;
private final SQLElementDirectory dir;
private boolean editable;
 
public SQLTableModelColumnPath(Path p, String fieldName, final String name) {
this(new FieldPath(p, fieldName), name);
this(new FieldPath(p, fieldName), name, null);
}
 
public SQLTableModelColumnPath(SQLField f) {
66,12 → 60,13
}
 
public SQLTableModelColumnPath(FieldPath fp) {
this(fp, getDescFor(fp.getField()).getTitleLabel());
this(fp, null, null);
}
 
public SQLTableModelColumnPath(FieldPath fp, final String name) {
super(name);
public SQLTableModelColumnPath(FieldPath fp, final String name, final SQLElementDirectory dir) {
super(name == null ? getDescFor(fp.getField(), dir).getTitleLabel() : name);
this.p = fp;
this.dir = dir;
this.editable = true;
}
 
84,9 → 79,9
public String getToolTip() {
final List<String> humanPath = new ArrayList<String>(this.p.getPath().length());
for (final SQLField f : this.p.getPath().getSingleFields()) {
humanPath.add(getLabelFor(f));
humanPath.add(getDescFor(f, this.dir).getLabel());
}
humanPath.add(getLabelFor(this.p.getField()));
humanPath.add(getDescFor(this.p.getField(), this.dir).getLabel());
return CollectionUtils.join(humanPath, IListe.SEP);
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/ListSQLLine.java
18,6 → 18,7
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValues.CreateMode;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTable.VirtualFields;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.view.list.search.SearchQueue;
import org.openconcerto.utils.CollectionUtils;
68,7 → 69,7
@GuardedBy("this")
private SQLRowValues row;
// Immutable
private final SQLTableModelColumns columns;
private final SQLTableModelSourceState state;
private final int id;
// allow to order by something not in the row
@GuardedBy("this")
79,12 → 80,12
@GuardedBy("this")
private List<Object> list;
 
ListSQLLine(SQLTableModelLinesSource src, SQLRowValues row, int id, final SQLTableModelColumns columns) {
ListSQLLine(SQLTableModelLinesSource src, SQLRowValues row, int id, final SQLTableModelSourceState state) {
super();
this.src = src;
this.setRow(row);
this.id = id;
this.columns = columns;
this.state = state;
this.clearCache();
}
 
95,6 → 96,14
updateList(columnCount, Collections.<Integer> emptySet());
}
 
public final SQLTableModelSourceState getState() {
return this.state;
}
 
public final SQLTableModelColumns getColumns() {
return this.getState().getAllColumns();
}
 
public final SQLTableModelLinesSource getSrc() {
return this.src;
}
140,7 → 149,7
}
 
public final void setValueAt(Object obj, int colIndex) {
this.columns.getColumns().get(colIndex).put(this, obj);
this.getColumns().getColumns().get(colIndex).put(this, obj);
}
 
// should update this.list at the passed indexes, and then recursively for dependent columns
175,7 → 184,7
for (int i = 0; i < newSize; i++) {
final Object o;
if (i >= alreadyLoaded || colsToUpdate.contains(i))
o = this.columns.getAllColumns().get(i).show(this.getRow());
o = this.getColumns().getAllColumns().get(i).show(this.getRow());
else
o = this.list.get(i);
newList.add(o);
200,7 → 209,11
* @return the columns that were affected, <code>null</code> meaning all.
*/
synchronized Set<Integer> loadAt(int id, SQLRowValues vals, Path p) {
assert vals == null || vals.getID() == id;
final String lastReferentField = SearchQueue.getLastReferentField(p);
// null vals means id was deleted, the only way we care is if it was pointing to us
// (otherwise the foreign key pointing to it would have changed first)
assert vals != null || lastReferentField != null;
// load() empties vals, so getFields() before
final Set<Integer> indexes = lastReferentField == null ? this.pathToIndex(p, vals.getFields()) : null;
// replace our values with the new ones
207,6 → 220,9
final SQLRowValues copy = this.getRow().deepCopy();
if (lastReferentField == null) {
for (final SQLRowValues v : copy.followPath(p, CreateMode.CREATE_NONE, false)) {
// check id, e.g. if p is BATIMENT <- LOCAL -> FAMILLE_LOCAL, then there's multiple
// familles
if (v.getID() == id)
v.load(vals.deepCopy(), null);
}
} else {
267,7 → 283,7
} else {
final Set<Integer> res = new HashSet<Integer>();
final Set<FieldPath> modifiedPaths = FieldPath.create(p, modifiedFields);
final List<? extends SQLTableModelColumn> cols = this.columns.getAllColumns();
final List<? extends SQLTableModelColumn> cols = this.getColumns().getAllColumns();
for (int i = 0; i < cols.size(); i++) {
final SQLTableModelColumn col = cols.get(i);
if (CollectionUtils.containsAny(col.getPaths(), modifiedPaths))
278,8 → 294,9
}
 
private static boolean containsFK(final SQLTable t, Collection<String> fields) {
final Set<SQLField> ffs = t.getFields(VirtualFields.FOREIGN_KEYS);
for (final String f : fields) {
if (t.getForeignKeys().contains(t.getField(f)))
if (ffs.contains(t.getField(f)))
return true;
}
return false;
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelLinesSourceOffline.java
16,21 → 16,18
import org.openconcerto.sql.model.ConnectionHandlerNoSetup;
import org.openconcerto.sql.model.OrderComparator;
import org.openconcerto.sql.model.SQLDataSource;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesCluster.State;
import org.openconcerto.sql.model.SQLRowValuesCluster.WalkOptions;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.model.graph.Link.Direction;
import org.openconcerto.sql.model.SQLTable.VirtualFields;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.utils.SQLUtils;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.CompareUtils;
import org.openconcerto.utils.NumberUtils;
import org.openconcerto.utils.RecursionType;
import org.openconcerto.utils.SetMap;
import org.openconcerto.utils.Value;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.utils.cc.IClosure;
 
import java.sql.SQLException;
import java.util.ArrayList;
42,6 → 39,7
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
62,16 → 60,16
// row container with equals() using reference equality
@NotThreadSafe
static private final class Row {
private final Number id;
private final Integer id;
private SQLRowValues vals;
 
private Row(Number id, SQLRowValues vals) {
private Row(Integer id, SQLRowValues vals) {
super();
this.id = id;
this.setRow(vals);
}
 
public final Number getID() {
public final Integer getID() {
return this.id;
}
 
109,7 → 107,7
private final List<Row> lines;
// since new lines have no database ID, give them a virtual one
private int freeID;
private final Map<Number, Row> id2line;
private final Map<Integer, Row> id2line;
// values that can be modified, read-only
private final SQLRowValues modifiableVals;
// original value for modified lines
123,7 → 121,7
this.lines = new LinkedList<Row>();
// the firsts are used in other part of the fwk
this.freeID = SQLRow.MIN_VALID_ID - 10;
this.id2line = new HashMap<Number, Row>();
this.id2line = new HashMap<Integer, Row>();
this.dbOrder = true;
}
 
131,19 → 129,6
super(model);
this.parent = parent;
this.modifiableVals = this.getParent().getElem().getPrivateGraph().toImmutable();
if (this.modifiableVals.getGraphSize() > 1) {
// because of updateRow() and commit() (precisely SQLElement.update())
if (this.modifiableVals.hasReferents())
throw new IllegalArgumentException("Referents are not supported");
this.modifiableVals.getGraph().walk(this.modifiableVals, null, new ITransformer<State<Object>, Object>() {
@Override
public Object transformChecked(State<Object> input) {
if (input.isBackwards())
throw new IllegalArgumentException("Referents are not supported");
return null;
}
}, new WalkOptions(Direction.ANY).setRecursionType(RecursionType.BREADTH_FIRST).setStartIncluded(false));
}
this.dbVals = new HashMap<Row, SQLRowValues>();
this.deleted = new HashSet<Number>();
}
157,15 → 142,15
return this.getModel().getUpdateQ().currentlyInQueue();
}
 
private final Row getRow(Number id) {
private final Row getRow(Integer id) {
return this.getRow(id, false);
}
 
private final Row getRow(Number id, final boolean required) {
private final Row getRow(Integer id, final boolean required) {
return this.getRow(id, required, null);
}
 
private final Row getRow(Number id, final boolean required, final Object idSource) {
private final Row getRow(Integer id, final boolean required, final Object idSource) {
final Row res = this.id2line.get(id);
if (required && res == null)
throw new IllegalArgumentException("Not in the list : " + (idSource == null ? id : idSource));
253,14 → 238,10
}
}
 
protected final SQLRowValuesListFetcher getFetcher() {
return ((SQLTableModelSourceStateOffline) this.getModel().getUpdateQ().getState()).getFetcher();
protected final List<SQLRowValues> fetch() {
return this.getUpdateQueueReq().getValues();
}
 
protected final List<SQLRowValues> fetch(final Where w) {
return this.getFetcher().fetch(w, true);
}
 
/**
* Fetch all rows and update our lines. Must be called by the {@link UpdateQueue}. Deleted rows
* will be removed, inserted rows added, virtual rows unchanged, and updated rows will only be
271,7 → 252,7
@Override
public List<ListSQLLine> getAll() {
assert checkUpdateThread();
final List<SQLRowValues> dbRows = this.fetch(null);
final List<SQLRowValues> dbRows = this.fetch();
 
if (this.lines.isEmpty()) {
// optimization of the else block
280,13 → 261,13
}
} else {
// delete
final Set<Number> dbIDs = new HashSet<Number>();
final Set<Integer> dbIDs = new HashSet<Integer>();
for (final SQLRowValues dbRow : dbRows) {
dbIDs.add(dbRow.getIDNumber(true));
dbIDs.add(dbRow.getIDNumber(true).intValue());
}
final Set<Number> deletedIDs = new HashSet<Number>(this.id2line.keySet());
final Set<Integer> deletedIDs = new HashSet<Integer>(this.id2line.keySet());
deletedIDs.removeAll(dbIDs);
for (final Number id : deletedIDs) {
for (final Integer id : deletedIDs) {
// don't delete virtual rows
if (id.intValue() >= SQLRow.MIN_VALID_ID) {
final Value<ListSQLLine> val = this.updateRow(id.intValue(), null);
332,10 → 313,7
@Override
public Value<ListSQLLine> get(final int id) {
assert checkUpdateThread();
final Where w = new Where(getParent().getPrimaryTable().getKey(), "=", id);
// since we use "=" pk, either 1 or 0
final SQLRowValues row = CollectionUtils.getSole(this.fetch(w));
return updateRow(id, row);
return updateRow(id, this.getUpdateQueueReq().getValues(id));
}
 
// *** Modify virtual rows ***
356,13 → 334,13
assert checkUpdateThread();
// make sure every needed path is there
if (grow)
vals.grow(getFetcher().getGraph(), false);
vals.grow(getUpdateQueueReq().getGraphToFetch(), false);
// ATTN only works because vals was just fetched or just copied
vals.getGraph().freeze();
final boolean fromDB = vals.hasID();
final List<Number> order;
final List<Integer> order;
final Row r;
r = new Row(fromDB ? vals.getIDNumber() : this.freeID--, vals);
r = new Row(fromDB ? vals.getID() : this.freeID--, vals);
this.id2line.put(r.getID(), r);
this.lines.add(r);
 
391,7 → 369,7
return this.getModel().getUpdateQ().execute(new FutureTask<SQLRowValues>(new OfflineCallable<SQLRowValues>() {
@Override
public SQLRowValues call() throws Exception {
final Row r = getRow(id);
final Row r = getRow(id.intValue());
return r == null ? null : rm(r).vals;
}
}));
429,63 → 407,99
@Override
public void run() {
getModel().getUpdateQ().updateLine(l, path, copy.get().getID(), copy.get());
recordOriginal(getRow(l.getID()), l);
recordOriginal(l);
}
});
}
 
public Future<?> updateRow(final Number id, final Path path, final SQLRowValues vals) {
checkCanModif(path);
// since SQLRowValues isn't thread-safe, use AtomicReference to safely pass it to another
// thread
final AtomicReference<SQLRowValues> copy = new AtomicReference<SQLRowValues>(vals.toImmutable());
public Future<?> replaceRow(final Number id, final SQLRowValues vals) {
checkCanModif(Path.get(vals.getTable()));
final SQLRowValues copy = vals.deepCopy();
return this.updateRow(id, new IClosure<SQLRowValues>() {
@Override
public void executeChecked(SQLRowValues newVals) {
final Set<String> contentFields = newVals.getTable().getFieldsNames(VirtualFields.CONTENT);
newVals.clearReferents().removeAll(contentFields);
newVals.load(copy, contentFields);
if (copy.hasReferents()) {
for (final Entry<SQLField, Set<SQLRowValues>> e : new SetMap<SQLField, SQLRowValues>(copy.getReferentsMap()).entrySet()) {
for (final SQLRowValues ref : e.getValue()) {
ref.put(e.getKey().getName(), newVals);
}
}
}
assert copy.getGraphSize() == 1;
}
}, false);
}
 
public Future<?> updateRow(final Number id, final SQLRowValues vals) {
checkCanModif(Path.get(vals.getTable()));
if (vals.getGraphSize() > 1)
throw new IllegalArgumentException("This method doesn't merge graphs");
final SQLRowValues copy = vals.deepCopy();
return this.updateRow(id, new IClosure<SQLRowValues>() {
@Override
public void executeChecked(SQLRowValues newVals) {
final Set<String> contentFields = newVals.getTable().getFieldsNames(VirtualFields.CONTENT);
newVals.load(copy, contentFields);
}
}, false);
}
 
public Future<?> updateRow(final Number id, final IClosure<SQLRowValues> valsClosure) {
return this.updateRow(id, valsClosure, true);
}
 
private Future<?> updateRow(final Number id, final IClosure<SQLRowValues> valsClosure, final boolean mdCanChange) {
return this.getModel().getUpdateQ().put(new OfflineRunnable() {
@Override
public void run() {
final ListSQLLine l = getModel().getUpdateQ().getLine(id);
// ATTN only works if path direction is only FOREIGN (e.g. for new rows referents
// won't have an ID)
getModel().getUpdateQ().updateLine(l, path, copy.get().getID(), copy.get());
recordOriginal(getRow(id), l);
_updateRow(id, valsClosure, mdCanChange);
}
});
}
 
protected Row _updateRow(final Number id, final IClosure<SQLRowValues> valsClosure, final boolean mdCanChange) {
assert checkUpdateThread();
final Row r = getRow(id.intValue(), true);
final SQLRowValues newVals = r.getRow().deepCopy();
valsClosure.executeChecked(newVals);
// make sure PK and metadata don't change
if (mdCanChange) {
final Set<String> notContent = newVals.getTable().getFieldsNames(VirtualFields.CONTENT.complement());
newVals.removeAll(notContent);
newVals.putAll(r.getRow().getValues(notContent, false));
}
assert CompareUtils.equals(r.getRow().getIDNumber(), newVals.getIDNumber());
// make sure every needed path is there
newVals.grow(getUpdateQueueReq().getGraphToFetch(), false);
newVals.getGraph().freeze();
setRow(r, newVals);
 
// call createLine() to apply filter
this.getModel().getUpdateQ().replaceLine(r.getID().intValue(), createLine(r));
return r;
}
 
private void checkCanModif(Path path) {
if (this.modifiableVals.followPath(path) == null)
throw new IllegalArgumentException("can only modify " + this.modifiableVals);
}
 
private void recordOriginal(Row r, ListSQLLine l) {
assert r.getID().intValue() == l.getID();
// if l isn't in the db, no need to update, the new values will be inserted
private void recordOriginal(ListSQLLine l) {
setRow(getRow(l.getID(), true), l.getRow());
}
 
private void setRow(Row r, SQLRowValues newVals) {
// if the existing row isn't in the DB, no need to update, the new values will be inserted
if (r.getRow().hasID() && !this.dbVals.containsKey(r)) {
// copy the initial state
this.dbVals.put(r, r.getRow());
}
r.setRow(l.getRow());
r.setRow(newVals);
}
 
// change foreign key at the outer edge of our private graph
// e.g. if this is a CPI can change CPI.ID_LOCAL, but not LOCAL.ID_BATIMENT, and not
// CPI.ID_OBSERVATION
public void changeFK(final Number lineID, final Path p, final int id) {
checkCanModif(p.minusLast());
// Disallow modification of private
if (this.modifiableVals.followPath(p) != null)
throw new IllegalArgumentException("can only modify a foreign key of " + this.modifiableVals);
this.getModel().getUpdateQ().put(new OfflineRunnable() {
@Override
public void run() {
final ListSQLLine line = getModel().getUpdateQ().getLine(lineID);
// TODO extract updateLines() from AbstractUpdateOneRunnable and delete
// ChangeFKRunnable
new ChangeFKRunnable(line, p, id).run();
recordOriginal(getRow(lineID, true), line);
}
});
}
 
// *** Order ***
 
@Override
509,12 → 523,12
final int count = this.lines.size();
final boolean after = inc > 0;
 
final List<Number> order;
final List<Integer> order;
// same algorithm as MoveQueue
int outerIndex = -1;
final List<Row> ourLines = new ArrayList<Row>(list.size());
for (final SQLRowAccessor r : list) {
final Row ourLine = this.getRow(r.getIDNumber(), true, r);
final Row ourLine = this.getRow(r.getID(), true, r);
final int index = this.indexOf(ourLine);
ourLines.add(ourLine);
if (outerIndex < 0 || after && index > outerIndex || !after && index < outerIndex) {
533,8 → 547,8
this.getModel().getUpdateQ().reorder(order);
}
 
private List<Number> getIDsOrder() {
final List<Number> ids = new ArrayList<Number>();
private List<Integer> getIDsOrder() {
final List<Integer> ids = new ArrayList<Integer>();
for (final Row r : this.lines)
ids.add(r.getID());
return ids;
554,10 → 568,10
assert checkUpdateThread();
final int count = this.lines.size();
 
final List<Number> order;
final List<Integer> order;
final List<Row> list = new ArrayList<Row>(ids.size());
for (final Object o : ids) {
final Number id = o instanceof SQLRowAccessor ? ((SQLRowAccessor) o).getIDNumber() : (Number) o;
final Integer id = o instanceof SQLRowAccessor ? ((SQLRowAccessor) o).getID() : ((Number) o).intValue();
list.add(this.getRow(id, true, o));
}
if (index <= 0) {
616,7 → 630,7
this.deleted.clear();
this.setDBOrder(true);
 
for (final SQLRowValues r : this.fetch(null))
for (final SQLRowValues r : this.fetch())
this._add(r, false, false);
this.getModel().getUpdateQ().setFullList(getLines(), null);
}
657,6 → 671,12
}
 
protected void coreCommit() throws SQLException {
// delete. Must be done first (e.g. there's a unique constraint and a deleted row conflicts
// with a new row)
getParent().getElem().archiveIDs(this.deleted);
this.deleted.clear();
 
// ordered rows
final Map<Row, SQLRow> newRows = new LinkedHashMap<Row, SQLRow>();
// insert, copy since we will remove some of the lines
for (final Row l : this.lines) {
668,7 → 688,7
newRow = l.getRow().asRow();
}
// if the line is to be updated, this will get replaced below but it won't
// changed the ordering of the map
// change the ordering of the map
newRows.put(l, newRow);
}
 
686,10 → 706,5
if (!wantedOrder.equals(dbOrder)) {
MoveQueue.moveAtOnce(wantedOrder.subList(1, wantedOrder.size()), true, wantedOrder.get(0));
}
 
// delete
for (final Number id : this.deleted)
getParent().getElem().archive(id.intValue());
this.deleted.clear();
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelLinesSourceOnline.java
53,7 → 53,7
fireChanged(evt);
}
};
this.getReq().addWhereListener(this.listener);
this.getParent().getReq().addWhereListener(this.listener);
this.moveQ = null;
}
 
68,7 → 68,7
 
@Override
protected void die() {
this.getReq().rmWhereListener(this.listener);
this.getParent().getReq().rmWhereListener(this.listener);
 
if (this.moveQ != null) {
final RunningState threadState = this.moveQ.getRunningState();
88,14 → 88,6
return this.parent;
}
 
public final ListSQLRequest getReq() {
return this.getParent().getReq();
}
 
public final ListSQLRequest getUpdateQueueReq() {
return ((SQLTableModelSourceStateOnline) this.getModel().getUpdateQ().getState()).getReq();
}
 
@Override
public List<ListSQLLine> getAll() {
final List<SQLRowValues> values = this.getUpdateQueueReq().getValues();
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelSource.java
13,7 → 13,9
package org.openconcerto.sql.view.list;
 
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.FieldPath;
import org.openconcerto.sql.model.IFieldPath;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLFieldsSet;
import org.openconcerto.sql.model.SQLRow;
22,6 → 24,8
import org.openconcerto.sql.model.SQLRowValues.ForeignCopyMode;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.request.BaseFillSQLRequest;
import org.openconcerto.sql.request.ListSQLRequest;
import org.openconcerto.utils.cc.IClosure;
import org.openconcerto.utils.change.ListChangeIndex;
import org.openconcerto.utils.change.ListChangeRecorder;
34,6 → 38,7
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
 
import javax.swing.SwingUtilities;
 
75,7 → 80,8
}
}
 
private final SQLTable table;
private final ListSQLRequest req;
private final SQLElement elem;
// only from EDT
private SQLRowValues inited;
// this.cols + debugCols, unmodifiable
95,14 → 101,23
this.lines = new ArrayList<WeakReference<SQLTableModelLinesSource>>();
}
 
public SQLTableModelSource(SQLRowValues graph) {
this.table = graph.getTable();
public SQLTableModelSource(final ListSQLRequest req, final SQLElement elem) {
if (elem == null)
throw new IllegalArgumentException("Missing element");
this.req = req;
this.elem = elem;
if (!this.getPrimaryTable().equals(this.elem.getTable()))
throw new IllegalArgumentException("not the same table: " + this.getPrimaryTable() + " != " + this.elem);
this.setAllCols(SQLTableModelColumns.empty());
this.cols = new ListChangeRecorder<SQLTableModelColumn>(new ArrayList<SQLTableModelColumn>());
this.debugCols = new ArrayList<SQLTableModelColumn>();
this.inited = graph;
this.inited = req.getGraph();
}
 
public SQLElement getElem() {
return this.elem;
}
 
// lazy initialization since this method calls colsChanged() which subclasses overload and
// they need their own attribute that aren't set yet since super() must be the first statement.
public void init() {
117,7 → 132,7
public void executeChecked(final FieldPath input) {
final SQLField f = input.getField();
if (f.getTable().getLocalContentFields().contains(f)) {
final SQLTableModelColumnPath col = new SQLTableModelColumnPath(input);
final SQLTableModelColumnPath col = new SQLTableModelColumnPath(input, null, getElem().getDirectory());
SQLTableModelSource.this.cols.add(col);
} else
SQLTableModelSource.this.debugCols.add(new SQLTableModelColumnPath(input.getPath(), f.getName(), f.toString()) {
145,7 → 160,8
}
 
public SQLTableModelSource(SQLTableModelSource src) {
this.table = src.table;
this.req = src.req;
this.elem = src.elem;
this.setAllCols(src.getAllColumns());
this.cols = new ListChangeRecorder<SQLTableModelColumn>(new ArrayList<SQLTableModelColumn>(src.cols));
this.debugCols = new ArrayList<SQLTableModelColumn>(src.debugCols);
178,13 → 194,46
fireColsChanged(beforeState, afterState);
}
 
protected abstract SQLTableModelSourceState createState();
protected final SQLTableModelSourceState createState() {
return new SQLTableModelSourceState(this.getAllColumns(), this.getReq());
}
 
protected void colsChanged(final ListChangeIndex<SQLTableModelColumn> change) {
private final void colsChanged(final ListChangeIndex<SQLTableModelColumn> change) {
final AtomicBoolean biggerGraph = new AtomicBoolean(false);
// add needed fields for each new column
this.getReq().changeGraphToFetch(new IClosure<SQLRowValues>() {
@Override
public void executeChecked(SQLRowValues g) {
for (final SQLTableModelColumn col : change.getItemsAdded()) {
// DebugRow should uses all *fetched* fields, but since it cannot know them
// getPaths() return all fields in the table. So don't fetch all fields just for
// this debug column
if (!(col instanceof DebugRow)) {
for (final IFieldPath p : col.getPaths()) {
if (BaseFillSQLRequest.addToFetch(g, p.getPath(), Collections.singleton(p.getFieldName())))
biggerGraph.set(true);
}
}
}
}
});
if (biggerGraph.get() && !allowBiggerGraph())
throw new IllegalStateException("Bigger graph not allowed");
}
 
protected abstract boolean allowBiggerGraph();
 
private void fireColsChanged(final SQLTableModelSourceState beforeState, final SQLTableModelSourceState afterState) {
// let know each of our LinesSource that the columns have changed
for (final SQLTableModelLinesSource line : getLines()) {
line.colsChanged(beforeState, afterState);
}
// before notifying our regular listeners
this.supp.firePropertyChange("cols", null, this.cols);
}
 
private final List<SQLTableModelLinesSource> getLines() {
final List<SQLTableModelLinesSource> res = new ArrayList<SQLTableModelLinesSource>();
int i = 0;
while (i < this.lines.size()) {
final WeakReference<SQLTableModelLinesSource> l = this.lines.get(i);
192,14 → 241,17
if (line == null)
this.lines.remove(i);
else {
line.colsChanged(beforeState, afterState);
res.add(line);
i++;
}
}
// before notifying our regular listeners
this.supp.firePropertyChange("cols", null, this.cols);
return res;
}
 
protected final int getLinesCount() {
return this.getLines().size();
}
 
public final SQLTableModelLinesSource createLinesSource(ITableModel model) {
this.init();
final SQLTableModelLinesSource res = this._createLinesSource(model);
214,7 → 266,9
*
* @return the maximum graph of our lines.
*/
public abstract SQLRowValues getMaxGraph();
public final SQLRowValues getMaxGraph() {
return this.getReq().getGraphToFetch();
}
 
// * columns
 
293,8 → 347,12
 
// * SQLIdentifier
 
public final ListSQLRequest getReq() {
return this.req;
}
 
public final SQLTable getPrimaryTable() {
return this.table;
return this.getReq().getPrimaryTable();
}
 
/**
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelSourceState.java
13,17 → 13,25
package org.openconcerto.sql.view.list;
 
import org.openconcerto.sql.request.ListSQLRequest;
 
import net.jcip.annotations.Immutable;
 
@Immutable
abstract class SQLTableModelSourceState {
public final class SQLTableModelSourceState {
 
private final ListSQLRequest req;
private final SQLTableModelColumns allCols;
 
protected SQLTableModelSourceState(final SQLTableModelColumns allCols) {
protected SQLTableModelSourceState(final SQLTableModelColumns allCols, final ListSQLRequest req) {
this.req = req.toUnmodifiable();
this.allCols = allCols;
}
 
public final ListSQLRequest getReq() {
return this.req;
}
 
public final SQLTableModelColumns getAllColumns() {
return this.allCols;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelSourceOffline.java
14,8 → 14,7
package org.openconcerto.sql.view.list;
 
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
import org.openconcerto.sql.request.ListSQLRequest;
 
/**
* A SQLTableModelSource *not* directly tied to the database.
24,28 → 23,14
*/
public class SQLTableModelSourceOffline extends SQLTableModelSource {
 
private final SQLRowValuesListFetcher fetcher;
private final SQLElement elem;
 
public SQLTableModelSourceOffline(final SQLRowValuesListFetcher fetcher, final SQLElement elem) {
super(fetcher.getGraph());
this.fetcher = fetcher.toUnmodifiable();
this.elem = elem;
if (!this.getPrimaryTable().equals(this.elem.getTable()))
throw new IllegalArgumentException("not the same table: " + this.getPrimaryTable() + " != " + this.elem);
public SQLTableModelSourceOffline(final ListSQLRequest req, final SQLElement elem) {
super(req, elem);
}
 
public final SQLRowValuesListFetcher getFetcher() {
return this.fetcher;
}
 
public SQLElement getElem() {
return this.elem;
}
 
@Override
protected SQLTableModelSourceState createState() {
return new SQLTableModelSourceStateOffline(this.getAllColumns(), this.getFetcher());
protected boolean allowBiggerGraph() {
// MAYBE allow if all lines source have only committed rows (refresh them)
return this.getLinesCount() == 0;
}
 
@Override
52,9 → 37,4
protected SQLTableModelLinesSourceOffline _createLinesSource(final ITableModel model) {
return new SQLTableModelLinesSourceOffline(this, model);
}
 
@Override
public SQLRowValues getMaxGraph() {
return this.getFetcher().getGraph();
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/IListFrame.java
14,6 → 14,7
package org.openconcerto.sql.view;
 
import static org.openconcerto.utils.FileUtils.FILENAME_ESCAPER;
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.State;
import org.openconcerto.sql.element.SQLComponent;
21,8 → 22,10
import org.openconcerto.sql.view.list.ITableModel;
import org.openconcerto.ui.FrameUtil;
import org.openconcerto.ui.state.WindowStateManager;
import org.openconcerto.utils.StringUtils;
 
import java.awt.GridLayout;
import java.awt.Window;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.beans.PropertyChangeEvent;
42,27 → 45,38
public class IListFrame extends JFrame {
 
public static final String SHORT_TITLE = "org.openconcerto.listframe.shortTitle";
private static final String FILE_STRUCT_VERSION = "20160923";
 
// state-EditFrame/cpi-window.xml
// windowState-20160923/IListFrame/ARTICLE.xml
static public final File getConfigFile(final SQLElement elem, final Class<? extends JFrame> c) {
return getConfigFile(elem, null, c);
}
 
// state-EditFrame/cpi-FullComp-window.xml
// windowState-20160923/IListFrame/ARTICLE-componentCode.xml
static public final File getConfigFile(final SQLComponent comp, final Class<? extends JFrame> c) {
return getConfigFile(comp.getElement(), comp, c);
}
 
static private final File getConfigFile(final SQLElement elem, final SQLComponent comp, final Class<? extends JFrame> c) {
static private final File getConfigFile(final SQLElement elem, final SQLComponent comp, final Class<? extends Window> c) {
final String compName = comp == null ? "" : "-" + comp.getCode();
return getConfigFile(c, elem.getCode() + compName);
}
 
// windowState-20160923/WindowClass/code.xml
static public final File getConfigFile(final Class<? extends Window> c, final String code) {
final Configuration conf = Configuration.getInstance();
if (conf == null)
return null;
// no getSimpleName() since for inner classes this is empty
final String compName = comp == null ? "" : "-" + comp.getClass().getName();
final String filename = FILENAME_ESCAPER.escape(elem.getPluralName() + compName) + "-window.xml";
return new File(conf.getConfDir(), "state-" + c.getSimpleName() + File.separator + filename);
final File structFile = new File(conf.getConfDir(), "windowState-" + FILE_STRUCT_VERSION);
return new File(structFile, c.getSimpleName() + File.separator + getConfigFileName(code));
}
 
static final String getConfigFileName(String code) {
if (StringUtils.isEmpty(code, true))
code = "default";
return StringUtils.Shortener.MD5.getBoundedLengthString(FILENAME_ESCAPER.escape(code), 70) + ".xml";
}
 
private final IListPanel panel;
private String title;
 
/trunk/OpenConcerto/src/org/openconcerto/sql/view/IListPanel.java
15,6 → 15,7
 
import static javax.swing.JOptionPane.DEFAULT_OPTION;
import static javax.swing.JOptionPane.QUESTION_MESSAGE;
 
import org.openconcerto.openoffice.ContentType;
import org.openconcerto.openoffice.OOUtils;
import org.openconcerto.openoffice.XMLFormatVersion;
24,7 → 25,6
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.request.ListSQLRequest;
import org.openconcerto.sql.sqlobject.SQLRequestComboBox;
import org.openconcerto.sql.users.rights.TableAllRights;
import org.openconcerto.sql.users.rights.UserRights;
38,6 → 38,7
import org.openconcerto.ui.SwingThreadUtils;
import org.openconcerto.ui.component.JRadioButtons.JStringRadioButtons;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.StringUtils;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.Tuple2.List2;
import org.openconcerto.utils.cc.IClosure;
98,16 → 99,24
 
protected static final String FALLBACK_KEY = "FALLBACK_ACTION";
 
private static final String FILE_STRUCT_VERSION = "20161018";
 
static public final File getConfigFile(final SQLElement elem, final Class<? extends Container> c) {
return getConfigFile(elem, c, null);
}
 
static public final File getConfigFile(final SQLElement elem, final Class<? extends Container> c, final String variant) {
final String suffix = StringUtils.isEmpty(variant, true) ? "" : "-" + variant;
return getConfigFile(c, elem.getCode() + suffix);
}
 
static public final File getConfigFile(final Class<? extends Container> c, String code) {
final Configuration conf = Configuration.getInstance();
if (conf == null)
return null;
final String suffix = variant == null || variant.length() == 0 ? "" : "-" + variant;
return new File(conf.getConfDir(), "state-" + c.getSimpleName() + "-list" + File.separator + elem.getPluralName() + suffix + ".xml");
 
final File structFile = new File(conf.getConfDir(), "jtableState-" + FILE_STRUCT_VERSION);
return new File(structFile, c.getSimpleName() + File.separator + IListFrame.getConfigFileName(code));
}
 
private final IListe liste;
834,12 → 843,6
this.searchComponent.setSearchFullMode(b);
}
 
public void setRequest(ListSQLRequest req) {
if (!req.getPrimaryTable().equals(this.getElement().getTable()))
throw new IllegalArgumentException("table diff: " + req + " / " + this.getElement());
this.getListe().setRequest(req);
}
 
/**
* Récupérer les valeurs de la row sélectionnée lors de l'ajout
*
/trunk/OpenConcerto/src/org/openconcerto/sql/PropsConfiguration.java
27,8 → 27,8
import org.openconcerto.sql.model.SQLSystem;
import org.openconcerto.sql.request.SQLFieldTranslator;
import org.openconcerto.sql.users.rights.UserRightsManager;
import org.openconcerto.utils.BaseDirs;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.FileUtils;
import org.openconcerto.utils.LogUtils;
import org.openconcerto.utils.MultipleOutputStream;
43,7 → 43,6
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
122,6 → 121,10
* redirect {@link System#err} and {@link System#out}.
*/
public static final String REDIRECT_TO_FILE = "redirectToFile";
/**
* Must be one of {@link StandardStreamsDest}.
*/
public static final String STD_STREAMS_DESTINATION = "stdStreamsDest";
 
// properties cannot contain null, so to be able to override a default, a non-null value
// meaning empty must be chosen (as setProperty(name, null) is the same as remove(name) i.e.
186,12 → 189,15
private ProductInfo productInfo;
@GuardedBy("restLock")
private SQLFilter filter;
private final Addable<SQLFieldTranslator> translator;
private final Addable<SQLElementDirectory> directory;
@GuardedBy("restLock")
private File wd;
@GuardedBy("restLock")
private BaseDirs baseDirs;
@GuardedBy("restLock")
private File logDir;
private final boolean inIDE;
 
// split sql tree and the rest since creating the tree is costly
// and nodes are inter-dependant, while the rest is mostly fast
// different instances, otherwise lock every Conf instances
234,7 → 240,9
this.directory = new Addable<SQLElementDirectory>() {
@Override
protected SQLElementDirectory create() {
return createDirectory();
final SQLElementDirectory res = createDirectory();
res.setTranslator(createTranslator(res));
return res;
}
 
@Override
242,18 → 250,7
obj.putAll(conf.getDirectory());
}
};
// SQLFieldTranslator is thread-safe
this.translator = new Addable<SQLFieldTranslator>() {
@Override
protected SQLFieldTranslator create() {
return createTranslator();
}
 
@Override
protected void add(SQLFieldTranslator obj, Configuration conf) {
obj.putAll(conf.getTranslator());
}
};
this.inIDE = Boolean.getBoolean("inIDE");
this.setUp();
}
 
271,7 → 268,6
UserRightsManager.clearInstanceIfSame(this.urMngr);
}
 
this.translator.destroy();
this.directory.destroy();
super.destroy();
}
291,6 → 287,10
throw new IllegalStateException("Destroyed");
}
 
public final boolean isInIDE() {
return this.inIDE;
}
 
public final String getProperty(final String name) {
return this.props.getProperty(name);
}
438,9 → 438,15
if (!hasWANProperties(wanAddr, wanPort))
return doCreateServer();
 
final String serverPropVal = getProperty("server.ip");
// TODO add and use server.port
final List<String> serverAndPort = Arrays.asList(serverPropVal.split(":"));
if (serverAndPort.size() != 2)
throw new IllegalStateException("Not in 'host:port' format : " + serverPropVal);
 
// if wanAddr is specified, always include it in ID, that way if we connect through the LAN
// or through the WAN we have the same ID
final String serverID = "tunnel to " + wanAddr + ":" + wanPort + " then " + getProperty("server.ip");
final String serverID = "tunnel to " + wanAddr + ":" + wanPort + " then " + serverPropVal;
final Logger log = Log.get();
Exception origExn = null;
final SQLServer defaultServer;
465,10 → 471,8
log.info("ssl connection to " + this.conn.getHost() + ":" + this.conn.getPort());
final int localPort = NetUtils.findFreePort(5436);
try {
// TODO add and use server.port
final String[] serverAndPort = getProperty("server.ip").split(":");
log.info("ssl tunnel from local port " + localPort + " to remote " + serverAndPort);
this.conn.setPortForwardingL(localPort, serverAndPort[0], Integer.valueOf(serverAndPort[1]));
this.conn.setPortForwardingL(localPort, serverAndPort.get(0), Integer.valueOf(serverAndPort.get(1)));
} catch (final Exception e1) {
throw new IllegalStateException("Impossible de créer la liaison sécurisée. Vérifier que le logiciel n'est pas déjà lancé.", e1);
}
686,14 → 690,28
this.setupLogging("logs");
}
 
public void setupLogging(final String dirName) {
this.setupLogging(dirName, Boolean.getBoolean(REDIRECT_TO_FILE));
public final void setupLogging(final String dirName) {
this.setupLogging(dirName, getStandardStreamsDestination());
}
 
protected boolean keepStandardStreamsWhenRedirectingToFile() {
return true;
protected final StandardStreamsDest getStandardStreamsDestination() {
String propVal = System.getProperty(STD_STREAMS_DESTINATION);
if (propVal == null)
propVal = this.getProperty(STD_STREAMS_DESTINATION);
 
final StandardStreamsDest res;
if (propVal != null)
res = StandardStreamsDest.valueOf(propVal);
else
res = getStandardStreamsDestinationDefault();
return res;
}
 
// used if neither system property nor configuration property are set
protected StandardStreamsDest getStandardStreamsDestinationDefault() {
return Boolean.getBoolean(REDIRECT_TO_FILE) ? StandardStreamsDest.ALSO_TO_FILE : StandardStreamsDest.DEFAULT;
}
 
protected DateFormat getLogDateFormat() {
return DATE_FORMAT;
}
722,7 → 740,20
return logDir;
}
 
public void setupLogging(final String dirName, final boolean redirectToFile) {
static public enum StandardStreamsDest {
DEFAULT(true), ONLY_TO_FILE(false), ALSO_TO_FILE(true);
private final boolean hasDefaultStreams;
 
private StandardStreamsDest(boolean hasDefaultStreams) {
this.hasDefaultStreams = hasDefaultStreams;
}
 
public final boolean hasDefaultStreams() {
return this.hasDefaultStreams;
}
}
 
public final void setupLogging(final String dirName, final StandardStreamsDest stdRedirect) {
final File logDir;
synchronized (this.restLock) {
if (this.logDir != null)
733,26 → 764,22
final String logNameBase = this.getAppName() + "_" + getLogDateFormat().format(new Date());
 
// must be done before setUpConsoleHandler(), otherwise log output not redirected
if (redirectToFile) {
if (stdRedirect != StandardStreamsDest.DEFAULT) {
final File logFile = new File(logDir, (logNameBase + ".txt"));
try {
FileUtils.mkdir_p(logFile.getParentFile());
System.out.println("Log file: " + logFile.getAbsolutePath());
System.out.println("Standard output and error file: " + logFile.getAbsolutePath());
final OutputStream fileOut = new FileOutputStream(logFile, true);
final OutputStream out, err;
System.out.println("Java System console:" + System.console());
boolean launchedFromEclipse = new File(".classpath").exists();
if (launchedFromEclipse) {
System.out.println("Launched from eclipse");
}
if ((System.console() != null || launchedFromEclipse) && this.keepStandardStreamsWhenRedirectingToFile()) {
if (this.isInIDE() || stdRedirect != StandardStreamsDest.ONLY_TO_FILE) {
System.out.println("Redirecting standard output to file and console");
out = new MultipleOutputStream(fileOut, new FileOutputStream(FileDescriptor.out));
out = new MultipleOutputStream(fileOut, System.out);
System.out.println("Redirecting error output to file and console");
err = new MultipleOutputStream(fileOut, new FileOutputStream(FileDescriptor.err));
 
err = new MultipleOutputStream(fileOut, System.err);
} else {
System.out.println("Redirecting standard output to file");
out = fileOut;
System.out.println("Redirecting error output to file");
err = fileOut;
}
System.setErr(new PrintStream(new BufferedOutputStream(err, 128), true));
824,12 → 851,12
return Arrays.asList("mapping", "mapping-" + this.getProperty("customer"));
}
 
protected SQLFieldTranslator createTranslator() {
protected SQLFieldTranslator createTranslator(final SQLElementDirectory dir) {
final List<String> mappings = getMappings();
if (mappings.size() == 0)
throw new IllegalStateException("empty mappings");
 
final SQLFieldTranslator trns = new SQLFieldTranslator(this.getRoot(), null, this.getDirectory());
final SQLFieldTranslator trns = new SQLFieldTranslator(this.getRoot(), null, dir);
// perhaps listen to UserProps (as in TM)
return loadTranslations(trns, this.getRoot(), mappings);
}
868,6 → 895,10
return new File(this.getProperty("wd"));
}
 
protected BaseDirs createBaseDirs() {
return BaseDirs.create(getProductInfo(), getAppVariant());
}
 
// *** add
 
/**
879,7 → 910,6
*/
@Override
public final Configuration add(final Configuration conf) {
this.translator.add(conf);
this.directory.add(conf);
return this;
}
1066,7 → 1096,7
 
@Override
public final SQLFieldTranslator getTranslator() {
return this.translator.get();
return this.getDirectory().getTranslator();
}
 
@Override
1098,6 → 1128,15
}
}
 
@Override
public BaseDirs getBaseDirs() {
synchronized (this.restLock) {
if (this.baseDirs == null)
this.baseDirs = this.createBaseDirs();
return this.baseDirs;
}
}
 
// *** setters
 
// MAYBE add synchronized (not necessary since they're private, and only called with the lock)
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/SoftwareInfoPanel.java
22,6 → 22,7
import org.openconcerto.sql.users.rights.UserRightsManager;
import org.openconcerto.ui.FormLayouter;
import org.openconcerto.ui.component.HTMLTextField;
import org.openconcerto.utils.BaseDirs;
import org.openconcerto.utils.ProductInfo;
import org.openconcerto.utils.SystemInfo;
import org.openconcerto.utils.cc.IFactory;
87,9 → 88,16
if (propsConf != null && propsConf.isUsingSSH()) {
res.put(Info.SECURE_LINK, propsConf.getWanHostAndPort());
}
if (conf != null)
res.put(Info.DB_URL, conf.getSystemRoot().getDataSource().getUrl());
if (conf != null) {
final String logs = propsConf == null ? "" : " ; " + SystemInfo.getLink(TM.tr("infoPanel.logs"), propsConf.getLogDir().toURI(), html);
res.put(Info.DIRS, SystemInfo.getLink(TM.tr("infoPanel.docs"), conf.getWD().toURI(), html) + logs);
final BaseDirs baseDirs = conf.getBaseDirs();
String dirs = " ; " + SystemInfo.getLink(TM.tr("infoPanel.dataDir"), baseDirs.getAppDataFolder().toURI(), html);
dirs = dirs + " ; " + SystemInfo.getLink(TM.tr("infoPanel.prefsDir"), baseDirs.getPreferencesFolder().toURI(), html);
dirs = dirs + " ; " + SystemInfo.getLink(TM.tr("infoPanel.cacheDir"), baseDirs.getCacheFolder().toURI(), html);
res.put(Info.DIRS, SystemInfo.getLink(TM.tr("infoPanel.docs"), conf.getWD().toURI(), html) + logs + dirs);
}
 
return res;
}
116,7 → 124,11
if (secureLink != null) {
this.l.add(TM.tr("infoPanel.secureLink"), new JLabel(secureLink));
}
this.l.add(TM.tr("infoPanel.dbURL"), new JLabel(infos.get(Info.DB_URL)));
this.l.add(TM.tr("infoPanel.dirs"), new HTMLTextField(infos.get(Info.DIRS)));
final JLabel dbURL = new JLabel(infos.get(Info.DB_URL));
if (dbURL != null)
this.l.add(TM.tr("infoPanel.dbURL"), dbURL);
final String dirs = infos.get(Info.DIRS);
if (dirs != null)
this.l.add(TM.tr("infoPanel.dirs"), new HTMLTextField(dirs));
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/SearchInfo.java
13,6 → 13,7
package org.openconcerto.sql.ui.light;
 
import org.openconcerto.sql.Log;
import org.openconcerto.sql.view.search.SearchList;
import org.openconcerto.sql.view.search.TextSearchSpec;
import org.openconcerto.sql.view.search.TextSearchSpec.Mode;
23,6 → 24,7
import java.util.List;
 
public class SearchInfo {
// TODO: add notion of operator
private final SearchList list = new SearchList();
private final List<String> texts = new ArrayList<String>();
 
32,7 → 34,7
final SearchContent param = params.getContent().get(i);
final String col = param.getColumn();
final String type = param.getType();
final String text = param.getText();
final String[] tTexts = param.getText().split(" ");
 
Mode mode = Mode.CONTAINS;
if (type.equals("contains")) {
47,12 → 49,13
throw new IllegalArgumentException("mode " + type + " not supported");
}
 
System.err.println("SearchInfo.SearchInfo() column:" + col + "type:" + type + " text:" + text);
System.err.println("SearchInfo.SearchInfo() ignoring column " + col);
for (final String text : tTexts) {
this.list.addSearchItem(new TextSearchSpec(text, mode));
this.texts.add(text);
Log.get().info("searching column:" + col + "type:" + type + " text:" + text);
}
}
}
 
public List<String> getTexts() {
return this.texts;
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/LightAutoCompleteComboBox.java
New file
0,0 → 1,104
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.sql.ui.light;
 
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.ComboSQLRequest;
import org.openconcerto.sql.sqlobject.IComboSelectionItem;
import org.openconcerto.ui.light.LightUIComboBox;
import org.openconcerto.ui.light.LightUIComboBoxElement;
import org.openconcerto.utils.io.JSONConverter;
 
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
 
import net.minidev.json.JSONObject;
 
public class LightAutoCompleteComboBox extends LightUIComboBox {
private static final String FILTER = "filter";
 
private final static Pattern QUERY_SPLIT_PATTERN = Pattern.compile("\\s+");
 
private String filter;
 
private ComboSQLRequest request;
 
public LightAutoCompleteComboBox(final JSONObject json) {
super(json);
}
 
public LightAutoCompleteComboBox(final String id) {
super(id);
this.setType(TYPE_AUTOCOMPLETE_COMBOBOX);
}
 
public void setComboRequest(final ComboSQLRequest request) {
this.request = request;
this.setFilter("");
this.setAlreadyFilled(true);
}
 
public ComboSQLRequest getComboRequest() {
return this.request;
}
 
public String getFilter() {
return this.filter;
}
 
public void setFilter(final String filter) {
this.filter = filter;
this.applyFilter();
}
 
private void applyFilter() {
if (this.request != null) {
Integer selectedId = null;
if (this.hasSelectedValue()) {
selectedId = this.getSelectedValue().getId();
}
 
this.clearValues();
if (this.hasNotSpecifedLine()) {
this.addValue(LightUIComboBox.getDefaultValue());
}
 
final Where where = this.hasSelectedValue() ? new Where(this.request.getPrimaryTable().getKey(), "=", this.getSelectedValue().getId()) : null;
final List<IComboSelectionItem> items = this.request.getComboItems(true, Arrays.asList(QUERY_SPLIT_PATTERN.split(this.filter)), Locale.getDefault(), where);
 
System.err.println("LightAutoCompleteComboBox.applyFilter() - items count: " + items.size());
for (final IComboSelectionItem item : items) {
this.addValue(new LightUIComboBoxElement(item.getId(), item.getLabel()));
}
 
this.setSelectedId(selectedId);
}
}
 
@Override
public JSONObject toJSON() {
final JSONObject json = super.toJSON();
json.put(FILTER, this.filter);
return json;
}
 
@Override
public void fromJSON(final JSONObject json) {
super.fromJSON(json);
 
this.filter = JSONConverter.getParameterFromJSON(json, FILTER, String.class);
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/LightUIPanelFiller.java
15,25 → 15,23
 
import org.openconcerto.sql.Log;
import org.openconcerto.sql.PropsConfiguration;
import org.openconcerto.sql.model.FieldPath;
import org.openconcerto.sql.model.Constraint;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLSyntax.ConstraintType;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.sqlobject.ElementComboBoxUtils;
import org.openconcerto.sql.request.ComboSQLRequest;
import org.openconcerto.sql.sqlobject.IComboSelectionItem;
import org.openconcerto.ui.light.LightUIComboBox;
import org.openconcerto.ui.light.LightUIComboBoxElement;
import org.openconcerto.ui.light.LightUIElement;
import org.openconcerto.ui.light.LightUILine;
import org.openconcerto.ui.light.LightUIPanel;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.io.JSONConverter;
 
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Set;
 
/**
* Fill value from default or database
64,11 → 62,11
this.fillFromRow(this.panel, configuration, row);
}
 
private void fillFromRow(final LightUIPanel panel, final PropsConfiguration configuration, SQLRowAccessor row) {
private void fillFromRow(final LightUIPanel panel, final PropsConfiguration configuration, SQLRowAccessor sqlRow) {
final int panelChildCount = panel.getChildrenCount();
// Convert as sqlrow if possible to get all values from db
if (row.hasID()) {
row = row.asRow();
if (sqlRow.hasID()) {
sqlRow = sqlRow.asRow();
}
for (int i = 0; i < panelChildCount; i++) {
final LightUILine panelChild = panel.getChild(i, LightUILine.class);
75,55 → 73,86
final int lineChildCount = panelChild.getChildrenCount();
for (int j = 0; j < lineChildCount; j++) {
final LightUIElement element = panelChild.getChild(j);
final SQLField field = configuration.getFieldMapper().getSQLFieldForItem(element.getId());
final SQLField sqlField = configuration.getFieldMapper().getSQLFieldForItem(element.getId());
 
SQLRowAccessor sqlRowTmp = this.getSQLRowForField(sqlRow, sqlField);
if (sqlRowTmp == null) {
throw new IllegalArgumentException("Impossible to reach the field: " + sqlField.getName() + " from table " + sqlRow.getTable().getName());
}
 
int type = element.getType();
if (type == LightUIElement.TYPE_TEXT_FIELD || type == LightUIElement.TYPE_TEXT_AREA) {
 
if (field == null) {
if (sqlField == null) {
Log.get().severe("No field found for text field : " + element.getId());
continue;
}
element.setValue(row.getString(field.getName()));
} else if (type == LightUIElement.TYPE_COMBOBOX) {
element.setValue(sqlRowTmp.getString(sqlField.getName()));
} else if (sqlField != null && sqlField.isKey() && (type == LightUIElement.TYPE_COMBOBOX || type == LightUIElement.TYPE_AUTOCOMPLETE_COMBOBOX)) {
// send: id,value
final LightUIComboBox combo = (LightUIComboBox) element;
if (!combo.isFillFromConvertor()) {
SQLTable foreignTable = field.getForeignTable();
final List<SQLField> fieldsToFetch = configuration.getDirectory().getElement(foreignTable).getComboRequest().getFields();
 
if (row.getObject(field.getName()) != null) {
final Where where = new Where(foreignTable.getKey(), "=", row.getForeignID(field.getName()));
final SQLRowValues graph = ElementComboBoxUtils.getGraphToFetch(configuration, foreignTable, fieldsToFetch);
final List<Tuple2<Path, List<FieldPath>>> expanded = ElementComboBoxUtils.expandGroupBy(graph, configuration.getDirectory());
List<SQLRowValues> fetchedRows = ElementComboBoxUtils.fetchRows(graph, where);
if (fetchedRows.size() > 1) {
throw new IllegalStateException("multiple rows fetched, id: " + ((row.hasID()) ? row.getID() : "undefined") + " table: " + row.getTable().getName());
LightUIComboBoxElement value = null;
final Number foreignID = sqlRowTmp.getForeignIDNumber(sqlField.getName());
if (foreignID != null) {
final SQLTable foreignTable = sqlField.getForeignTable();
final ComboSQLRequest req = configuration.getDirectory().getElement(foreignTable).getComboRequest();
final IComboSelectionItem comboItem = req.getComboItem(foreignID.intValue());
if (comboItem != null) {
value = new LightUIComboBoxElement(comboItem.getId());
value.setValue1(comboItem.getLabel());
}
 
for (final SQLRowValues vals : fetchedRows) {
LightUIComboBoxElement value = ElementComboBoxUtils.createLightUIItem(expanded, vals);
}
combo.setSelectedValue(value);
}
} else {
element.setValue(null);
}
}
} else if (type == LightUIElement.TYPE_CHECKBOX) {
if (row.getBoolean(field.getName())) {
if (sqlRowTmp.getObject(sqlField.getName()) != null && sqlRowTmp.getBoolean(sqlField.getName())) {
element.setValue("true");
} else {
element.setValue("false");
}
} else if (type == LightUIElement.TYPE_DATE) {
Calendar date = row.getDate(field.getName());
Calendar date = sqlRowTmp.getDate(sqlField.getName());
if (date != null) {
element.setValue(JSONConverter.getJSON(date).toString());
}
} else if (type == LightUIElement.TYPE_PANEL) {
this.fillFromRow((LightUIPanel) element, configuration, row);
this.fillFromRow((LightUIPanel) element, configuration, sqlRowTmp);
} else if (type == LightUIElement.TYPE_SLIDER) {
final Integer value = sqlRowTmp.getInt(sqlField.getName());
if (value != null) {
element.setValue(value.toString());
}
}
}
}
}
 
public SQLRowAccessor getSQLRowForField(final SQLRowAccessor sqlRow, final SQLField sqlField) {
SQLRowAccessor sqlRowResult = sqlRow;
if (sqlField != null && !sqlField.getTable().getName().equals(sqlRow.getTable().getName())) {
sqlRowResult = this.findSQLRow(sqlRow, sqlField);
}
return sqlRowResult;
}
 
public SQLRowAccessor findSQLRow(final SQLRowAccessor sqlRow, final SQLField sqlField) {
final Set<Constraint> constraints = sqlRow.getTable().getAllConstraints();
for (final Constraint constraint : constraints) {
if (constraint.getType().equals(ConstraintType.FOREIGN_KEY)) {
// FIXME: this doesn't work when foreign key is composed of more than one field
final String firstFkCols = constraint.getCols().get(0);
final SQLRowAccessor fkRow = sqlRow.getForeign(firstFkCols);
if (fkRow != null) {
if (fkRow.getTable().getName().equals(sqlField.getTable().getName())) {
return fkRow;
} else {
final SQLRowAccessor sqlRowResult = this.findSQLRow(fkRow, sqlField);
if (sqlRowResult != null) {
return sqlRowResult;
}
}
}
}
}
return null;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/LightForeignRowValuesTableOffline.java
New file
0,0 → 1,91
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.sql.ui.light;
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.view.list.ITableModel;
import org.openconcerto.sql.view.list.SQLTableModelLinesSourceOffline;
import org.openconcerto.ui.light.LightUIElement;
import org.openconcerto.ui.light.SearchSpec;
import org.openconcerto.ui.light.TableContent;
 
import java.util.concurrent.Future;
 
import net.minidev.json.JSONObject;
 
public class LightForeignRowValuesTableOffline extends LightRowValuesTable {
 
private SQLField foreignField;
private Number parentRowId;
 
public LightForeignRowValuesTableOffline(final Configuration configuration, final Number userId, final String id, final ITableModel model, final SQLField foreignField, final Number parentRowId) {
super(configuration, userId, id, model);
 
this.foreignField = foreignField;
this.parentRowId = parentRowId;
this.init();
}
 
public LightForeignRowValuesTableOffline(final LightForeignRowValuesTableOffline table) {
super(table);
 
this.foreignField = table.foreignField;
this.parentRowId = table.parentRowId;
this.init();
}
 
private final void init() {
if (this.getTableSpec().getContent() == null) {
this.getTableSpec().setContent(new TableContent(this.getId()));
}
}
 
public final SQLField getForeignField() {
return this.foreignField;
}
 
public final Number getParentRowId() {
return this.parentRowId;
}
 
public Future<?> commitRows() {
return ((SQLTableModelLinesSourceOffline) this.getModel().getLinesSource()).commit();
}
 
public void addNewRow(final SQLRowValues sqlRow) {
((SQLTableModelLinesSourceOffline) this.getModel().getLinesSource()).add(sqlRow);
}
 
@Override
public void doSearch(final Configuration configuration, final SearchSpec searchSpec, final int offset) {
// TODO: Implement search in offline table
this.getModel().fireTableRowsInserted(0, Integer.MAX_VALUE);
}
 
@Override
public LightUIElement clone() {
return new LightForeignRowValuesTableOffline(this);
}
 
@Override
public void fromJSON(final JSONObject json) {
super.fromJSON(json);
 
if (this.getTableSpec().getContent() != null) {
this.getTableSpec().getContent().clearRows();
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/LightForeignRowValuesTableOnline.java
New file
0,0 → 1,51
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.sql.ui.light;
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.view.list.ITableModel;
import org.openconcerto.ui.light.LightUIElement;
 
public class LightForeignRowValuesTableOnline extends LightRowValuesTableOnline {
private SQLField foreignField;
private Number parentRowId;
 
public LightForeignRowValuesTableOnline(final Configuration configuration, final Number userId, final String id, final ITableModel model, final SQLField foreignField, final Number parentRowId) {
super(configuration, userId, id, model);
 
this.foreignField = foreignField;
this.parentRowId = parentRowId;
}
 
public LightForeignRowValuesTableOnline(final LightForeignRowValuesTableOnline table) {
super(table);
 
this.foreignField = table.foreignField;
this.parentRowId = table.parentRowId;
}
 
public final SQLField getForeignField() {
return this.foreignField;
}
 
public final Number getParentRowId() {
return this.parentRowId;
}
 
@Override
public LightUIElement clone() {
return new LightForeignRowValuesTableOnline(this);
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/GroupToLightUIConvertor.java
18,7 → 18,7
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.FieldMapper;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.request.RowItemDesc;
import org.openconcerto.sql.view.EditPanel.EditMode;
import org.openconcerto.ui.group.Group;
26,7 → 26,6
import org.openconcerto.ui.group.LayoutHints;
import org.openconcerto.ui.light.CustomEditorProvider;
import org.openconcerto.ui.light.LightUICheckBox;
import org.openconcerto.ui.light.LightUIComboBox;
import org.openconcerto.ui.light.LightUIDate;
import org.openconcerto.ui.light.LightUIElement;
import org.openconcerto.ui.light.LightUIFrame;
38,6 → 37,8
import org.openconcerto.utils.i18n.TranslationManager;
 
import java.awt.Color;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;
48,7 → 49,6
private PropsConfiguration configuration;
private FieldMapper mapper;
private Map<String, CustomEditorProvider> customEditorProviders = new HashMap<String, CustomEditorProvider>();
private Map<String, ConvertorModifer> modifers = new HashMap<String, ConvertorModifer>();
 
public GroupToLightUIConvertor(PropsConfiguration conf) {
this(conf, 4);
63,7 → 63,7
}
}
 
public LightEditFrame convert(final Group group, final SQLRowValues defaultRow, final LightUIFrame parentFrame, final EditMode editMode) {
public LightEditFrame convert(final Group group, final SQLRowAccessor defaultRow, final LightUIFrame parentFrame, final EditMode editMode) {
if (group == null) {
throw new IllegalArgumentException("Null Group");
}
76,66 → 76,69
throw new IllegalArgumentException("This group isn't attached to this SQLElement, group ID: " + group.getId() + " element code: " + sqlElement.getCode());
}
 
final LightEditFrame editFrame = new LightEditFrame(this.configuration, group, defaultRow, parentFrame, editMode);
final LightEditFrame editFrame = new LightEditFrame(this.configuration, group, defaultRow.asRowValues(), parentFrame, editMode);
final LightUIPanel framePanel = editFrame.getFirstChild(LightUIPanel.class);
append(framePanel, group);
append(sqlElement, framePanel, group);
 
String frameTitle = TranslationManager.getInstance().getTranslationForItem(group.getId());
if (frameTitle == null) {
frameTitle = group.getId();
}
editFrame.setTitle(frameTitle);
 
editFrame.createTitlePanel(frameTitle);
 
Log.get().warning("No translation for " + group.getId());
return editFrame;
}
 
private void append(final LightUIPanel panel, final Item item) {
private void append(final SQLElement sqlElement, final LightUIPanel panel, final Item item) {
if (item instanceof Group) {
final Group gr = (Group) item;
int size = gr.getSize();
 
final String groupTitle = TranslationManager.getInstance().getTranslationForItem(gr.getId());
final LightUIPanel childPanel = new LightUIPanel(gr.getId());
childPanel.setFillWidth(true);
childPanel.setGridWidth(4);
 
if (gr.getLocalHint().isFoldable()) {
final LightUIPanel childPanel = new LightUIPanel(gr.getId());
childPanel.setTitle(groupTitle);
childPanel.setFoldable(true);
childPanel.setGridWidth(4);
childPanel.setFillWidth(true);
for (int i = 0; i < size; i++) {
this.append(childPanel, gr.getItem(i));
}
if (this.modifers.containsKey(gr.getId())) {
this.modifers.get(gr.getId()).process(childPanel);
}
final LightUILine line = new LightUILine();
line.addChild(childPanel);
panel.addChild(line);
} else {
if (groupTitle != null) {
final LightUILine titleLine = new LightUILine();
final LightUILabel titleLabel = new LightUILabel(gr.getId() + ".title.label");
titleLabel.setGridWidth(4);
titleLabel.setFontBold(true);
titleLabel.setLabel(groupTitle);
titleLabel.setFillWidth(true);
titleLine.addChild(titleLabel);
panel.addChild(titleLine);
childPanel.addChild(titleLine);
final LightUILine line = new LightUILine();
panel.addChild(line);
childPanel.addChild(line);
}
}
 
for (int i = 0; i < size; i++) {
final Item it = gr.getItem(i);
this.append(panel, it);
this.append(sqlElement, childPanel, gr.getItem(i));
}
}
 
final LightUILine line = new LightUILine();
line.addChild(childPanel);
panel.addChild(line);
} else {
final LayoutHints localHint = item.getLocalHint();
LightUILine currentLine = panel.getLastLine();
 
if (currentLine.getTotalGridWidth() >= 4) {
currentLine = new LightUILine();
panel.addChild(currentLine);
}
 
currentLine.setMarginTop(1);
currentLine.setMarginBottom(1);
if (localHint.isSeparated()) {
if (currentLine.getWidth() > 0) {
if (currentLine.getChildrenCount() > 0) {
currentLine = new LightUILine();
panel.addChild(currentLine);
}
148,7 → 151,7
currentLine.setWeightY(1);
}
 
if (currentLine.getWidth() >= this.maxColumnCount) {
if (currentLine.getChildrenCount() >= this.maxColumnCount) {
currentLine = new LightUILine();
panel.addChild(currentLine);
}
155,28 → 158,21
 
final SQLField field = this.mapper.getSQLFieldForItem(item.getId());
LightUILabel elementLabel = null;
 
String label = this.getLabelForItem(field, item);
if (label == null) {
label = item.getId();
Log.get().warning("No translation for " + item.getId());
}
 
if (localHint.showLabel()) {
currentLine.setElementPadding(5);
 
elementLabel = new LightUILabel(item.getId() + ".label");
elementLabel.setHorizontalAlignement(LightUIElement.HALIGN_RIGHT);
String label = TranslationManager.getInstance().getTranslationForItem(item.getId());
 
if (label == null && field != null) {
final RowItemDesc desc = this.configuration.getTranslator().getDescFor(field.getTable(), field.getName());
if (desc != null) {
label = desc.getLabel();
}
}
 
if (label == null) {
label = item.getId();
elementLabel.setBackgroundColor(Color.ORANGE);
elementLabel.setToolTip("No translation for " + item.getId());
Log.get().warning("No translation for " + item.getId());
}
 
elementLabel.setLabel(label);
elementLabel.setWeightX(0);
if (localHint.isSplit()) {
elementLabel.setHorizontalAlignement(LightUIElement.HALIGN_LEFT);
if (currentLine.getChildrenCount() != 0) {
195,8 → 191,9
if (field != null) {
Class<?> javaType = field.getType().getJavaType();
if (field.isKey()) {
elementEditor = new LightUIComboBox(item.getId());
elementEditor = new LightAutoCompleteComboBox(item.getId());
elementEditor.setMinInputSize(20);
elementEditor.setValueType(LightUIElement.VALUE_TYPE_REF);
} else if (javaType.equals(String.class)) {
if (field.getType().getSize() > 1000) {
elementEditor = new LightUITextArea(item.getId());
207,15 → 204,24
elementEditor.setValue("");
elementEditor.setMinInputSize(10);
}
elementEditor.setValueType(LightUIElement.VALUE_TYPE_STRING);
} else if (javaType.equals(Boolean.class)) {
elementEditor = new LightUICheckBox(item.getId(), "");
elementEditor.setLabel(label);
elementEditor.setValueType(LightUIElement.VALUE_TYPE_BOOLEAN);
elementLabel.setLabel("");
} else if (javaType.equals(Date.class)) {
elementEditor = new LightUIDate(item.getId());
} else if (javaType.equals(Boolean.class)) {
elementEditor = new LightUICheckBox(item.getId(), "");
elementEditor.setValueType(LightUIElement.VALUE_TYPE_DATE);
} else if (javaType.equals(Timestamp.class)) {
elementEditor = new LightUIDate(item.getId());
} else if (javaType.equals(Integer.class)) {
elementEditor.setValueType(LightUIElement.VALUE_TYPE_DATE);
} else if (javaType.equals(Integer.class) || javaType.equals(Long.class) || javaType.equals(Short.class) || javaType.equals(BigInteger.class)) {
elementEditor = new LightUITextField(item.getId());
elementEditor.setValueType(LightUIElement.VALUE_TYPE_INTEGER);
} else if (javaType.equals(BigDecimal.class) || javaType.equals(Float.class) || javaType.equals(Double.class)) {
elementEditor = new LightUITextField(item.getId());
elementEditor.setValueType(LightUIElement.VALUE_TYPE_DECIMAL);
} else {
elementEditor = new LightUITextField(item.getId());
Log.get().warning("unsupported type " + javaType.getName());
225,6 → 231,7
elementEditor = new LightUITextField(item.getId());
elementEditor.setMinInputSize(10);
elementEditor.setToolTip("No field attached to " + item.getId());
elementEditor.setValueType(LightUIElement.VALUE_TYPE_STRING);
Log.get().warning("No field attached to " + item.getId());
if (elementLabel != null) {
elementLabel.setBackgroundColor(Color.ORANGE);
235,12 → 242,10
 
if (elementEditor != null) {
elementEditor.setWeightX(1);
if (this.modifers.containsKey(item.getId())) {
this.modifers.get(item.getId()).process(elementEditor);
}
}
 
if (localHint.isSplit()) {
if (currentLine.getWidth() > 0) {
if (currentLine.getTotalGridWidth() > 0) {
currentLine = new LightUILine();
panel.addChild(currentLine);
}
259,11 → 264,24
elementEditor.setGridWidth(1);
}
elementEditor.setFillWidth(localHint.fillWidth());
elementEditor.setMarginBottom(4);
currentLine.addChild(elementEditor);
 
}
}
 
private String getLabelForItem(final SQLField field, final Item item) {
String label = TranslationManager.getInstance().getTranslationForItem(item.getId());
 
if (label == null && field != null) {
final RowItemDesc desc = this.configuration.getTranslator().getDescFor(field.getTable(), field.getName());
if (desc != null) {
label = desc.getLabel();
}
}
return label;
}
 
private LightUIElement getCustomEditor(final String id) {
final CustomEditorProvider customEditorProvider = this.customEditorProviders.get(id);
if (customEditorProvider != null) {
283,12 → 301,4
public void putAllCustomEditorProvider(final Map<String, CustomEditorProvider> map) {
this.customEditorProviders.putAll(map);
}
 
public void addModifer(final String itemId, final ConvertorModifer modifer) {
this.modifers.put(itemId, modifer);
}
 
public void addAllModifer(final Map<String, ConvertorModifer> modifers) {
this.modifers.putAll(modifers);
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/LightRowValuesTable.java
14,211 → 14,309
package org.openconcerto.sql.ui.light;
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.ListSQLRequest;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.view.list.ITableModel;
import org.openconcerto.sql.view.list.ListSQLLine;
import org.openconcerto.sql.view.list.SQLTableModelColumn;
import org.openconcerto.sql.view.list.SQLTableModelSourceOnline;
import org.openconcerto.ui.SwingThreadUtils;
import org.openconcerto.ui.light.ColumnSpec;
import org.openconcerto.ui.light.ColumnsSpec;
import org.openconcerto.ui.light.LightUIElement;
import org.openconcerto.ui.light.LightUITable;
import org.openconcerto.ui.light.Row;
import org.openconcerto.ui.light.TableContent;
import org.openconcerto.utils.io.JSONConverter;
import org.openconcerto.ui.light.SearchSpec;
import org.openconcerto.utils.NumberUtils;
import org.openconcerto.utils.cc.ITransformer;
 
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
 
import net.minidev.json.JSONArray;
import javax.swing.SwingUtilities;
import javax.swing.event.TableModelListener;
 
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.DOMBuilder;
 
import net.minidev.json.JSONObject;
 
public class LightRowValuesTable extends LightUITable {
List<SQLRowValues> listRowValues = new ArrayList<SQLRowValues>();
public abstract class LightRowValuesTable extends LightUITable {
 
String fieldRefName;
private SQLRowAccessor refRow;
private boolean autoCommit = false;
private List<Integer> deletedIds = new ArrayList<Integer>();
public static final int MAX_LINE_TO_SEND = 100;
 
// Init from json constructor
public LightRowValuesTable(final JSONObject json) {
super(json);
this.init();
private int totalRowCount = -1;
 
private int offset = 0;
 
private ITableModel model;
 
private final ITransformer<SQLSelect, SQLSelect> orginTransformer;
 
private List<TableModelListener> tableModelListeners = new ArrayList<TableModelListener>();
 
public LightRowValuesTable(final Configuration configuration, final Number userId, final String id, final ITableModel model) {
super(id);
 
this.model = model;
final ColumnsSpec columnsSpec = this.createColumnsSpecFromModelSource(configuration, userId);
 
this.getTableSpec().setColumns(columnsSpec);
 
if (model.getReq() instanceof SQLTableModelSourceOnline) {
final SQLTableModelSourceOnline source = (SQLTableModelSourceOnline) model.getReq();
this.orginTransformer = source.getReq().getSelectTransf();
} else {
this.orginTransformer = null;
}
 
this.setDynamicLoad(true);
}
 
// Clone constructor
public LightRowValuesTable(final LightRowValuesTable tableElement) {
super(tableElement);
this.listRowValues = tableElement.listRowValues;
this.fieldRefName = tableElement.fieldRefName;
this.deletedIds = tableElement.deletedIds;
this.init();
 
this.model = tableElement.model;
this.totalRowCount = tableElement.totalRowCount;
this.orginTransformer = tableElement.orginTransformer;
}
 
public LightRowValuesTable(final LightUITable table, final String fieldRefName) {
super(table);
this.fieldRefName = fieldRefName;
this.init();
// Json constructor
public LightRowValuesTable(final JSONObject json) {
super(json);
this.orginTransformer = null;
}
 
public String getFieldRefName() {
return this.fieldRefName;
public final int getTotalRowsCount() {
return this.totalRowCount;
}
 
public SQLRowValues getRowValues(final int index) {
return this.listRowValues.get(index);
public final int getOffset() {
return this.offset;
}
 
public int getRowValuesCount() {
return this.listRowValues.size();
public final void setOffset(final int offset) {
this.offset = offset;
}
 
public boolean isAutoCommit() {
return this.autoCommit;
public final void addTableModelListener(final TableModelListener tableModelListener) {
this.tableModelListeners.add(tableModelListener);
this.model.addTableModelListener(tableModelListener);
}
 
public void setAutoCommit(boolean autoCommit) {
if (autoCommit) {
if (this.refRow == null) {
throw new IllegalArgumentException("Set parent row before put this table in auto commit mode");
public final void removeTableModelListener(final TableModelListener tableModelListener) {
this.tableModelListeners.remove(tableModelListener);
this.model.removeTableModelListener(tableModelListener);
}
 
public ITableModel getModel() {
return this.model;
}
this.autoCommit = autoCommit;
}
 
/**
* Permet de charger les lignes et de lier les nouvelles lignes
*
*/
public void setParentSQLRow(final Configuration configuration, final SQLElement sqlElement, final SQLRowAccessor sqlRow) {
 
final TableContent content = new TableContent(getId());
content.setRows(new ArrayList<Row>());
getTableSpec().setContent(content);
this.refRow = sqlRow;
this.refetchTable(configuration, sqlElement);
public final LightListSqlRow getRowFromSqlID(final Number sqlID) {
if (this.hasRow()) {
final int size = this.getTableSpec().getContent().getRowsCount();
for (int i = 0; i < size; i++) {
final LightListSqlRow row = (LightListSqlRow) this.getRow(i);
if (NumberUtils.areNumericallyEqual(row.getSqlRow().getIDNumber(), sqlID)) {
return row;
}
}
}
return null;
}
 
public void refetchTable(final Configuration configuration, final SQLElement sqlElement) {
this.getTableSpec().getContent().getRows().clear();
this.listRowValues.clear();
if (this.refRow != null && !this.refRow.isUndefined()) {
final SQLTableModelSourceOnline tableSource = sqlElement.getTableSource(true);
public final LightListSqlRow createLightListRowFromListLine(final ListSQLLine listSqlLine, final int index) throws IllegalStateException {
final ColumnsSpec columnsSpec = this.getTableSpec().getColumns();
final List<SQLTableModelColumn> sqlColumns = this.getModelColumns();
final int colSize = sqlColumns.size();
 
final ListSQLRequest req = tableSource.getReq();
req.setWhere(new Where(sqlElement.getTable().getField(getFieldRefName()), "=", this.refRow.getID()));
List<SQLRowValues> listRowValues = req.getValues();
final LightListSqlRow row = new LightListSqlRow(listSqlLine.getRow(), listSqlLine.getID());
final List<Object> values = new ArrayList<Object>();
for (int i = 0; i < colSize; i++) {
final String columnId = columnsSpec.getColumn(i).getId();
final SQLTableModelColumn col = getColumnFromId(sqlColumns, columnId);
 
for (final SQLRowValues rowValues : listRowValues) {
this.addRowValues(configuration, rowValues);
if (col != null) {
Object value = col.show(row.getSqlRow());
if (col.getLightUIrenderer() != null) {
value = col.getLightUIrenderer().getLightUIElement(value, 0, i);
}
values.add(value);
} else {
throw new IllegalArgumentException("column " + columnId + " is in ColumnsSpec but it is not found in SQLTableModelColumn");
}
}
row.setValues(values);
 
public SQLRowAccessor getRefRow() {
return this.refRow;
return row;
}
 
public void clearRowValues(){
this.getTableSpec().getContent().getRows().clear();
this.listRowValues.clear();
public final SQLRowAccessor getFirstSelectedSqlRow() {
final List<Row> selectedRows = this.getSelectedRows();
if (selectedRows.isEmpty()) {
return null;
} else {
return ((LightListSqlRow) selectedRows.get(0)).getSqlRow();
}
public void removeRowValuesAt(int index) {
final TableContent content = this.getTableSpec().getContent();
content.getRows().remove(index);
this.listRowValues.remove(index);
}
 
public void addRowValues(final Configuration configuration, final SQLRowValues rowValues) {
final TableContent content = this.getTableSpec().getContent();
this.listRowValues.add(rowValues);
 
content.getRows().add(this.createRowFromRowValues(configuration, rowValues, this.listRowValues.size() - 1));
private final List<SQLTableModelColumn> getModelColumns() {
try {
// TODO: clean swing
return SwingThreadUtils.call(new Callable<List<SQLTableModelColumn>>() {
@Override
public List<SQLTableModelColumn> call() throws Exception {
return LightRowValuesTable.this.getModel().getReq().getColumns();
}
 
public void setRowValues(final Configuration configuration, final SQLRowValues rowValues, final int index) {
final TableContent content = this.getTableSpec().getContent();
this.listRowValues.set(index, rowValues);
content.getRows().set(index, this.createRowFromRowValues(configuration, rowValues, index));
});
} catch (final Exception ex) {
throw new IllegalStateException(ex);
}
}
 
public void archiveDeletedRows(final Configuration configuration) {
final SQLElement sqlElement = configuration.getDirectory().getElementForCode(this.getElementCode());
for (final Integer deletedId : this.deletedIds) {
try {
sqlElement.archive(deletedId);
} catch (final SQLException ex) {
throw new IllegalArgumentException(ex.getMessage(), ex);
private final SQLTableModelColumn getColumnFromId(final List<SQLTableModelColumn> allCols, final String columnId) {
final int columnSize = allCols.size();
for (int i = 0; i < columnSize; i++) {
final SQLTableModelColumn tableModelColumn = allCols.get(i);
if (tableModelColumn.getIdentifier().equals(columnId)) {
return tableModelColumn;
}
}
return null;
}
 
public void addDeletedId(final int idToDelete) {
this.deletedIds.add(idToDelete);
}
/**
* Get columns user preferences for a specific table
*
* @param configuration - The user SQL configuration
* @param userId - Id of the user who want view the table
*
* @return the XML which contains user preferences
*
* @throws IllegalArgumentException
* @throws IllegalStateException
*
*/
// TODO: move in LightUITable, maybe move LightUITable in FrameWork_SQL
private final Document getColumnsSpecUserPerfs(final Configuration configuration, final Number userId) throws IllegalArgumentException, IllegalStateException {
Document columnsPrefs = null;
final DOMBuilder in = new DOMBuilder();
org.w3c.dom.Document w3cDoc = null;
 
protected Row createRowFromRowValues(final Configuration configuration, final SQLRowValues sqlRow, final int index) {
final SQLElement element = configuration.getDirectory().getElementForCode(this.getElementCode());
if (element == null) {
throw new IllegalArgumentException("Unable to find element for code: " + this.getElementCode());
w3cDoc = configuration.getXMLConf(userId, this.getId());
if (w3cDoc != null) {
columnsPrefs = in.build(w3cDoc);
}
return columnsPrefs;
}
 
final SQLTableModelSourceOnline tableSource = element.getTableSource(true);
final List<SQLTableModelColumn> allCols = tableSource.getColumns();
/**
* Create ColumnsSpec from list of SQLTableModelColumn and apply user preferences
*
* @param configuration - current SQL configuration of user
* @param userId - Id of user
*
* @return New ColumnsSpec with user preferences application
*
* @throws IllegalArgumentException
* @throws IllegalStateException
*/
private final ColumnsSpec createColumnsSpecFromModelSource(final Configuration configuration, final Number userId) throws IllegalArgumentException, IllegalStateException {
final List<String> possibleColumnIds = new ArrayList<String>();
final List<String> sortedIds = new ArrayList<String>();
final List<ColumnSpec> columnsSpec = new ArrayList<ColumnSpec>();
 
final Row row = element.createRowFromSQLRow(sqlRow, allCols, this.getTableSpec().getColumns());
row.setId(index);
return row;
final List<SQLTableModelColumn> columns = this.getModelColumns();
final int columnsCount = columns.size();
 
for (int i = 0; i < columnsCount; i++) {
final SQLTableModelColumn sqlColumn = columns.get(i);
// TODO : creer la notion d'ID un peu plus dans le l'esprit sales.invoice.amount
final String columnId = sqlColumn.getIdentifier();
 
possibleColumnIds.add(columnId);
Class<?> valueClass = sqlColumn.getValueClass();
if (sqlColumn.getLightUIrenderer() != null) {
valueClass = LightUIElement.class;
}
 
private void init() {
if (this.getTableSpec().getContent() == null) {
this.getTableSpec().setContent(new TableContent(this.getId()));
columnsSpec.add(new ColumnSpec(columnId, valueClass, sqlColumn.getName(), null, false, null));
}
 
// TODO : recuperer l'info sauvegardée sur le serveur par user (à coder)
sortedIds.add(columnsSpec.get(0).getId());
 
final ColumnsSpec cSpec = new ColumnsSpec(this.getId(), columnsSpec, possibleColumnIds, sortedIds);
cSpec.setAllowMove(true);
cSpec.setAllowResize(true);
 
final Document xmlColumnsPref = this.getColumnsSpecUserPerfs(configuration, userId);
if (!cSpec.setUserPrefs(xmlColumnsPref)) {
configuration.removeXMLConf(userId, this.getId());
}
 
@Override
public LightUIElement clone() {
return new LightRowValuesTable(this);
return cSpec;
}
 
// TODO: merge with OpenConcerto List search system
public abstract void doSearch(final Configuration configuration, final SearchSpec searchSpec, final int offset);
 
@Override
public JSONObject toJSON() {
final JSONObject json = super.toJSON();
 
// TODO: implement row values JSONAble
if (this.listRowValues != null && !this.listRowValues.isEmpty()) {
json.put("list-row-values", null);
public Row getRowById(Number rowId) {
for (int i = 0; i < this.model.getRowCount(); i++) {
if (NumberUtils.areNumericallyEqual(this.model.getRow(i).getID(), rowId)) {
return this.createLightListRowFromListLine(this.model.getRow(i), i);
}
if (this.deletedIds != null && !this.deletedIds.isEmpty()) {
json.put("deleted-ids", JSONConverter.getJSON(this.deletedIds));
}
 
return json;
return super.getRowById(rowId);
}
 
/**
* Create default columns preferences from the SQLTableModelLinesSourceOnline
*
* @return XML document with columns preferences
*/
@Override
public void fromJSON(final JSONObject json) {
super.fromJSON(json);
final JSONArray jsonListRowValues = JSONConverter.getParameterFromJSON(json, "list-row-values", JSONArray.class);
this.listRowValues = new ArrayList<SQLRowValues>();
// TODO: implement row values JSONAble
if (jsonListRowValues != null) {
public Document createDefaultXmlPreferences() {
final Element rootElement = new Element("list");
final List<SQLTableModelColumn> columns = this.getModelColumns();
 
final int sqlColumnsCount = columns.size();
for (int i = 0; i < sqlColumnsCount; i++) {
final SQLTableModelColumn sqlColumn = columns.get(i);
final String columnId = sqlColumn.getIdentifier();
final ColumnSpec columnSpec = new ColumnSpec(columnId, sqlColumn.getValueClass(), sqlColumn.getName(), null, false, null);
final Element columnElement = this.createXmlColumn(columnId, columnSpec.getMaxWidth(), columnSpec.getMinWidth(), columnSpec.getWidth());
rootElement.addContent(columnElement);
}
final Document xmlConf = new Document(rootElement);
 
if (this.getTableSpec().getContent() != null) {
this.getTableSpec().getContent().getRows().clear();
return xmlConf;
}
 
final JSONArray jsonDeletedIds = JSONConverter.getParameterFromJSON(json, "deleted-ids", JSONArray.class);
this.deletedIds = new ArrayList<Integer>();
if (jsonDeletedIds != null) {
for (final Object jsonDeletedId : jsonDeletedIds) {
this.deletedIds.add(JSONConverter.getObjectFromJSON(jsonDeletedId, Integer.class));
@Override
public void destroy() {
super.destroy();
 
// TODO: clean swing
try {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
final List<TableModelListener> tableModelListeners = LightRowValuesTable.this.tableModelListeners;
for (int i = tableModelListeners.size() - 1; i > -1; i--) {
LightRowValuesTable.this.removeTableModelListener(tableModelListeners.get(i));
}
}
});
} catch (final Exception ex) {
throw new IllegalStateException(ex);
}
 
this.clearRows();
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/LightEditFrame.java
20,12 → 20,12
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.view.EditPanel.EditMode;
import org.openconcerto.sql.view.list.ListSQLLine;
import org.openconcerto.sql.view.list.SQLTableModelLinesSourceOffline;
import org.openconcerto.ui.group.Group;
import org.openconcerto.ui.group.Item;
import org.openconcerto.ui.light.ComboValueConvertor;
import org.openconcerto.ui.light.CustomEditorProvider;
import org.openconcerto.ui.light.JSONToLightUIConvertor;
import org.openconcerto.ui.light.LightUICheckBox;
33,13 → 33,14
import org.openconcerto.ui.light.LightUIDate;
import org.openconcerto.ui.light.LightUIElement;
import org.openconcerto.ui.light.LightUIFrame;
import org.openconcerto.ui.light.StringValueConvertor;
import org.openconcerto.utils.io.JSONConverter;
 
import java.math.BigDecimal;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.Future;
 
import net.minidev.json.JSONObject;
 
87,10 → 88,13
/**
* Commit the SQLRowValues attached to this frame
*
* @param configuration Current configuration
* @param configuration - Current configuration
*
* @return The inserted SQLRow
*
* @throws SQLException When an error occur in SQLRowValues.commit()
*/
public SQLRow commitSqlRow(final Configuration configuration) {
public SQLRow commitSqlRow(final Configuration configuration) throws SQLException {
if (this.editMode.equals(EditMode.READONLY)) {
throw new IllegalArgumentException("Impossible to commit values when the frame is read only");
}
98,7 → 102,7
try {
return this.sqlRow.prune(sqlElement.getPrivateGraph()).commit();
} catch (final SQLException ex) {
throw new IllegalArgumentException("Unable to commit SQLRowValues, edit frame ID: " + this.getId());
throw ex;
}
}
 
120,14 → 124,14
* @param conf
* @param userId
*/
public void updateRow(final Configuration configuration, final int userId) {
public void updateRow(final Configuration configuration, final String sessionSecurityToken, final int userId) {
if (this.editMode.equals(EditMode.READONLY)) {
throw new IllegalArgumentException("Impossible to update values when the frame is read only");
}
this.updateRow(configuration, this.group, userId);
this.updateRow(configuration, this.group, sessionSecurityToken, userId);
}
 
private void updateRow(final Configuration configuration, final Group group, final int userId) {
private void updateRow(final Configuration configuration, final Group group, final String sessionSecurityToken, final int userId) {
final FieldMapper fieldMapper = configuration.getFieldMapper();
if (fieldMapper == null) {
throw new IllegalStateException("null field mapper");
134,26 → 138,25
}
 
final SQLElement sqlElement = configuration.getDirectory().getElement(this.sqlRow.getTable());
final Map<String, ComboValueConvertor<?>> valueConvertors = sqlElement.getComboConvertors();
 
Map<String, CustomEditorProvider> customEditors = null;
final Map<String, CustomEditorProvider> customEditors;
if (this.editMode.equals(EditMode.CREATION)) {
customEditors = sqlElement.getCustomEditorProviderForCreation(configuration, userId);
customEditors = sqlElement.getCustomEditorProviderForCreation(configuration, sessionSecurityToken);
} else {
customEditors = sqlElement.getCustomEditorProviderForModification(configuration, this.sqlRow, userId);
customEditors = sqlElement.getCustomEditorProviderForModification(configuration, this.sqlRow, sessionSecurityToken);
}
 
this.createRowValues(configuration, fieldMapper, this.group, valueConvertors, customEditors);
this.createRowValues(configuration, sqlElement, fieldMapper, this.group, customEditors);
this.setMetaData(userId);
}
 
final protected void createRowValues(final Configuration conf, final FieldMapper fieldMapper, final Group group, final Map<String, ComboValueConvertor<?>> valueConvertors,
final protected void createRowValues(final Configuration configuration, final SQLElement sqlElement, final FieldMapper fieldMapper, final Group group,
final Map<String, CustomEditorProvider> customEditors) {
final int itemCount = group.getSize();
for (int i = 0; i < itemCount; i++) {
final Item item = group.getItem(i);
if (item instanceof Group) {
this.createRowValues(conf, fieldMapper, (Group) item, valueConvertors, customEditors);
this.createRowValues(configuration, sqlElement, fieldMapper, (Group) item, customEditors);
} else {
final SQLField field = fieldMapper.getSQLFieldForItem(item.getId());
if (field != null) {
163,28 → 166,10
throw new IllegalArgumentException("Impossible to find UI Element with id: " + item.getId());
}
 
if (!valueConvertors.containsKey(item.getId()) && !customEditors.containsKey(item.getId())) {
this.putValueFromUserControl(conf, field, uiElement);
} else if (valueConvertors.containsKey(item.getId())) {
if (!(uiElement instanceof LightUIComboBox)) {
throw new IllegalArgumentException("The UI Element with ID " + item.getId() + ", must be an instance of LightUIComboBox");
if (!uiElement.isNotSaved()) {
this.putValueFromUserControl(configuration, sqlElement, field, uiElement, customEditors);
}
final LightUIComboBox combo = (LightUIComboBox) uiElement;
if (combo.hasSelectedValue() && combo.getSelectedValue().getId() != 0) {
final ComboValueConvertor<?> valueConvertor = valueConvertors.get(item.getId());
if (valueConvertor instanceof StringValueConvertor) {
this.sqlRow.put(field.getFieldName(), ((StringValueConvertor) valueConvertor).getIdFromIndex(combo.getSelectedValue().getId()));
} else {
final int selectedId = combo.getSelectedValue().getId();
this.sqlRow.put(field.getFieldName(), selectedId);
}
} else {
this.sqlRow.put(field.getFieldName(), null);
}
} else if (customEditors.containsKey(item.getId())) {
Log.get().warning("Unable to save value of element: " + item.getId());
}
} else {
Log.get().warning("No field attached to " + item.getId());
}
}
191,59 → 176,79
}
}
 
final protected void putValueFromUserControl(final Configuration conf, final SQLField field, final LightUIElement uiElement) {
final Class<?> fieldType = field.getType().getJavaType();
if (field.isKey()) {
final protected void putValueFromUserControl(final Configuration configuration, final SQLElement sqlElement, final SQLField sqlField, final LightUIElement uiElement,
final Map<String, CustomEditorProvider> customEditors) {
if (!uiElement.isNotSaved()) {
final Class<?> fieldType = sqlField.getType().getJavaType();
if (customEditors.containsKey(uiElement.getId())) {
final CustomEditorProvider customEditor = customEditors.get(uiElement.getId());
if (customEditor instanceof SavableCustomEditorProvider) {
((SavableCustomEditorProvider) customEditor).save(this.sqlRow, sqlField, uiElement);
}
} else {
final String fieldName = sqlField.getFieldName();
if (sqlField.isKey()) {
if (!(uiElement instanceof LightUIComboBox)) {
throw new IllegalArgumentException("Invalid UI Element for field: " + field.getName() + ". When field is foreign key, UI Element must be a LightUIDate");
throw new IllegalArgumentException("Invalid UI Element for field: " + fieldName + ". When field is foreign key, UI Element must be a LightUIDate");
}
final LightUIComboBox combo = (LightUIComboBox) uiElement;
 
if (combo.hasSelectedValue()) {
final SQLRowValues tmp = new SQLRowValues(this.sqlRow.getTable()).put(field.getName(), null);
conf.getDirectory().getShowAs().expand(tmp);
final SQLRowValues toFetch = (SQLRowValues) tmp.getForeign(field.getName());
tmp.remove(field.getName());
 
final SQLRowValues fetched = SQLRowValuesListFetcher.create(toFetch).fetchOne(combo.getSelectedValue().getId());
if (fetched == null) {
throw new IllegalArgumentException("Impossible to find Row in database - table: " + field.getForeignTable().getName() + ", id: " + combo.getSelectedValue().getId());
this.sqlRow.put(fieldName, combo.getSelectedValue().getId());
} else {
this.sqlRow.put(fieldName, null);
}
 
this.sqlRow.put(field.getFieldName(), fetched);
} else {
this.sqlRow.put(field.getFieldName(), null);
}
} else if (fieldType.equals(String.class)) {
this.sqlRow.put(field.getFieldName(), uiElement.getValue());
final String value = uiElement.getValue();
if (value == null && !sqlField.isNullable()) {
Log.get().warning("ignoring null value for not nullable field " + fieldName + " from table " + sqlField.getTable().getName());
} else {
if (fieldType.equals(String.class)) {
// FIXME check string size against field size
this.sqlRow.put(fieldName, value);
} else if (fieldType.equals(Date.class)) {
if (!(uiElement instanceof LightUIDate)) {
throw new IllegalArgumentException("Invalid UI Element for field: " + field.getName() + ". When field is Date, UI Element must be a LightUIDate");
throw new IllegalArgumentException("Invalid UI Element for field: " + fieldName + ". When field is Date, UI Element must be a LightUIDate");
}
this.sqlRow.put(field.getFieldName(), ((LightUIDate) uiElement).getValueAsDate());
this.sqlRow.put(fieldName, ((LightUIDate) uiElement).getValueAsDate());
} else if (fieldType.equals(Boolean.class)) {
if (!(uiElement instanceof LightUICheckBox)) {
throw new IllegalArgumentException("Invalid UI Element for field: " + field.getName() + ". When field is Boolean, UI Element must be a LightUICheckBox");
throw new IllegalArgumentException("Invalid UI Element for field: " + fieldName + ". When field is Boolean, UI Element must be a LightUICheckBox");
}
this.sqlRow.put(field.getFieldName(), ((LightUICheckBox) uiElement).isChecked());
this.sqlRow.put(fieldName, ((LightUICheckBox) uiElement).isChecked());
} else if (fieldType.equals(Timestamp.class)) {
if (!(uiElement instanceof LightUIDate)) {
throw new IllegalArgumentException("Invalid UI Element for field: " + field.getName() + ". When field is Date, UI Element must be a LightUIDate");
throw new IllegalArgumentException("Invalid UI Element for field: " + fieldName + ". When field is Date, UI Element must be a LightUIDate");
}
this.sqlRow.put(field.getFieldName(), ((LightUIDate) uiElement).getValueAsDate());
this.sqlRow.put(fieldName, ((LightUIDate) uiElement).getValueAsDate());
} else if (fieldType.equals(Integer.class)) {
if (uiElement.getValue() != null && !uiElement.getValue().trim().isEmpty()) {
if (!uiElement.getValue().matches("^-?\\d+$")) {
throw new IllegalArgumentException("Invalid value for field: " + field.getName() + " value: " + uiElement.getValue());
if (value != null && !value.trim().isEmpty()) {
if (!value.matches("^-?\\d+$")) {
throw new IllegalArgumentException("Invalid value for field: " + fieldName + " value: " + value);
}
this.sqlRow.put(field.getFieldName(), Integer.parseInt(uiElement.getValue()));
this.sqlRow.put(fieldName, Integer.parseInt(value));
} else {
this.sqlRow.put(field.getFieldName(), null);
this.sqlRow.put(fieldName, null);
}
} else if (fieldType.equals(Double.class) || fieldType.equals(Float.class) || fieldType.equals(BigDecimal.class)) {
if (value != null && !value.trim().isEmpty()) {
try {
this.sqlRow.put(fieldName, new BigDecimal(value));
} catch (final Exception ex) {
throw new IllegalArgumentException("Invalid value for field: " + fieldName + " value: " + value);
}
 
} else {
Log.get().warning("unsupported type " + fieldType.getName());
this.sqlRow.put(fieldName, null);
}
} else {
Log.get().warning("unsupported type " + fieldName);
}
}
}
}
}
}
 
/**
* Save all referent rows store in LightRowValuesTable
253,45 → 258,40
* @param row Element saved row
* @param customEditors List of custom editors used in element edit frame
*/
final public void saveReferentRows(final Configuration configuration, final SQLRow parentSqlRow, final Map<String, CustomEditorProvider> customEditors) {
this.saveReferentRows(configuration, this.group, parentSqlRow, customEditors);
final public void saveReferentRows(final Configuration configuration, final SQLRow parentSqlRow, final Map<String, CustomEditorProvider> customEditors, final String sessionSecurityToken) {
this.saveReferentRows(configuration, this.group, parentSqlRow, customEditors, sessionSecurityToken);
}
 
final private void saveReferentRows(final Configuration configuration, final Group group, final SQLRow parentSqlRow, final Map<String, CustomEditorProvider> customEditors) {
final private void saveReferentRows(final Configuration configuration, final Group group, final SQLRow parentSqlRow, final Map<String, CustomEditorProvider> customEditors,
final String sessionSecurityToken) {
for (int i = 0; i < group.getSize(); i++) {
final Item item = group.getItem(i);
if (item instanceof Group) {
this.saveReferentRows(configuration, (Group) item, parentSqlRow, customEditors);
this.saveReferentRows(configuration, (Group) item, parentSqlRow, customEditors, sessionSecurityToken);
} else if (customEditors.containsKey(item.getId())) {
final LightUIElement element = this.findChild(item.getId(), false);
if (element instanceof LightRowValuesTable) {
final LightRowValuesTable rowValsTable = (LightRowValuesTable) element;
final int rowValuesCount = rowValsTable.getRowValuesCount();
for (int j = 0; j < rowValuesCount; j++) {
SQLRowValues rowValues = rowValsTable.getRowValues(j);
if (!rowValues.isFrozen()) {
rowValues.put(rowValsTable.getFieldRefName(), parentSqlRow.getID());
if (element instanceof LightForeignRowValuesTableOffline) {
final LightForeignRowValuesTableOffline foreignTable = (LightForeignRowValuesTableOffline) element;
for (int j = 0; j < foreignTable.getRowsCount(); j++) {
final ListSQLLine listLine = foreignTable.getModel().getRow(j);
final SQLRowValues rowVals = listLine.getRow().createEmptyUpdateRow();
rowVals.put(foreignTable.getForeignField().getName(), parentSqlRow.getID());
((SQLTableModelLinesSourceOffline) foreignTable.getModel().getLinesSource()).updateRow(listLine.getID(), rowVals);
}
final Future<?> fCommit = foreignTable.commitRows();
 
try {
final SQLElement el = configuration.getDirectory().getElement(rowValues.getTable());
boolean insertion = !rowValues.hasID();
SQLRow rowInserted = rowValues.prune(el.getPrivateGraph()).commit();
if (insertion) {
((SQLElement) el).doAfterLightInsert(rowInserted);
fCommit.get();
} catch (final Exception ex) {
throw new IllegalArgumentException(ex);
}
} catch (SQLException e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
}
}
rowValsTable.archiveDeletedRows(configuration);
}
}
}
}
 
final protected void setMetaData(final int userId) {
final SQLTable sqlTable = this.sqlRow.getTable();
// FIXME: Creation user not specified.
if (this.sqlRow.getObject(sqlTable.getCreationUserField().getName()) == null || this.sqlRow.getObject(sqlTable.getCreationDateField().getName()) == null) {
this.sqlRow.put(sqlTable.getCreationUserField().getName(), userId);
this.sqlRow.put(sqlTable.getCreationDateField().getName(), new Date());
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/SavableCustomEditorProvider.java
New file
0,0 → 1,31
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.sql.ui.light;
 
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.ui.light.CustomEditorProvider;
import org.openconcerto.ui.light.LightUIElement;
 
public abstract class SavableCustomEditorProvider extends CustomEditorProvider {
 
public final void save(final SQLRowValues sqlRow, final SQLField sqlField, final LightUIElement uiElement) throws IllegalArgumentException, IllegalStateException {
if (sqlRow == null || sqlField == null) {
throw new IllegalStateException("Impossible to save this editor: " + uiElement.getId());
}
this._save(sqlRow, sqlField, uiElement);
}
 
protected abstract void _save(final SQLRowValues sqlRow, final SQLField sqlField, final LightUIElement uiElement) throws IllegalArgumentException, IllegalStateException;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/LightListSqlRow.java
New file
0,0 → 1,30
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.sql.ui.light;
 
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.ui.light.Row;
 
public class LightListSqlRow extends Row {
final private SQLRowValues sqlRow;
 
public LightListSqlRow(final SQLRowValues sqlRow, final Number id) {
super(id);
this.sqlRow = sqlRow;
}
 
public SQLRowValues getSqlRow() {
return this.sqlRow;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/LightRowValuesTableOnline.java
New file
0,0 → 1,147
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.sql.ui.light;
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLFunctionField;
import org.openconcerto.sql.model.SQLFunctionField.SQLFunction;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.ListSQLRequest;
import org.openconcerto.sql.view.list.ITableModel;
import org.openconcerto.sql.view.list.SQLTableModelColumn;
import org.openconcerto.sql.view.list.SQLTableModelSource;
import org.openconcerto.sql.view.list.SQLTableModelSourceOnline;
import org.openconcerto.ui.light.SearchSpec;
import org.openconcerto.utils.cc.ITransformer;
 
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
 
import javax.swing.SwingUtilities;
 
import net.minidev.json.JSONObject;
 
public class LightRowValuesTableOnline extends LightRowValuesTable {
private final ITransformer<SQLSelect, SQLSelect> orginTransformer;
 
public LightRowValuesTableOnline(final Configuration configuration, final Number userId, final String id, final ITableModel model) {
super(configuration, userId, id, model);
 
this.orginTransformer = ((SQLTableModelSourceOnline) model.getReq()).getReq().getSelectTransf();
}
 
// Clone constructor
public LightRowValuesTableOnline(final LightRowValuesTableOnline tableElement) {
super(tableElement);
 
this.orginTransformer = tableElement.orginTransformer;
}
 
// Json constructor
public LightRowValuesTableOnline(final JSONObject json) {
super(json);
this.orginTransformer = null;
}
 
@Override
public void doSearch(final Configuration configuration, final SearchSpec searchSpec, final int offset) {
this.getTableSpec().setSearch(searchSpec);
 
this.setOffset(offset);
 
final SQLTableModelSource tableSource = this.getModel().getReq();
final SearchInfo sInfo = (searchSpec != null) ? new SearchInfo(searchSpec) : null;
 
final FutureTask<ListSQLRequest> f = new FutureTask<ListSQLRequest>(new Callable<ListSQLRequest>() {
@Override
public ListSQLRequest call() throws Exception {
final ListSQLRequest req = tableSource.getReq();
final List<SQLTableModelColumn> columns = tableSource.getColumns();
req.setSelectTransf(new ITransformer<SQLSelect, SQLSelect>() {
@Override
public SQLSelect transformChecked(final SQLSelect sel) {
if (LightRowValuesTableOnline.this.orginTransformer != null) {
LightRowValuesTableOnline.this.orginTransformer.transformChecked(sel);
}
setWhere(sel, sInfo, columns);
return sel;
}
});
return req;
}
});
 
// TODO: clean swing
SwingUtilities.invokeLater(f);
 
try {
final ListSQLRequest req = f.get();
 
// get values
long t4 = System.currentTimeMillis();
final List<SQLRowValues> rowValues = req.getValues();
final int size = rowValues.size();
long t5 = System.currentTimeMillis();
 
System.err.println("DefaultTableContentHandler.handle() getValues() :" + size + " : " + (t5 - t4) + " ms");
} catch (final Exception e) {
throw new IllegalStateException(e);
}
}
 
/**
* Apply filter on ListSQLRequest
*
* @param sel - The ListSQLRequest select
* @param sInfo - Search parameters
*/
private final void setWhere(final SQLSelect sel, final SearchInfo sInfo, final List<SQLTableModelColumn> cols) {
if (sInfo != null) {
final Set<SQLField> fields = new HashSet<SQLField>();
for (final SQLTableModelColumn sqlTableModelColumn : cols) {
fields.addAll(sqlTableModelColumn.getFields());
}
final List<Where> wheres = new ArrayList<Where>();
final List<Where> wFields = new ArrayList<Where>();
 
final List<String> texts = sInfo.getTexts();
for (String string : texts) {
wFields.clear();
for (final SQLField sqlField : fields) {
if (sqlField.getType().getJavaType().equals(String.class)) {
final Where w = new Where(new SQLFunctionField(SQLFunction.LOWER, sel.getAlias(sqlField)), "LIKE", "%" + string.toLowerCase() + "%");
wFields.add(w);
}
}
wheres.add(Where.or(wFields));
}
 
final Where w;
if (sel.getWhere() != null) {
w = Where.and(sel.getWhere(), Where.and(wheres));
} else {
w = Where.and(wheres);
}
sel.setWhere(w);
System.err.println(sel.asString());
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/Login.java
138,7 → 138,7
return res;
}
 
private final List<SQLRow> findUser(final String login) {
public final List<SQLRow> findUser(final String login) {
final SQLSelect selUser = new SQLSelect();
selUser.addSelect(this.userT.getField("ID"));
selUser.addSelect(this.userT.getField("PASSWORD"));
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/RadioButtons.java
48,7 → 48,8
public RadioButtons(String colName) {
super();
this.colName = colName;
this.tm = Transformer.nopTransformer();
// ATTN javac 1.6 cannot infer the generic type
this.tm = Transformer.<String> nopTransformer();
}
 
public final RadioButtons initLocalization(final TM tm) {
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/ConnexionPanel.java
149,6 → 149,9
 
public ConnexionPanel(final Runnable r, final JImage imageLogo, final boolean societeSelector, final boolean allowStoredPass) {
super();
if (r == null) {
throw new IllegalArgumentException("null runnable");
}
this.login = new Login(Configuration.getInstance().getRoot());
 
this.societeSelector = societeSelector;
372,14 → 375,10
 
private boolean areFieldsValidated() {
if (this.societeSelector) {
final SQLRow selectedRow = this.comboSociete.getSelectedRow();
// don't use isData() since it calls isArchived() and since ComboRequest doesn't include
// ARCHIVE field this triggers a request
if (selectedRow == null || selectedRow.isUndefined()) {
if (this.comboSociete.isEmpty()) {
return false;
}
}
 
if (this.textLogin == null || this.textLogin.isEmpty() || this.textPassWord == null) {
return false;
} else {
486,6 → 485,7
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
ConnexionPanel.this.reloadPanel.setMode(ReloadPanel.MODE_BLINK);
JOptionPane.showMessageDialog(ConnexionPanel.this, TM.getTM().translate("loginPanel." + error, userName));
// Guillaume wants this for the Nego
492,7 → 492,10
if (Login.UNKNOWN_USER.equals(error))
ConnexionPanel.this.textLogin.setValue(ConnexionPanel.this.adminLogin);
setConnecting(false);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/ElementComboBoxUtils.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/SQLRequestComboBox.java
23,6 → 23,7
import org.openconcerto.sql.request.ComboSQLRequest.KeepMode;
import org.openconcerto.sql.request.SQLForeignRowItemView;
import org.openconcerto.sql.request.SQLRowItemView;
import org.openconcerto.sql.view.list.ITableModel.SleepState;
import org.openconcerto.sql.view.search.SearchSpec;
import org.openconcerto.ui.FontUtils;
import org.openconcerto.ui.component.ComboLockedMode;
228,7 → 229,7
// CloturePayeMensuellePanel)
SwingUtilities.invokeLater(new Runnable() {
public void run() {
SQLRequestComboBox.this.req.setOnScreen(true);
updateListeners();
}
});
}
235,7 → 236,7
 
@Override
public void ancestorRemoved(AncestorEvent event) {
SQLRequestComboBox.this.req.setOnScreen(false);
updateListeners();
}
 
@Override
288,25 → 289,14
 
this.uiLayout();
 
// Initialise UI : mode was set in the constructor, but the UI wasn't updated (since it
// is
// either not created or depending on the request). Do it before setRunning() since it
// might
// trigger setEnabled() and the one below would be unnecessary.
// Initialise UI : mode attribute was set in the constructor, but the UI wasn't updated
// since it wasn't created (and lacking the request). If updateEnabled() doesn't set the
// mode, set it with the value set in the constructor.
if (!updateEnabled())
this.setInteractionMode(this.getInteractionMode());
 
// *without* : resetValue() => doUpdateAll() since it was never filled
// then this is made displayable => setRunning(true) => dirty = true since not on screen
// finally made visible => setOnScreen(true) => second doUpdateAll()
// *with* : setRunning(true) => update ignored since not on screen, dirty = true
// resetValue() => doUpdateAll() since it was never filled, dirty = false
// then this is made displayable => setRunning(true) => no change
// finally made visible => setOnScreen(true) => not dirty => no update
this.req.setRunning(true);
 
}
 
// return true if setEnabled() was called
private final boolean updateEnabled() {
boolean res = false;
if (isDisabledState()) {
344,7 → 334,14
 
protected void updateListeners() {
if (hasModel()) {
this.req.setRunning(this.isDisplayable());
final SleepState newState;
if (!this.isDisplayable())
newState = SleepState.HIBERNATING;
else if (!this.isShowing())
newState = SleepState.SLEEPING;
else
newState = SleepState.AWAKE;
this.req.setSleepState(newState);
}
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/itemview/VWRowItemView.java
155,7 → 155,7
}
 
@Override
public void removeValueListener(PropertyChangeListener l) {
public final void removeValueListener(PropertyChangeListener l) {
assert SwingUtilities.isEventDispatchThread();
this.supp.removePropertyChangeListener(VALUE_PROPNAME, l);
if (!this.supp.hasListeners(VALUE_PROPNAME)) {
181,7 → 181,7
}
 
@Override
public void removeEmptyListener(EmptyListener l) {
public final void removeEmptyListener(EmptyListener l) {
this.helper.removeEmptyListener(l);
}
 
198,7 → 198,7
}
 
@Override
public void removeValidListener(ValidListener l) {
public final void removeValidListener(ValidListener l) {
this.getWrapper().removeValidListener(new ChainValidListener(this, l));
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/ElementComboBox.java
22,6 → 22,7
import org.openconcerto.sql.request.SQLRowItemView;
import org.openconcerto.sql.view.EditFrame;
import org.openconcerto.sql.view.EditPanel;
import org.openconcerto.sql.view.EditPanel.EditMode;
import org.openconcerto.sql.view.EditPanelListener;
import org.openconcerto.sql.view.IListButton;
import org.openconcerto.sql.view.IListFrame;
289,7 → 290,7
}
if (!displayed) {
if (this.viewFrame == null) {
this.viewFrame = new EditFrame(this.element, this.isModif ? EditPanel.MODIFICATION : EditPanel.READONLY);
this.viewFrame = createEditFrame(this.isModif ? EditPanel.MODIFICATION : EditPanel.READONLY);
// dispose since if we change canModif, the old frame will be orphaned
this.viewFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
318,7 → 319,7
 
private final EditFrame getAddFrame() {
if (this.addFrame == null) {
this.addFrame = new EditFrame(this.element, EditPanel.CREATION);
this.addFrame = createEditFrame(EditPanel.CREATION);
this.addFrame.addEditPanelListener(new EditPanelListener() {
@Override
public void cancelled() {
341,6 → 342,17
return this.addFrame;
}
 
protected final EditFrame createEditFrame(final EditMode mode) {
final SQLComponent res = this.createSQLComponent(mode);
if (res.getElement() != this.getElement())
throw new IllegalStateException("Wrong element");
return new EditFrame(res, mode);
}
 
protected SQLComponent createSQLComponent(final EditMode mode) {
return this.element.createDefaultComponent();
}
 
/**
* The sql component of the add frame.
*
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/IComboModel.java
20,13 → 20,17
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTableEvent;
import org.openconcerto.sql.model.SQLTableModifiedListener;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.ComboSQLRequest;
import org.openconcerto.sql.request.ComboSQLRequest.KeepMode;
import org.openconcerto.sql.view.list.ITableModel.SleepState;
import org.openconcerto.sql.view.search.SearchSpec;
import org.openconcerto.sql.view.search.SearchSpecUtils;
import org.openconcerto.ui.SwingThreadUtils;
import org.openconcerto.ui.component.combo.Log;
import org.openconcerto.utils.CompareUtils;
import org.openconcerto.utils.RTInterruptedException;
import org.openconcerto.utils.StringUtils;
import org.openconcerto.utils.SwingWorker2;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.utils.checks.EmptyChangeSupport;
41,11 → 45,15
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.regex.Pattern;
 
import javax.swing.SwingUtilities;
import javax.swing.event.ListDataEvent;
66,8 → 74,8
 
private boolean filledOnce = false;
private ITransformer<List<IComboSelectionItem>, IComboSelectionItem> firstFillTransf = null;
private boolean isADirtyDrityGirl = true;
private boolean isOnScreen = false;
@GuardedBy("this")
private boolean isADirtyDrityGirl;
private boolean sleepAllowed = true;
@GuardedBy("this")
private int requestDelay = 50;
90,15 → 98,25
 
@GuardedBy("this")
private SearchSpec search;
private final boolean reqSearchable;
@GuardedBy("this")
private List<String> searchQuery;
@GuardedBy("this")
private Where searchForceInclude;
 
private PropertyChangeListener filterListener;
// whether this is listening in order to self-update
private boolean running;
@GuardedBy("this")
private SleepState sleepState;
 
private boolean debug = false;
private boolean addMissingItem;
 
public IComboModel(final ComboSQLRequest req) {
this(req, req.isSearchable());
}
 
public IComboModel(final ComboSQLRequest req, final boolean reqSearchable) {
if (req == null)
throw new NullPointerException("null request");
this.req = req;
108,6 → 126,10
this.idToSelect = SQLRow.NONEXISTANT_ID;
 
this.search = null;
// if req change and is no longer searchable, getComboItems() will fail
this.reqSearchable = reqSearchable;
this.searchQuery = Collections.emptyList();
this.searchForceInclude = null;
this.runnables = new ArrayList<Runnable>();
this.willUpdate = null;
this.updating = false;
114,7 → 136,8
this.itemsByID = new HashMap<Integer, IComboSelectionItem>();
this.addMissingItem = true;
 
this.running = false;
this.sleepState = SleepState.HIBERNATING;
this.isADirtyDrityGirl = true;
 
this.setSelectOnAdd(false);
// we always change the selection after changing the items so don't make an extra fire
201,21 → 224,27
super.fireContentsChanged(source, index0, index1);
}
 
void setRunning(final boolean b) {
synchronized void setSleepState(SleepState newState) {
assert SwingUtilities.isEventDispatchThread();
if (this.running != b) {
this.running = b;
if (this.running) {
if (newState == SleepState.SLEEPING && !this.isSleepAllowed())
newState = SleepState.AWAKE;
final SleepState prev = this.sleepState;
if (prev != newState) {
this.sleepState = newState;
if (prev == SleepState.HIBERNATING) {
this.req.addTableListener(this);
this.req.addWhereListener(this.filterListener);
// since we weren't listening, we must have missed lots of things
this.fillCombo();
} else {
this.isADirtyDrityGirl = true;
} else if (newState == SleepState.HIBERNATING) {
this.req.removeTableListener(this);
this.req.rmWhereListener(this.filterListener);
}
if (newState == SleepState.AWAKE && this.isADirtyDrityGirl) {
this.fillCombo();
}
}
}
 
public final ComboSQLRequest getRequest() {
return this.req;
230,19 → 259,6
Log.get().info(s);
}
 
synchronized void setOnScreen(boolean isOnScreen) {
if (this.isOnScreen != isOnScreen) {
this.isOnScreen = isOnScreen;
if (this.isOnScreen && this.isADirtyDrityGirl) {
this.fillCombo();
}
}
}
 
private synchronized boolean isOnScreen() {
return this.isOnScreen;
}
 
/**
* Whether this combo is allowed to delay {@link #fillCombo()} when it isn't visible.
*
256,6 → 272,14
return this.sleepAllowed;
}
 
private final synchronized boolean isActive() {
return this.sleepState == SleepState.AWAKE;
}
 
private final synchronized boolean areValuesObsolete() {
return this.isUpdating() || !this.isActive();
}
 
public synchronized final int getRequestDelay() {
return this.requestDelay;
}
273,7 → 297,7
/**
* Reload this combo. This method is thread-safe.
*/
public synchronized final void fillCombo() {
public final void fillCombo() {
this.fillCombo(null, true);
}
 
280,7 → 304,7
public synchronized final void fillCombo(final Runnable r, final boolean readCache) {
// wholly synch otherwise we might get onScreen after the if
// and thus completely ignore that fillCombo()
if (!this.isSleepAllowed() || this.isOnScreen() || r != null) {
if (this.isActive() || r != null) {
this.doUpdateAll(r, readCache);
} else {
this.isADirtyDrityGirl = true;
323,6 → 347,9
final int delay = this.getRequestDelay();
// copy the current search, if it changes fillCombo() will be called
final SearchSpec search = this.getSearch();
final List<String> searchQuery = this.getSearchQuery();
final Where searchForceInclude = this.getSearchForceInclude();
log("will call getComboItems(" + readCache + ", " + searchQuery + ", " + searchForceInclude + ")");
// commencer l'update après, sinon modeToSelect == 0
final SwingWorker2<List<IComboSelectionItem>, Object> worker = new SwingWorker2<List<IComboSelectionItem>, Object>() {
 
330,7 → 357,7
protected List<IComboSelectionItem> doInBackground() throws InterruptedException {
// attends 1 peu pour voir si on va pas être annulé
Thread.sleep(delay);
return SearchSpecUtils.filter(IComboModel.this.req.getComboItems(readCache), search);
return SearchSpecUtils.filter(IComboModel.this.req.getComboItems(readCache, searchQuery, Locale.getDefault(), searchForceInclude), search);
}
 
// Runs on the event-dispatching thread.
345,7 → 372,7
 
final boolean firstFill = !IComboModel.this.filledOnce;
// store before removing since it can trigger a selection change
final int idToSelect = IComboModel.this.idToSelect;
final int idToSelect = getWantedID();
List<IComboSelectionItem> items = null;
try {
items = this.get();
378,6 → 405,10
IComboModel.this.setSelectedItem(items.get(0));
else if (noSelection && firstFill && getFirstFillSelection() != null)
IComboModel.this.setSelectedItem(getFirstFillSelection().transformChecked(items));
// if the wanted ID has changed after we made the request, and we didn't
// get the it, retry
else if (!noSelection && getComboItem(idToSelect) == null && !CompareUtils.equals(getSearchForceInclude(), searchForceInclude))
fillCombo();
else
selectID(idToSelect);
 
485,16 → 516,20
*/
@Override
public final IComboSelectionItem getValue() {
if (!this.isUpdating())
return this.getSelectedValue();
else if (this.getWantedID() == SQLRow.NONEXISTANT_ID)
return null;
else if (this.getRequest().getKeepMode() == KeepMode.NONE)
return new IComboSelectionItem(getWantedID(), null);
else
final IComboSelectionItem res;
if (!areValuesObsolete()) {
res = this.getSelectedValue();
} else if (this.getWantedID() == SQLRow.NONEXISTANT_ID) {
res = null;
} else if (this.getRequest().getKeepMode() == KeepMode.NONE) {
res = new IComboSelectionItem(getWantedID(), null);
} else {
// no point in passing an SQLRowValues as the graph would be limited to just this row
return new IComboSelectionItem(new SQLRow(this.getForeignTable(), getWantedID()), null);
res = new IComboSelectionItem(new SQLRow(this.getForeignTable(), getWantedID()), null);
}
assert (this.getWantedID() != SQLRow.NONEXISTANT_ID && this.getWantedID() == res.getId()) || (this.getWantedID() == SQLRow.NONEXISTANT_ID && res == null);
return res;
}
 
/**
* The currently selected item. I.e. the value before the last {@link #setValue(int)}) (possibly
520,9 → 555,11
}
 
private final void setWantedID(int id) {
assert SwingUtilities.isEventDispatchThread();
if (this.idToSelect != id) {
final int old = this.idToSelect;
this.idToSelect = id;
this.setSearchForceInclude(id);
this.propSupp.firePropertyChange("wantedID", old, id);
this.propSupp.firePropertyChange("value", null, getValue());
this.emptySupp.fireEmptyChange(this.isEmpty());
574,23 → 611,31
log("entering selectID " + id);
assert SwingUtilities.isEventDispatchThread();
 
// no need to launch another updateAll() if one is already underway
if (this.neverBeenFilled() && !isUpdating())
// don't use fillCombo() which won't really update unless we're on screen
this.doUpdateAll(null, true);
 
if (this.isUpdating()) {
if (this.areValuesObsolete()) {
this.setWantedID(id);
log("isUpdating: this.idToSelect = " + id);
// if this is updating, the wanted ID will eventually get selected, otherwise signal
// that on a wake up we should check wantedID
if (!this.isUpdating()) {
synchronized (this) {
this.isADirtyDrityGirl = true;
}
}
log("values are obsolete: wantedID = " + id);
} else if (id == SQLRow.NONEXISTANT_ID) {
this.setSelectedItem(null);
log("NONEXISTANT_ID: setSelectedItem(null)");
} else {
final IComboSelectionItem item = this.getComboItem(id);
log("valid id : " + id + " item: " + item);
// * setSelectedItem() use IComboSelectionItem.equals() so it must compare the ID and
// the flag since even if ID doesn't change the combo might get refreshed and the
// selected row :
final boolean forceIncludeChanged = this.setSearchForceInclude(id);
log("valid id : " + id + " forceIncludeChanged: " + forceIncludeChanged + " item: " + item);
if (forceIncludeChanged && item == null) {
this.fillCombo();
assert this.isUpdating() : "If not isUpdating(), getValue() will return the current UI value instead of the wantedID";
this.setWantedID(id);
} else {
// * setSelectedItem() use IComboSelectionItem.equals() so it must compare the ID
// and the flag since even if ID doesn't change the combo might get refreshed and
// the selected row :
// 1. get removed : in that case we want to add the "warning" item
// 2. get added : in that case remove the "warning"
// * ATTN item being null means id isn't in the result set, getSelectedValue() being
597,8 → 642,8
// null means nothing is selected. For example if the current selection is empty and
// now we want ID 34 but it isn't returned by the request, both will be null.
if (item == null && this.addMissingItem()) {
// si l'ID voulu n'est pas la, essayer d'aller le chercher directement dans la base
// sans respecter le filtre
// si l'ID voulu n'est pas là, essayer d'aller le chercher directement dans la
// base sans respecter le filtre
final ComboSQLRequest comboSQLRequest = this.req.clone();
comboSQLRequest.setFilterEnabled(false);
comboSQLRequest.setWhere(null);
637,6 → 682,7
}
}
}
}
 
/**
* Whether missing item are fetched from the database. If {@link #setValue(int)} is called with
787,21 → 833,66
 
@Override
public boolean isSearchable() {
return !this.getRequest().getSearchFields().isEmpty();
return this.reqSearchable;
}
 
private final static Pattern QUERY_SPLIT_PATTERN = Pattern.compile("\\s+");
 
/**
* Set the search query. The query will be used to match rows using
* {@link ComboSQLRequest#setSearchFields(java.util.Collection)}. I.e. if there's no field set,
* this method won't have any effect.
*
* @param s the search query.
* @param r {@inheritDoc}
* @return {@inheritDoc}
*/
@Override
public boolean setSearch(String s, Runnable r) {
if (this.getRequest().setSearch(s)) {
if (r != null) {
// no need to trim() since trailing empty strings are not returned
final List<String> split = StringUtils.isEmpty(s) ? Collections.<String> emptyList() : Arrays.asList(QUERY_SPLIT_PATTERN.split(s));
boolean res = false;
synchronized (this) {
this.runnables.add(r);
if (!split.equals(this.searchQuery)) {
this.searchQuery = split;
if (this.isSearchable()) {
res = true;
}
}
return true;
} else {
SwingUtilities.invokeLater(r);
log("setSearch() fillCombo: " + res + " query: " + this.searchQuery);
}
if (res) {
this.fillCombo(r, true);
} else if (r != null) {
SwingThreadUtils.invoke(r);
}
return res;
}
 
private synchronized List<String> getSearchQuery() {
assert this.searchQuery != null : "Null query means don't use the search which should only be governed by isSearchable()";
return this.isSearchable() ? this.searchQuery : null;
}
 
private boolean setSearchForceInclude(final int id) {
if (!this.isSearchable()) {
assert this.getSearchForceInclude() == null;
return false;
}
final Where newVal = id != SQLRow.NONEXISTANT_ID ? new Where(getRequest().getPrimaryTable().getKey(), "=", id) : null;
final boolean changed;
synchronized (this) {
changed = !CompareUtils.equals(this.searchForceInclude, newVal);
if (changed) {
this.searchForceInclude = newVal;
}
}
// don't fillCombo() (as when searchQuery is changed) since we want to avoid it if the ID is
// available
return changed;
}
 
private synchronized Where getSearchForceInclude() {
return this.searchForceInclude;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/JUniqueTextField.java
69,6 → 69,9
*/
public class JUniqueTextField extends JPanel implements ValueWrapper<String>, Documented, TextComponent, RowItemViewComponent, SQLComponentItem, MouseListener {
 
public static int RETRY_COUNT = 5;
public static int SLEEP_WAIT_MS = 200;
 
public static final boolean exists(final SQLField f, final String t, final Number idToExclude, final boolean withCache) throws RTInterruptedException {
final SQLSelect selNum = new SQLSelect();
selNum.addSelectFunctionStar("COUNT");
182,7 → 185,16
while (JUniqueTextField.this.loop) {
 
try {
checkValidation(false);
if (!checkValidation(false)) {
final String number = getAutoRefreshNumber();
if (number != null && number.trim().length() > 0 && !number.equals(getText())) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JUniqueTextField.this.textField.setText(number);
}
});
}
}
} catch (RTInterruptedException e) {
// Arret normal si le texte a changé
}
200,6 → 212,15
this.validationThread.start();
}
 
/**
* Actualisation automatique du numéro si il est pris lors la vérification automatique
*
* @return le nouveau numéro. Par défaut null, pas d'actualisation automatique.
*/
public String getAutoRefreshNumber() {
return null;
}
 
public synchronized boolean checkValidation() throws RTInterruptedException {
return checkValidation(true);
}
228,7 → 249,7
return false;
}
 
if (System.currentTimeMillis() - this.lastCheck < 1000) {
if (withCache && System.currentTimeMillis() - this.lastCheck < 1000) {
// Ne pas checker 2 fois dans la meme seconde
// On laisse le watcher le faire
this.waitTime = 1000;
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/IComboSelectionItem.java
20,6 → 20,8
import org.openconcerto.ui.component.combo.VarDesc;
import org.openconcerto.utils.CompareUtils;
 
import java.util.Comparator;
 
/**
* @author ILM Informatique
*/
35,6 → 37,19
private final String fLabel;
private int flag;
 
public static final Comparator<? super IComboSelectionItem> CASE_SENSITIVE_LABEL_COMPARATOR = new Comparator<IComboSelectionItem>() {
@Override
public int compare(IComboSelectionItem o1, IComboSelectionItem o2) {
return o1.getLabel().compareTo(o2.getLabel());
}
};
public static final Comparator<? super IComboSelectionItem> CASE_INSENSITIVE_LABEL_COMPARATOR = new Comparator<IComboSelectionItem>() {
@Override
public int compare(IComboSelectionItem o1, IComboSelectionItem o2) {
return o1.getLabel().compareToIgnoreCase(o2.getLabel());
}
};
 
public IComboSelectionItem(final int id, final String label) {
this(null, id, label);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/Configuration.java
16,22 → 16,6
*/
package org.openconcerto.sql;
 
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
 
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
 
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.model.DBFileCache;
48,7 → 32,26
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.SQLFieldTranslator;
import org.openconcerto.utils.BaseDirs;
import org.openconcerto.utils.FileUtils;
import org.openconcerto.utils.StringUtils;
 
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
 
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
 
import net.jcip.annotations.GuardedBy;
 
/**
75,8 → 78,24
 
public static final void setInstance(Configuration instance) {
Configuration.instance = instance;
try {
instance.migrateToNewDBConfDir();
} catch (IOException e) {
throw new IllegalStateException("Couldn't migrate");
}
}
 
static public final void migrateToNewDir(final File oldDir, final File newDir) throws IOException {
if (oldDir.exists() && !newDir.exists()) {
if (!oldDir.isDirectory())
throw new IOException("Old file isn't a directory : " + oldDir);
FileUtils.mkdir_p(newDir.getParentFile());
final String err = FileUtils.mv(oldDir, newDir);
if (err != null)
throw new IOException("Couldn't migrate from " + oldDir + " : " + err);
}
}
 
@GuardedBy("this")
private ExecutorService nonInteractiveSQLExecutor;
 
112,16 → 131,27
*/
public final String getAppID() {
final String appName = this.getAppName();
if (appName == null || appName.length() == 0)
if (StringUtils.isEmpty(appName))
return null;
return appName + getAppIDSuffix();
final String variant = this.getAppVariant();
if (StringUtils.isEmpty(variant, true))
return appName;
return appName + '-' + variant;
}
 
protected String getAppIDSuffix() {
return "";
public String getAppVariant() {
return null;
}
 
public File getConfDir() {
public abstract BaseDirs getBaseDirs();
 
public final File getConfDir() {
return getBaseDirs().getPreferencesFolder();
}
 
// for migration use
@Deprecated
protected File getOldConfDir() {
return new File(getDefaultConfDir(), this.getAppID());
}
 
134,30 → 164,29
return getConfDir(getRoot());
}
 
/**
* Move {@link #getConfDir()}/<code>name</code> to {@link #getConfDirForRoot()}/
* <code>name</code> if necessary.
*
* @param name the name of the file or directory to move.
* @return the new file in <code>getConfDirForRoot()</code>.
*/
public final File migrateToConfDirForRoot(final String name) {
final File oldFile = new File(this.getConfDir(), name);
final File newFile = new File(this.getConfDirForRoot(), name);
if (oldFile.exists() && !newFile.exists()) {
try {
FileUtils.mkdir_p(newFile.getParentFile());
oldFile.renameTo(newFile);
} catch (IOException e) {
e.printStackTrace();
FileUtils.rmR(oldFile);
public final void migrateToNewDBConfDir() throws IOException {
final File oldFile = getOldDBConfDir();
final File newFile = getDBConfDir();
migrateToNewDir(oldFile, newFile);
}
 
// for migration use
private File getOldDBConfDir() {
return new File(getOldConfDir(), "dataDepedent");
}
return newFile;
 
// for migration use
@Deprecated
protected final File getOldConfDir(DBStructureItem<?> db) {
return DBItemFileCache.getDescendant(getOldDBConfDir(), DBFileCache.getJDBCAncestorNames(db, true));
}
 
private File getDBConfDir() {
return new File(getConfDir(), "dataDependent");
}
 
public final File getConfDir(DBStructureItem<?> db) {
return DBItemFileCache.getDescendant(new File(getConfDir(), "dataDepedent"), DBFileCache.getJDBCAncestorNames(db, true));
return DBItemFileCache.getDescendant(getDBConfDir(), DBFileCache.getJDBCAncestorNames(db, true));
}
 
/**
188,11 → 217,13
/**
* Get xml value from table FWK_LIST_PREFS for an user and a table.
*
* @throws ParserConfigurationException
* @throws IOException
* @throws SAXException
* @param userId - Id of user
* @param idTable - Id of table
*
* @throws IllegalStateException
* @throws IllegalArgumentException
*/
public Document getXMLConf(final long userId, final String idTable) throws ParserConfigurationException, SAXException, IOException {
public Document getXMLConf(final Number userId, final String idTable) throws IllegalStateException, IllegalArgumentException {
final SQLElement element = this.getDirectory().getElement("FWK_LIST_PREFS");
final SQLTable columnPrefsTable = element.getTable();
final SQLSelect select = new SQLSelect();
201,13 → 232,41
final List<SQLRow> rqResult = SQLRowListRSH.execute(select);
if (rqResult != null && !rqResult.isEmpty()) {
final DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
final DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
DocumentBuilder docBuilder;
try {
docBuilder = docFactory.newDocumentBuilder();
} catch (final ParserConfigurationException ex) {
throw new IllegalStateException("Impossible to create new XML document", ex);
}
 
try {
return docBuilder.parse(new InputSource(new StringReader(rqResult.get(0).getString("VALUE"))));
} catch (final SAXException ex) {
throw new IllegalArgumentException("Impossible to parse XML from database", ex);
} catch (final IOException ex) {
throw new IllegalStateException("Impossible to read content of database", ex);
}
}
return null;
}
 
/**
* Remove XML value from table FWK_LIST_PREFS for an user and a table.
*
* @param userId - Id of user
* @param idTable - Id of table
*
* @throws IllegalStateException
* @throws IllegalArgumentException
*/
public void removeXMLConf(final Number userId, final String idTable) throws IllegalStateException, IllegalArgumentException {
final SQLElement element = this.getDirectory().getElement("FWK_LIST_PREFS");
final SQLTable columnPrefsTable = element.getTable();
 
this.getRoot().getDBSystemRoot().getDataSource().execute("DELETE FROM " + columnPrefsTable.getSQLName().quote() + " WHERE \"ID_USER\" = " + userId + " AND \"ID_TABLE\" = '" + idTable + "'");
}
 
/**
* An executor that should be used for background SQL requests. It can be used to limit the
* concurrent number of database connections (as establishing a connection is expensive and the
* server might have restrictions).
/trunk/OpenConcerto/src/org/openconcerto/sql/request/ComboSQLRequestUtils.java
New file
0,0 → 1,122
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.sql.request;
 
import org.openconcerto.sql.TransfFieldExpander;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.model.FieldPath;
import org.openconcerto.sql.model.IFieldPath;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.graph.Link.Direction;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.ITransformer;
 
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
 
final class ComboSQLRequestUtils {
 
// return the foreign row in field and its fields
private static Tuple2<SQLRowValues, List<FieldPath>> expandOnce(final SQLRowValues vals, final IFieldPath field) {
assert vals.getFields().contains(field.getFieldName());
 
final Object foreignObj = vals.getObject(field.getFieldName());
if (!(foreignObj instanceof SQLRowValues))
return Tuple2.create(null, Collections.<FieldPath> emptyList());
 
final SQLRowValues foreign = (SQLRowValues) foreignObj;
// foreign since we used getObject()
final Path newPath = field.getPath().add(field.getField(), Direction.FOREIGN);
final List<FieldPath> res = new ArrayList<FieldPath>();
for (final String f : foreign.getFields())
res.add(new FieldPath(newPath, f));
 
return Tuple2.create(foreign, res);
}
 
// recursively walk vals to collect all foreign fields
public static final List<FieldPath> expand(final SQLRowValues vals, final IFieldPath field) {
assert vals.getTable() == field.getTable();
final List<FieldPath> fields = new ArrayList<FieldPath>();
 
if (!field.getTable().getForeignKeys().contains(field.getField())) {
// si ce n'est pas une clef alors il n'y a pas à l'expandre
fields.add(field.getFieldPath());
} else {
final Tuple2<SQLRowValues, List<FieldPath>> tmp = expandOnce(vals, field);
for (final FieldPath f : tmp.get1()) {
fields.addAll(expand(tmp.get0(), f));
}
}
 
return fields;
}
 
/**
* Group fields of the passed row by the parent foreign key. For each item of the result, the
* path to the ancestor is also included, e.g. [ LOCAL, LOCAL.ID_BATIMENT,
* LOCAL.ID_BATIMENT.ID_SITE, LOCAL.ID_BATIMENT.ID_SITE.ID_ETABLISSEMENT ].
*
* @param vals the row to go through.
* @param dir how to find the parents.
* @return the complete expansion, e.g. [ [LOCAL.DESIGNATION], [BAT.DES], [SITE.DES,
* ADRESSE.CP], [ETABLISSEMENT.DES] ].
*/
public static final List<Tuple2<Path, List<FieldPath>>> expandGroupBy(final SQLRowValues vals, final SQLElementDirectory dir) {
return expandGroupBy(new Path(vals.getTable()), vals, dir);
}
 
private static final List<Tuple2<Path, List<FieldPath>>> expandGroupBy(final Path fieldsPath, final SQLRowValues vals, final SQLElementDirectory dir) {
assert fieldsPath.getLast() == vals.getTable();
if (vals.size() == 0)
return Collections.emptyList();
 
final SQLElement element = dir.getElement(fieldsPath.getLast());
// treat missing element as the root
final SQLField parentFF = element == null ? null : element.getParentForeignField();
 
final List<Tuple2<Path, List<FieldPath>>> res = new ArrayList<Tuple2<Path, List<FieldPath>>>();
final List<FieldPath> currentL = new ArrayList<FieldPath>();
res.add(Tuple2.create(fieldsPath, currentL));
SQLRowValues parent = null;
for (final String f : vals.getFields()) {
if (parentFF != null && f.equals(parentFF.getName())) {
final Object val = vals.getObject(f);
parent = val instanceof SQLRowValues ? (SQLRowValues) val : null;
} else {
currentL.addAll(expand(vals, new FieldPath(fieldsPath, f)));
}
}
if (parent != null)
res.addAll(expandGroupBy(fieldsPath.add(parentFF, Direction.FOREIGN), parent, dir));
 
return res;
}
 
public static final TransfFieldExpander getShowAs(final SQLElementDirectory dir) {
final TransfFieldExpander exp = new TransfFieldExpander(new ITransformer<SQLField, List<SQLField>>() {
@Override
public List<SQLField> transformChecked(SQLField fk) {
final SQLTable foreignTable = fk.getDBSystemRoot().getGraph().getForeignTable(fk);
return dir.getElement(foreignTable).getComboRequest().getFields();
}
});
return exp;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/request/BaseFillSQLRequest.java
14,7 → 14,9
package org.openconcerto.sql.request;
 
import org.openconcerto.sql.FieldExpander;
import org.openconcerto.sql.TM;
import org.openconcerto.sql.model.FieldRef;
import org.openconcerto.sql.model.IFieldPath;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValues.CreateMode;
21,34 → 23,43
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
import org.openconcerto.sql.model.SQLSearchMode;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLSyntax;
import org.openconcerto.sql.model.SQLSyntax.CaseBuilder;
import org.openconcerto.sql.model.SQLSyntax.DateProp;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTable.VirtualFields;
import org.openconcerto.sql.model.SQLTableModifiedListener;
import org.openconcerto.sql.model.SQLType;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.IClosure;
import org.openconcerto.utils.cc.ITransformer;
 
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Pattern;
 
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;
 
@ThreadSafe
public abstract class BaseFillSQLRequest extends BaseSQLRequest implements Cloneable {
public abstract class BaseFillSQLRequest extends BaseSQLRequest {
 
private final static Pattern QUERY_SPLIT_PATTERN = Pattern.compile("\\s+");
private static boolean DEFAULT_SELECT_LOCK = true;
 
/**
75,32 → 86,39
fetcher.setReferentsOrdered(true, true);
}
 
static public final void addToFetch(final SQLRowValues input, final Path p, final Collection<String> fields) {
static public final boolean addToFetch(final SQLRowValues input, final Path p, final Collection<String> fields) {
assert p == null || p.isSingleLink() : "Graph size not sufficient to know if graph was modified";
final int graphSize = input.getGraphSize();
// don't back track : e.g. if path is SITE -> CLIENT <- SITE we want the siblings of SITE,
// if we want fields of the primary SITE we pass the path SITE
final SQLRowValues r = p == null ? input : input.followPathToOne(p, CreateMode.CREATE_ONE, false);
boolean modified = input.getGraphSize() > graphSize;
for (final String f : fields) {
// don't overwrite foreign rows
if (!r.getFields().contains(f))
// don't overwrite foreign rows and update modified
if (!r.getFields().contains(f)) {
r.put(f, null);
modified = true;
}
}
return modified;
}
 
private final SQLTable primaryTable;
@GuardedBy("this")
private List<Path> order;
@GuardedBy("this")
private Where where;
@GuardedBy("this")
private Map<SQLField, SQLSearchMode> searchFields;
private Map<IFieldPath, SearchField> searchFields;
@GuardedBy("this")
private List<String> searchQuery;
private int searchLimit;
@GuardedBy("this")
private ITransformer<SQLSelect, SQLSelect> selTransf;
@GuardedBy("this")
private boolean lockSelect;
 
private final SQLRowValues graph;
@GuardedBy("this")
private SQLRowValues graph;
@GuardedBy("this")
private SQLRowValues graphToFetch;
 
@GuardedBy("this")
113,17 → 131,18
 
private final PropertyChangeSupport supp = new PropertyChangeSupport(this);
 
public BaseFillSQLRequest(final SQLTable primaryTable, final Where w) {
public BaseFillSQLRequest(final SQLRowValues graph, final Where w) {
super();
if (primaryTable == null)
if (graph == null)
throw new NullPointerException();
this.primaryTable = primaryTable;
this.primaryTable = graph.getTable();
this.setOrder(null);
this.where = w;
this.searchFields = Collections.emptyMap();
this.searchQuery = Collections.emptyList();
this.searchLimit = 35;
this.selTransf = null;
this.lockSelect = getDefaultLockSelect();
this.graph = null;
this.graph = graph.toImmutable();
this.graphToFetch = null;
}
 
131,9 → 150,10
super();
this.primaryTable = req.getPrimaryTable();
synchronized (req) {
this.order = req.order;
this.where = req.where;
this.searchFields = req.searchFields;
this.searchQuery = req.searchQuery;
this.searchLimit = req.searchLimit;
this.selTransf = req.selTransf;
this.lockSelect = req.lockSelect;
// use methods since they're both lazy
199,53 → 219,24
return casted;
}
 
@Override
public BaseFillSQLRequest clone() {
synchronized (this) {
return this.clone(false);
}
}
 
// must be called with our lock
protected abstract BaseFillSQLRequest clone(boolean forFreeze);
 
private final SQLRowValues computeGraph() {
if (this.getFields() == null)
return null;
 
final SQLRowValues vals = new SQLRowValues(this.getPrimaryTable());
for (final SQLField f : this.getFields()) {
vals.put(f.getName(), null);
}
 
this.getShowAs().expand(vals);
static protected final SQLRowValues computeGraph(final SQLTable t, final Collection<String> fields, final FieldExpander exp) {
final SQLRowValues vals = new SQLRowValues(t).putNulls(fields);
exp.expand(vals);
return vals.toImmutable();
}
 
/**
* The graph computed by expanding {@link #getFields()} by {@link #getShowAs()}.
* The graph with fields to be automatically added to the UI.
*
* @return the expanded frozen graph.
*/
public final SQLRowValues getGraph() {
synchronized (this) {
if (this.graph == null) {
assert !this.isFrozen() : "no computation should take place after frozen()";
this.graph = this.computeGraph();
}
return this.graph;
}
}
 
// should be called if getFields(), getOrder() or getShowAs() change
protected final void clearGraph() {
synchronized (this) {
checkFrozen();
this.graph = null;
this.graphToFetch = null;
}
}
 
/**
* The graph to fetch, should be a superset of {@link #getGraph()}. To modify it, see
* {@link #addToGraphToFetch(Path, Set)} and {@link #changeGraphToFetch(IClosure)}.
258,16 → 249,8
assert !this.isFrozen() : "no computation should take place after frozen()";
final SQLRowValues tmp = this.getGraph().deepCopy();
this.customizeToFetch(tmp);
// fetch order fields, so that consumers can order an updated row in an existing
// list
for (final Path orderP : this.getOrder()) {
final SQLRowValues orderVals = tmp.followPath(orderP);
if (orderVals != null && orderVals.getTable().isOrdered()) {
orderVals.put(orderVals.getTable().getOrderField().getName(), null);
this.setGraphToFetch(tmp, true);
}
}
this.graphToFetch = tmp.toImmutable();
}
return this.graphToFetch;
}
}
296,18 → 279,30
public void executeChecked(SQLRowValues input) {
addToFetch(input, p, fields);
}
});
}, false);
}
 
public final void changeGraphToFetch(IClosure<SQLRowValues> cl) {
this.changeGraphToFetch(cl, true);
}
 
private final void changeGraphToFetch(IClosure<SQLRowValues> cl, final boolean checkNeeded) {
synchronized (this) {
checkFrozen();
final SQLRowValues tmp = this.getGraphToFetch().deepCopy();
cl.executeChecked(tmp);
this.graphToFetch = tmp.toImmutable();
this.setGraphToFetch(tmp, checkNeeded);
}
fireWhereChange();
}
 
private final void setGraphToFetch(final SQLRowValues tmp, final boolean checkNeeded) {
assert Thread.holdsLock(this) && !this.isFrozen();
if (checkNeeded && !tmp.graphContains(this.getGraph()))
throw new IllegalArgumentException("New graph too small");
this.graphToFetch = tmp.toImmutable();
}
 
protected void customizeToFetch(final SQLRowValues graphToFetch) {
}
 
314,9 → 309,18
protected synchronized final SQLRowValuesListFetcher getFetcher() {
if (this.isFrozen())
return this.frozen;
// fetch order fields, so that consumers can order an updated row in an existing list
final SQLRowValues tmp = getGraphToFetch().deepCopy();
for (final Path orderP : this.getOrder()) {
final SQLRowValues orderVals = tmp.followPath(orderP);
if (orderVals != null && orderVals.getTable().isOrdered()) {
orderVals.put(orderVals.getTable().getOrderField().getName(), null);
}
}
// graphToFetch can be modified freely so don't the use the simple constructor
final SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(getGraphToFetch(), false);
return setupFetcher(fetcher);
// order to have predictable result (this will both order the referent rows and main rows.
// The latter will be overwritten by our own getOrder())
return setupFetcher(SQLRowValuesListFetcher.create(tmp, true));
}
 
// allow to pass fetcher since they are mostly immutable (and for huge graphs they are slow to
342,10 → 346,27
return fetcher;
}
 
protected List<Path> getOrder() {
protected synchronized final List<Path> getOrder() {
if (this.order != null)
return this.order;
return this.getDefaultOrder();
}
 
protected List<Path> getDefaultOrder() {
return Collections.singletonList(Path.get(getPrimaryTable()));
}
 
/**
* Change the ordering of this request.
*
* @param l the list of tables, <code>null</code> to restore the {@link #getDefaultOrder()
* default} .
*/
public synchronized final void setOrder(List<Path> l) {
checkFrozen();
this.order = l == null ? null : Collections.unmodifiableList(new ArrayList<Path>(l));
}
 
public final void setWhere(final Where w) {
synchronized (this) {
checkFrozen();
365,9 → 386,14
* <code>false</code> to not be searchable.
*/
public final void setSearchable(final boolean b) {
this.setSearchFields(b ? this.getFields() : Collections.<SQLField> emptyList());
this.setSearchFields(b ? getDefaultSearchFields() : Collections.<SearchField> emptyList());
}
 
protected Collection<SearchField> getDefaultSearchFields() {
final Set<String> names = CollectionUtils.inter(this.getGraph().getFields(), this.getPrimaryTable().getFieldsNames(VirtualFields.LOCAL_CONTENT));
return mapOfModesToSearchFields(CollectionUtils.<String, SQLSearchMode> createMap(names));
}
 
/**
* Set the fields used to search.
*
374,10 → 400,18
* @param searchFields only rows with these fields containing the terms will match.
* @see #setSearch(String)
*/
public final void setSearchFields(final Collection<SQLField> searchFields) {
this.setSearchFields(CollectionUtils.<SQLField, SQLSearchMode> createMap(searchFields));
public final void setSearchFieldsNames(final Collection<String> searchFields) {
this.setSearchFieldsNames(CollectionUtils.<String, SQLSearchMode> createMap(searchFields));
}
 
protected final Collection<SearchField> mapOfModesToSearchFields(Map<String, SQLSearchMode> searchFields) {
final List<SearchField> list = new ArrayList<SearchField>();
for (final Entry<String, SQLSearchMode> e : searchFields.entrySet()) {
list.add(new SearchField(getPrimaryTable().getField(e.getKey()), e.getValue() == null ? SQLSearchMode.CONTAINS : e.getValue()));
}
return list;
}
 
/**
* Set the fields used to search.
*
384,57 → 418,42
* @param searchFields for each field to search, how to match.
* @see #setSearch(String)
*/
public final void setSearchFields(Map<SQLField, SQLSearchMode> searchFields) {
public final void setSearchFieldsNames(Map<String, SQLSearchMode> searchFields) {
this.setSearchFields(mapOfModesToSearchFields(searchFields));
}
 
public final void setSearchFields(final Collection<SearchField> searchFields) {
// can be outside the synchronized block, since it can't be reverted
checkFrozen();
searchFields = new HashMap<SQLField, SQLSearchMode>(searchFields);
final Iterator<Entry<SQLField, SQLSearchMode>> iter = searchFields.entrySet().iterator();
while (iter.hasNext()) {
final Entry<SQLField, SQLSearchMode> e = iter.next();
if (!String.class.isAssignableFrom(e.getKey().getType().getJavaType())) {
iter.remove();
} else if (e.getValue() == null) {
e.setValue(SQLSearchMode.CONTAINS);
final Map<IFieldPath, SearchField> copy = new HashMap<IFieldPath, SearchField>();
for (final SearchField f : searchFields) {
final SearchField prev = copy.put(f.getField(), f);
if (prev != null)
throw new IllegalArgumentException("Duplicate : " + f.getField());
}
}
searchFields = Collections.unmodifiableMap(searchFields);
synchronized (this) {
this.searchFields = searchFields;
this.searchFields = Collections.unmodifiableMap(copy);
}
fireWhereChange();
}
 
public Map<SQLField, SQLSearchMode> getSearchFields() {
public Map<IFieldPath, SearchField> getSearchFields() {
synchronized (this) {
return this.searchFields;
}
}
 
/**
* Set the search query. The query will be used to match rows using
* {@link #setSearchFields(Map)}. I.e. if there's no field set, this method won't have any
* effect.
*
* @param s the search query.
* @return <code>true</code> if the request changed.
*/
public boolean setSearch(String s) {
// no need to trim() since trailing empty strings are not returned
final List<String> split = Arrays.asList(QUERY_SPLIT_PATTERN.split(s));
boolean res = false;
synchronized (this) {
checkFrozen();
if (!split.equals(this.searchQuery)) {
this.searchQuery = split;
if (!this.getSearchFields().isEmpty()) {
res = true;
public synchronized final boolean isSearchable() {
return !this.getSearchFields().isEmpty();
}
 
public synchronized final void setSearchLimit(final int limit) {
this.searchLimit = limit;
}
 
public synchronized final int getSearchLimit() {
return this.searchLimit;
}
if (res)
this.fireWhereChange();
return res;
}
 
public final synchronized void setLockSelect(boolean lockSelect) {
checkFrozen();
445,54 → 464,221
return this.lockSelect;
}
 
@Override
public final Collection<SQLField> getAllFields() {
// don't rely on the expansion of our fields, since our fetcher can be arbitrary modified
// (eg by adding a where on a field of a non-displayed table)
return this.getFetcher().getReq().getFields();
public Set<SQLTable> getTables() {
final Set<SQLTable> res = new HashSet<SQLTable>();
for (final SQLRowValues v : this.getGraphToFetch().getGraph().getItems())
res.add(v.getTable());
return res;
}
 
protected abstract Collection<SQLField> getFields();
public final void addTableListener(SQLTableModifiedListener l) {
for (final SQLTable t : this.getTables()) {
t.addTableModifiedListener(l);
}
}
 
public final void removeTableListener(SQLTableModifiedListener l) {
for (final SQLTable t : this.getTables()) {
t.removeTableModifiedListener(l);
}
}
 
protected final List<SQLField> getFields() {
return this.getPrimaryTable().getFields(this.getGraph().getFields());
}
 
protected SQLSelect transformSelect(final SQLSelect sel) {
final Map<SQLField, SQLSearchMode> searchFields;
final List<String> searchQuery;
final ITransformer<SQLSelect, SQLSelect> transf = this.getSelectTransf();
return transf == null ? sel : transf.transformChecked(sel);
}
 
// @param searchQuery null means don't want to search in SQL (i.e. no WHERE, no LIMIT), empty
// means nothing to search (i.e. no WHERE but LIMIT).
protected final ITransformer<SQLSelect, SQLSelect> createSearchTransformer(final List<String> searchQuery, final Locale l, final Where forceInclude) {
if (searchQuery == null)
return null;
final Map<IFieldPath, SearchField> searchFields;
final int searchLimit;
final boolean searchable;
synchronized (this) {
searchFields = this.getSearchFields();
searchQuery = this.searchQuery;
searchLimit = this.getSearchLimit();
searchable = this.isSearchable();
}
if (!searchable) {
throw new IllegalArgumentException("Cannot search " + searchQuery);
}
// continue even if searchQuery is empty to apply the LIMIT
final List<String> immutableQuery = Collections.unmodifiableList(new ArrayList<String>(searchQuery));
return new ITransformer<SQLSelect, SQLSelect>() {
@Override
public SQLSelect transformChecked(SQLSelect sel) {
return transformSelectSearch(sel, searchFields, searchLimit, immutableQuery, l, forceInclude);
}
};
}
 
static protected final SQLSelect transformSelectSearch(final SQLSelect sel, final Map<IFieldPath, SearchField> searchFields, final int searchLimit, final List<String> searchQuery, final Locale l,
final Where forceInclude) {
final Where w;
final Set<String> matchScore = new HashSet<String>();
if (!searchFields.isEmpty()) {
if (!searchQuery.isEmpty()) {
final SQLSyntax syntax = sel.getSyntax();
Where where = null;
for (final String searchTerm : searchQuery) {
Where termWhere = null;
for (final FieldRef selF : sel.getSelectFields()) {
final SQLSearchMode mode = searchFields.get(selF.getField());
if (mode != null) {
termWhere = Where.createRaw(createWhere(selF, mode, searchTerm)).or(termWhere);
if (!mode.equals(SQLSearchMode.EQUALS))
matchScore.add("case when " + createWhere(selF, SQLSearchMode.EQUALS, searchTerm) + " then 1 else 0 end");
for (final SearchField searchField : searchFields.values()) {
final FieldRef selF = sel.followFieldPath(searchField.getField());
final SQLSearchMode mode = searchField.getMode();
final List<String> formatted = searchField.format(selF, l);
final String fieldWhere = createWhere(syntax, formatted, mode, searchTerm);
termWhere = Where.createRaw(fieldWhere).or(termWhere);
if (searchField.getScore() > 0 || !searchField.getHigherModes().isEmpty()) {
final CaseBuilder caseBuilder = syntax.createCaseWhenBuilder().setElse("0");
for (final Tuple2<SQLSearchMode, Integer> hm : searchField.getHigherModes()) {
caseBuilder.addWhen(createWhere(syntax, formatted, hm.get0(), searchTerm), String.valueOf(hm.get1()));
}
if (searchField.getScore() > 0) {
caseBuilder.addWhen(fieldWhere, String.valueOf(searchField.getScore()));
}
matchScore.add(caseBuilder.build());
}
}
where = Where.and(termWhere, where);
}
// only use forceInclude when there's a restriction otherwise the include transforms
// itself into a restrict
if (where != null)
where = where.or(forceInclude);
w = where;
} else {
w = null;
}
sel.andWhere(w);
if (forceInclude != null)
matchScore.add("case when " + forceInclude + " then 10000 else 0 end");
if (!matchScore.isEmpty())
sel.getOrder().add(0, CollectionUtils.join(matchScore, " + ") + " DESC");
if (searchLimit >= 0)
sel.setLimit(searchLimit);
 
final ITransformer<SQLSelect, SQLSelect> transf = this.getSelectTransf();
return transf == null ? sel : transf.transformChecked(sel);
return sel;
}
 
protected String createWhere(final FieldRef selF, final SQLSearchMode mode, final String searchQuery) {
return "lower(" + selF.getFieldRef() + ") " + mode.generateSQL(selF.getField().getDBRoot(), searchQuery.toLowerCase());
static protected final String createWhere(final SQLSyntax syntax, final List<String> formatted, final SQLSearchMode mode, final String searchQuery) {
return CollectionUtils.join(formatted, " OR ", new ITransformer<String, String>() {
@Override
public String transformChecked(String sqlExpr) {
return createWhere(sqlExpr, mode, syntax, searchQuery);
}
});
}
 
static public final List<String> defaultFormat(final FieldRef selF, final Locale l) {
final SQLType type = selF.getField().getType();
final SQLSyntax syntax = SQLSyntax.get(selF.getField());
if (type.getJavaType() == String.class) {
return Collections.singletonList(selF.getFieldRef());
} else if (type.getJavaType() == Boolean.class) {
return Collections.singletonList("case when " + selF.getFieldRef() + " then " + syntax.quoteString(TM.tr("true_key")) + " else " + syntax.quoteString(TM.tr("false_key")) + " end");
} else if (Timestamp.class.isAssignableFrom(type.getJavaType())) {
final String shortFmt = formatTime(selF, DateProp.SHORT_DATETIME_SKELETON, l, syntax);
final String longFmt = formatTime(selF, DateProp.LONG_DATETIME_SKELETON, l, syntax);
return Arrays.asList(shortFmt, longFmt);
} else if (Time.class.isAssignableFrom(type.getJavaType())) {
return Collections.singletonList(formatTime(selF, DateProp.TIME_SKELETON, l, syntax));
} else if (Date.class.isAssignableFrom(type.getJavaType())) {
final String shortFmt = formatTime(selF, DateProp.SHORT_DATE_SKELETON, l, syntax);
final String longFmt = formatTime(selF, DateProp.LONG_DATE_SKELETON, l, syntax);
return Arrays.asList(shortFmt, longFmt);
} else {
return Collections.singletonList(syntax.cast(selF.getFieldRef(), String.class));
}
}
 
static public final String formatTime(final FieldRef selF, final List<String> simpleFormat, final Locale l, final SQLSyntax syntax) {
return syntax.getFormatTimestampSimple(selF.getFieldRef(), DateProp.getBestPattern(simpleFormat, l), l);
}
 
static protected final String createWhere(final String sqlExpr, final SQLSearchMode mode, final SQLSyntax syntax, final String searchQuery) {
return "lower(" + sqlExpr + ") " + mode.generateSQL(syntax, searchQuery.toLowerCase());
}
 
static public class SearchField {
private final IFieldPath field;
private final SQLSearchMode mode;
private final int score;
private final List<Tuple2<SQLSearchMode, Integer>> higherModes;
 
public SearchField(IFieldPath field, SQLSearchMode mode) {
this(field, mode, 1);
}
 
/**
* Create a new search field.
*
* @param field which field to search.
* @param mode how to search.
* @param score the score (>0) to attribute if the field matches. Allow to rank fields
* between themselves.
*/
public SearchField(IFieldPath field, SQLSearchMode mode, int score) {
this(field, mode, score, -1, -1);
}
 
public SearchField(final IFieldPath field, final SQLSearchMode mode, final int score, final int score2, final int score3) {
super();
if (field.getField().getFieldGroup().getKeyType() != null)
throw new IllegalArgumentException("Field is a key : " + field);
this.field = field;
this.mode = mode;
/*
* for now we could pass <code>1</code> so that a row with more matches is higher ranked
* (e.g. if searching "a" ["ant", "cat"] is better than ["ant", "horse"]), or
* <code>0</code> to ignore the match count. But this only works because we have
* separate WHERE and ORDER BY ; if we had a computed column with "WHERE score > 0 ORDER
* BY score" this would be complicated.
*/
if (score < 1)
throw new IllegalArgumentException("Invalid score : " + score);
this.score = score;
final List<SQLSearchMode> higherModes = field.getField().getType().getJavaType() == String.class ? this.mode.getHigherModes() : Collections.<SQLSearchMode> emptyList();
if (higherModes.isEmpty()) {
this.higherModes = Collections.emptyList();
} else {
if (higherModes.size() > 2)
throw new IllegalStateException("Too many higher modes " + higherModes);
final List<Tuple2<SQLSearchMode, Integer>> tmp = new ArrayList<Tuple2<SQLSearchMode, Integer>>(2);
tmp.add(Tuple2.create(higherModes.get(0), score3 < 1 ? Math.max((int) (this.score * 1.5), this.score + 2) : score3));
if (higherModes.size() > 1)
tmp.add(Tuple2.create(higherModes.get(1), score2 < 1 ? Math.max((int) (this.score * 1.2), this.score + 1) : score2));
this.higherModes = Collections.unmodifiableList(tmp);
}
}
 
public final IFieldPath getField() {
return this.field;
}
 
public final SQLSearchMode getMode() {
return this.mode;
}
 
public final int getScore() {
return this.score;
}
 
public List<Tuple2<SQLSearchMode, Integer>> getHigherModes() {
return this.higherModes;
}
 
protected List<String> format(final FieldRef selF, final Locale l) {
if (getField().getField() != selF.getField())
throw new IllegalArgumentException("Wrong field");
return defaultFormat(selF, l);
}
}
 
public final synchronized ITransformer<SQLSelect, SQLSelect> getSelectTransf() {
return this.selTransf;
}
510,8 → 696,6
this.fireWhereChange();
}
 
protected abstract FieldExpander getShowAs();
 
public final SQLTable getPrimaryTable() {
return this.primaryTable;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/request/SQLFieldTranslator.java
52,14 → 52,14
import java.util.Set;
import java.util.prefs.Preferences;
 
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;
 
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
 
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;
 
/**
* Class to obtain a RowItemDesc from a table and a name.
*
149,21 → 149,12
this.unknownCodes = new HashSet<String>();
}
 
public SQLFieldTranslator(DBRoot base) {
this(base, null);
}
 
public SQLFieldTranslator(DBRoot base, InputStream inputStream) {
this(base, inputStream, null);
}
 
/**
* Create a new instance.
*
* @param root the default root for tables.
* @param inputStream the XML, can be <code>null</code>.
* @param dir the directory where to look for tables not in <code>root</code>, can be
* <code>null</code>.
* @param dir the directory where to look for tables not in <code>root</code>.
*/
public SQLFieldTranslator(DBRoot root, InputStream inputStream, SQLElementDirectory dir) {
try {
194,6 → 185,10
fetchAndPut(this.table, null);
}
 
public final SQLElementDirectory getDirectory() {
return this.dir;
}
 
/**
* Add all translations of <code>o</code> to this, note that if a table is present in both this
* and <code>o</code> its translations won't be changed.
/trunk/OpenConcerto/src/org/openconcerto/sql/request/SQLRowView.java
16,7 → 16,6
import org.openconcerto.sql.Log;
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
37,7 → 36,6
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
410,19 → 408,6
this.select(null);
}
 
/*
* (non-Javadoc)
*
* @see org.openconcerto.devis.request.BaseSQLRequest#getAllFields()
*/
public Collection<SQLField> getAllFields() {
final Set<SQLField> res = new HashSet<SQLField>();
for (final SQLRowItemView view : this.getViewsFast()) {
res.addAll(view.getFields());
}
return res;
}
 
private void setSelectedID(int selectedID) {
this.selectedID = selectedID;
if (!existsInDB())
/trunk/OpenConcerto/src/org/openconcerto/sql/request/ComboSQLRequest.java
17,17 → 17,17
* @author ILM Informatique
*/
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.TransfFieldExpander;
import org.openconcerto.sql.FieldExpander;
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.model.FieldPath;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
import org.openconcerto.sql.model.SQLSearchMode;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.sqlobject.ElementComboBoxUtils;
import org.openconcerto.sql.sqlobject.IComboSelectionItem;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.CompareUtils;
43,6 → 43,8
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
 
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.Immutable;
50,7 → 52,7
 
// final: use setSelectTransf()
@ThreadSafe
public final class ComboSQLRequest extends FilteredFillSQLRequest {
public final class ComboSQLRequest extends FilteredFillSQLRequest implements Cloneable {
 
static private final SQLCache<CacheKey, List<IComboSelectionItem>> cache = new SQLCache<CacheKey, List<IComboSelectionItem>>(60, -1, "items of " + ComboSQLRequest.class);
 
61,6 → 63,10
private final SQLRowValues graph;
private final SQLRowValuesListFetcher fetcher;
private final Where where;
private final ITransformer<SQLSelect, SQLSelect> selTransformer;
// compute request once and for all to speed up equals (OK since the fetcher and its
// parameter are immutable)
private final String select;
private final String fieldSeparator;
private final String undefLabel;
private final KeepMode keepRows;
68,8 → 74,9
private final IClosure<IComboSelectionItem> customizeItem;
private final Comparator<? super IComboSelectionItem> itemsOrder;
 
public CacheKey(SQLRowValues graph, SQLRowValuesListFetcher f, Where w, String fieldSeparator, String undefLabel, IClosure<IComboSelectionItem> c, KeepMode keepRows,
Comparator<? super IComboSelectionItem> itemsOrder) {
// ATTN selTransformer (as the fetcher and the where) should be immutable
public CacheKey(SQLRowValues graph, SQLRowValuesListFetcher f, Where w, ITransformer<SQLSelect, SQLSelect> selTransformer, String fieldSeparator, String undefLabel,
IClosure<IComboSelectionItem> c, KeepMode keepRows, Comparator<? super IComboSelectionItem> itemsOrder) {
super();
if (!graph.isFrozen())
throw new IllegalArgumentException("Not frozen : " + graph);
80,6 → 87,8
throw new IllegalArgumentException("Not frozen : " + f);
this.fetcher = f;
this.where = w;
this.selTransformer = selTransformer;
this.select = this.fetcher.getReq(this.where, this.selTransformer).asString();
 
this.fieldSeparator = fieldSeparator;
this.undefLabel = undefLabel;
108,12 → 117,12
public boolean equals(Object obj) {
if (this == obj)
return true;
if (getClass() != obj.getClass())
if (obj == null || getClass() != obj.getClass())
return false;
final CacheKey other = (CacheKey) obj;
return this.keepRows == other.keepRows && this.fieldSeparator.equals(other.fieldSeparator) && CompareUtils.equals(this.undefLabel, other.undefLabel)
&& CompareUtils.equals(this.where, other.where) && this.graph.getGraphFirstDifference(other.graph, true) == null && CompareUtils.equals(this.fetcher, other.fetcher)
&& CompareUtils.equals(this.customizeItem, other.customizeItem) && CompareUtils.equals(this.itemsOrder, other.itemsOrder);
&& this.graph.getGraphFirstDifference(other.graph, true) == null && this.select.equals(other.select) && CompareUtils.equals(this.customizeItem, other.customizeItem)
&& CompareUtils.equals(this.itemsOrder, other.itemsOrder);
}
};
 
164,10 → 173,21
setDefaultItemsOrder(null);
}
 
private static final SQLElementDirectory getDirectory(final SQLElementDirectory dir) {
if (dir != null)
return dir;
 
final Configuration conf = Configuration.getInstance();
return conf == null ? null : conf.getDirectory();
}
 
private static final FieldExpander getExpander(SQLElementDirectory dir) {
dir = getDirectory(dir);
return dir != null ? ComboSQLRequestUtils.getShowAs(dir) : FieldExpander.getEmpty();
}
 
// immutable
@GuardedBy("this")
private List<SQLField> comboFields;
private final TransfFieldExpander exp;
private final SQLElementDirectory dir;
 
@GuardedBy("this")
private String fieldSeparator = getDefaultFieldSeparator();
179,8 → 199,6
private IClosure<IComboSelectionItem> customizeItem;
 
@GuardedBy("this")
private List<Path> order;
@GuardedBy("this")
private Comparator<? super IComboSelectionItem> itemsOrder;
 
public ComboSQLRequest(SQLTable table, List<String> l) {
188,23 → 206,27
}
 
public ComboSQLRequest(SQLTable table, List<String> l, Where where) {
super(table, where);
this(table, l, where, null);
}
 
public ComboSQLRequest(SQLTable table, List<String> l, Where where, final SQLElementDirectory dir) {
this(computeGraph(table, l, getExpander(dir)), where, dir);
}
 
public ComboSQLRequest(SQLRowValues graph, Where where, final SQLElementDirectory dir) {
super(graph, where);
this.dir = dir;
this.undefLabel = null;
// don't use memory
this.keepRows = KeepMode.NONE;
this.customizeItem = null;
this.order = null;
this.itemsOrder = getDefaultItemsOrder();
this.exp = ElementComboBoxUtils.getShowAs(Configuration.getInstance());
this.setFields(l);
}
 
protected ComboSQLRequest(ComboSQLRequest c, final boolean freeze) {
super(c, freeze);
this.exp = new TransfFieldExpander(c.exp);
this.dir = c.dir;
synchronized (c) {
this.comboFields = c.comboFields;
this.order = c.order == null ? null : new ArrayList<Path>(c.order);
this.itemsOrder = c.itemsOrder;
 
this.fieldSeparator = c.fieldSeparator;
214,6 → 236,10
}
}
 
private final SQLElementDirectory getDirectory() {
return getDirectory(this.dir);
}
 
@Override
public ComboSQLRequest toUnmodifiable() {
return this.toUnmodifiableP(this.getClass());
231,27 → 257,6
return new ComboSQLRequest(this, forFreeze);
}
 
public final void setFields(Collection<String> fields) {
final List<SQLField> l = new ArrayList<SQLField>();
for (final String fName : fields) {
l.add(this.getPrimaryTable().getField(fName));
}
setSQLFieldsUnsafe(l);
}
 
public final void setSQLFields(Collection<SQLField> fields) {
for (final SQLField f : fields)
if (f.getTable() != getPrimaryTable())
throw new IllegalArgumentException("Not in " + getPrimaryTable() + " : " + f);
setSQLFieldsUnsafe(new ArrayList<SQLField>(fields));
}
 
private synchronized void setSQLFieldsUnsafe(List<SQLField> fields) {
checkFrozen();
this.comboFields = Collections.unmodifiableList(fields);
this.clearGraph();
}
 
/**
* Set the label of the undefined row. If <code>null</code> (the default) then the undefined
* will not be fetched, otherwise it will and its label will be <code>undefLabel</code>.
281,7 → 286,7
*/
public final IComboSelectionItem getComboItem(int id) {
// historically this method didn't use the cache
final List<IComboSelectionItem> res = getComboItems(id, false, false);
final List<IComboSelectionItem> res = getComboItems(id, null, null, null, false, false);
return getSole(res, id);
}
 
290,17 → 295,20
}
 
public final List<IComboSelectionItem> getComboItems(final boolean readCache) {
return this.getComboItems(null, readCache, true);
return this.getComboItems(readCache, null, null, null);
}
 
private final List<IComboSelectionItem> getComboItems(final Number id, final boolean readCache, final boolean writeCache) {
if (this.getFields().isEmpty())
throw new IllegalStateException("Empty fields");
public final List<IComboSelectionItem> getComboItems(final boolean readCache, final List<String> searchQuery, final Locale locale, final Where searchForceInclude) {
return this.getComboItems(null, searchQuery, locale, searchForceInclude, readCache, true);
}
 
private final List<IComboSelectionItem> getComboItems(final Number id, final List<String> searchQuery, final Locale locale, final Where searchForceInclude, final boolean readCache,
final boolean writeCache) {
final Where w = id == null ? null : new Where(this.getPrimaryTable().getKey(), "=", id);
 
// this encapsulates a snapshot of our state, so this method doesn't access any of our
// fields and doesn't need to be synchronized
final CacheKey cacheKey = getCacheKey(w);
final CacheKey cacheKey = getCacheKey(w, searchQuery, locale, searchForceInclude);
final SQLRowValuesListFetcher comboSelect = cacheKey.fetcher;
final CacheResult<List<IComboSelectionItem>> l = cache.check(cacheKey, readCache, writeCache, comboSelect.getGraph().getGraph().getTables());
if (l.getState() == CacheResult.State.INTERRUPTED)
311,10 → 319,10
try {
// group fields by ancestor, need not be part of CacheKey assuming parent-child
// relations don't change
final List<Tuple2<Path, List<FieldPath>>> ancestors = ElementComboBoxUtils.expandGroupBy(cacheKey.graph, Configuration.getInstance().getDirectory());
final List<Tuple2<Path, List<FieldPath>>> ancestors = ComboSQLRequestUtils.expandGroupBy(cacheKey.graph, getDirectory());
final List<IComboSelectionItem> result = new ArrayList<IComboSelectionItem>();
// SQLRowValuesListFetcher doesn't cache
for (final SQLRowValues vals : comboSelect.fetch(w)) {
for (final SQLRowValues vals : comboSelect.fetch(w, cacheKey.selTransformer, null)) {
if (Thread.currentThread().isInterrupted())
throw new RTInterruptedException("interrupted in fill");
// each item should be created with the same state and since it will be put in
336,11 → 344,12
}
 
protected final CacheKey getCacheKey() {
return getCacheKey(null);
return getCacheKey(null, null, null, null);
}
 
private final synchronized CacheKey getCacheKey(final Where w) {
return new CacheKey(this.getGraph(), this.getFetcher(), w, this.fieldSeparator, this.undefLabel, this.customizeItem, this.keepRows, this.itemsOrder);
private final synchronized CacheKey getCacheKey(final Where w, final List<String> searchQuery, final Locale l, final Where searchForceInclude) {
return new CacheKey(this.getGraph(), this.getFetcher(), w, this.createSearchTransformer(searchQuery, l, searchForceInclude), this.fieldSeparator, this.undefLabel, this.customizeItem,
this.keepRows, this.itemsOrder);
}
 
@Override
350,12 → 359,26
}
 
@Override
protected synchronized final List<Path> getOrder() {
if (this.order != null)
return this.order;
protected Collection<SearchField> getDefaultSearchFields() {
final List<SearchField> res = new ArrayList<SearchField>();
final List<Tuple2<Path, List<FieldPath>>> expandGroupBy = ComboSQLRequestUtils.expandGroupBy(getGraph(), getDirectory());
int rank = 10;
final ListIterator<Tuple2<Path, List<FieldPath>>> iter = expandGroupBy.listIterator(expandGroupBy.size());
assert !iter.hasNext();
while (iter.hasPrevious()) {
final Tuple2<Path, List<FieldPath>> element = iter.previous();
for (final FieldPath fp : element.get1()) {
res.add(new SearchField(fp, SQLSearchMode.CONTAINS, rank));
}
rank *= 5;
}
return res;
}
 
@Override
protected List<Path> getDefaultOrder() {
// order the combo by ancestors
final List<Tuple2<Path, List<FieldPath>>> expandGroupBy = ElementComboBoxUtils.expandGroupBy(getGraph(), Configuration.getInstance().getDirectory());
final List<Tuple2<Path, List<FieldPath>>> expandGroupBy = ComboSQLRequestUtils.expandGroupBy(getGraph(), getDirectory());
final List<Path> res = new ArrayList<Path>(expandGroupBy.size());
for (final Tuple2<Path, List<FieldPath>> ancestor : expandGroupBy)
res.add(0, ancestor.get0());
362,17 → 385,6
return res;
}
 
/**
* Change the ordering of this combo. By default this is ordered by ancestors.
*
* @param l the list of tables, <code>null</code> to restore the default.
*/
public synchronized final void setOrder(List<Path> l) {
checkFrozen();
this.order = Collections.unmodifiableList(new ArrayList<Path>(l));
this.clearGraph();
}
 
public final void setNaturalItemsOrder(final boolean b) {
this.setItemsOrder(b ? CompareUtils.<IComboSelectionItem> naturalOrder() : null);
}
422,11 → 434,6
return res;
}
 
@Override
protected final TransfFieldExpander getShowAs() {
return this.exp;
}
 
/**
* Renvoie la valeur du champ sous forme de String. De plus est sensé faire quelques
* conversions, eg traduire les booléens en "oui" "non".
446,11 → 453,6
return result;
}
 
@Override
public synchronized final List<SQLField> getFields() {
return this.comboFields;
}
 
/**
* Set the string that is used to join the fields of a row.
*
/trunk/OpenConcerto/src/org/openconcerto/sql/request/UpdateBuilder.java
15,6 → 15,7
 
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
 
import org.openconcerto.sql.model.SQLBase;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLName;
62,6 → 63,10
return this.t;
}
 
public final SQLSyntax getSyntax() {
return SQLSyntax.get(this.getTable());
}
 
private final void checkField(final String field) {
checkField(field, getTable());
}
242,7 → 247,7
computedWhere = w.and(computedWhere);
}
final String w = computedWhere == null ? "" : "\nWHERE " + computedWhere.getClause();
return "UPDATE " + this.getTable().getServer().getSQLSystem().getSyntax().getUpdate(this.getTable(), unmodifiableList(computedTables), unmodifiableMap(this.fields)) + w;
return "UPDATE " + this.getSyntax().getUpdate(this.getTable(), unmodifiableList(computedTables), unmodifiableMap(this.fields)) + w;
}
 
@Override
/trunk/OpenConcerto/src/org/openconcerto/sql/request/Inserter.java
13,10 → 13,13
package org.openconcerto.sql.request;
 
import org.openconcerto.sql.model.ColumnListHandlerGeneric;
import org.openconcerto.sql.model.ConnectionHandlerNoSetup;
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.model.DBSystemRoot;
import org.openconcerto.sql.model.ListListHandlerGeneric;
import org.openconcerto.sql.model.SQLDataSource;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLName;
import org.openconcerto.sql.model.SQLSystem;
import org.openconcerto.sql.model.SQLTable;
26,7 → 29,9
 
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
 
/**
* Allow to insert some rows into a table and get some feedback.
40,6 → 45,12
}
 
public static final class Insertion<T> {
 
// syntactic sugar to allow new Insertion<?>()
static public final <I> Insertion<I> create(List<I> res, int count) {
return new Insertion<I>(res, count);
}
 
private final List<T> list;
private final int count;
 
73,12 → 84,24
 
private static final String[] EMPTY_ARRAY = new String[0];
 
private static List<Class<?>> getPKTypes(SQLTable t) {
final Set<SQLField> pk = t.getPrimaryKeys();
final List<Class<?>> res = new ArrayList<Class<?>>(pk.size());
for (final SQLField f : pk) {
res.add(f.getType().getJavaType());
}
return res;
}
 
private final List<String> pk;
private final List<Class<?>> pkTypes;
private final DBSystemRoot sysRoot;
private final SQLName tableName;
 
public Inserter(final SQLTable t) {
this(t.getDBSystemRoot(), t.getSQLName(), t.getPKsNames());
// pass types since we can't always rely on ResultSet.getObject() for generated keys (e.g.
// MySQL always return BigInt no matter the actual type of the keys)
this(t.getDBSystemRoot(), t.getSQLName(), t.getPKsNames(), getPKTypes(t));
}
 
public Inserter(final SQLCreateTable t) {
90,6 → 113,10
}
 
public Inserter(final DBSystemRoot sysRoot, final SQLName tableName, final List<String> pk) {
this(sysRoot, tableName, pk, null);
}
 
public Inserter(final DBSystemRoot sysRoot, final SQLName tableName, final List<String> pk, final List<Class<?>> pkTypes) {
super();
if (sysRoot == null || tableName == null)
throw new NullPointerException();
96,6 → 123,9
this.sysRoot = sysRoot;
this.tableName = tableName;
this.pk = pk;
this.pkTypes = pkTypes == null ? null : new ArrayList<Class<?>>(pkTypes);
if (this.pkTypes != null && this.pkTypes.size() != this.pk.size())
throw new IllegalArgumentException("Size mismatch");
}
 
protected final SQLSystem getSystem() {
119,8 → 149,8
* @throws SQLException if an error occurs while inserting.
*/
@SuppressWarnings("unchecked")
public final Insertion<Object[]> insertReturnAllFields(final String sql) throws SQLException {
return (Insertion<Object[]>) insert(sql, ReturnMode.ALL_FIELDS, true);
public final Insertion<List<?>> insertReturnAllFields(final String sql) throws SQLException {
return (Insertion<List<?>>) insert(sql, ReturnMode.ALL_FIELDS, true);
}
 
/**
186,14 → 216,20
if (dontGetGK) {
list = null;
} else {
list = (List<?>) (mode == ReturnMode.FIRST_FIELD ? SQLDataSource.COLUMN_LIST_HANDLER : SQLDataSource.ARRAY_LIST_HANDLER).handle(stmt.getGeneratedKeys());
if (Inserter.this.pkTypes == null) {
list = (List<?>) (mode == ReturnMode.FIRST_FIELD ? SQLDataSource.COLUMN_LIST_HANDLER : SQLDataSource.LIST_LIST_HANDLER).handle(stmt.getGeneratedKeys());
} else {
if (mode == ReturnMode.FIRST_FIELD) {
list = ColumnListHandlerGeneric.create(1, Inserter.this.pkTypes.get(0)).handle(stmt.getGeneratedKeys());
} else {
list = ListListHandlerGeneric.create(Object.class, Inserter.this.pkTypes).handle(stmt.getGeneratedKeys());
}
}
assert list.size() <= count;
if (requireAllRows && list.size() != count)
throw new IllegalStateException("Missing keys");
}
@SuppressWarnings("unchecked")
final Insertion<?> res = new Insertion<Object>((List<Object>) list, count);
return res;
return Insertion.create(list, count);
} finally {
stmt.close();
}
/trunk/OpenConcerto/src/org/openconcerto/sql/request/FilteredFillSQLRequest.java
89,8 → 89,8
};
}
 
public FilteredFillSQLRequest(final SQLTable primaryTable, Where w) {
super(primaryTable, w);
public FilteredFillSQLRequest(final SQLRowValues graph, Where w) {
super(graph, w);
this.filter = getDefaultFilter();
this.filterInfo = Tuple2.create(null, null);
this.setFilterEnabled(true);
/trunk/OpenConcerto/src/org/openconcerto/sql/request/BaseSQLRequest.java
13,14 → 13,6
package org.openconcerto.sql.request;
 
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLFieldsSet;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTableModifiedListener;
 
import java.util.Collection;
import java.util.Set;
 
import net.jcip.annotations.ThreadSafe;
 
@ThreadSafe
29,27 → 21,4
public BaseSQLRequest() {
super();
}
 
public final Set<SQLTable> getTables() {
return new SQLFieldsSet(this.getAllFields()).getTables();
}
 
/**
* Tous les champs qui intéressent cette requête. Souvent les champs après expansion.
*
* @return les champs qui intéressent cette requête.
*/
protected abstract Collection<SQLField> getAllFields();
 
public final void addTableListener(SQLTableModifiedListener l) {
for (final SQLTable t : this.getTables()) {
t.addTableModifiedListener(l);
}
}
 
public final void removeTableListener(SQLTableModifiedListener l) {
for (final SQLTable t : this.getTables()) {
t.removeTableModifiedListener(l);
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/request/ListSQLRequest.java
21,8 → 21,6
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
 
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
 
import net.jcip.annotations.ThreadSafe;
30,60 → 28,41
@ThreadSafe
public class ListSQLRequest extends FilteredFillSQLRequest {
 
// les champs à afficher (avant expansion)
// immutable
private final List<SQLField> listFields;
private static final FieldExpander getExpander(final FieldExpander showAs) {
final FieldExpander res;
if (showAs != null) {
res = showAs;
} else {
final Configuration conf = Configuration.getInstance();
if (conf == null) {
res = FieldExpander.getEmpty();
} else {
res = conf.getShowAs();
}
}
return res;
}
 
private final FieldExpander showAs;
 
public ListSQLRequest(SQLTable table, List fieldss) {
public ListSQLRequest(SQLTable table, List<String> fieldss) {
this(table, fieldss, null);
}
 
// fieldss : list de String (préferré) ou de SQLField
public ListSQLRequest(SQLTable table, List fieldss, Where where) {
public ListSQLRequest(SQLTable table, List<String> fieldss, Where where) {
this(table, fieldss, where, null);
}
 
public ListSQLRequest(SQLTable table, List fieldss, Where where, final FieldExpander showAs) {
super(table, where);
if (!this.getPrimaryTable().isOrdered())
throw new IllegalArgumentException(table + " is not ordered.");
 
final List<SQLField> tmpList = new ArrayList<SQLField>();
for (final Object field : fieldss) {
final SQLField f;
if (field instanceof String)
f = this.getPrimaryTable().getField((String) field);
else if (field instanceof SQLField) {
final SQLField fToCheck = (SQLField) field;
if (fToCheck.getTable().equals(this.getPrimaryTable()))
f = fToCheck;
else
throw new IllegalArgumentException("field " + fToCheck + " not part of the primary table : " + this.getPrimaryTable());
} else
throw new IllegalArgumentException("must be a fieldname or a SQLField but got : " + field);
 
tmpList.add(f);
public ListSQLRequest(SQLTable table, List<String> fieldss, Where where, final FieldExpander showAs) {
this(computeGraph(table, fieldss, getExpander(showAs)), where);
}
this.listFields = Collections.unmodifiableList(tmpList);
 
if (showAs != null) {
this.showAs = showAs;
} else {
final Configuration conf = Configuration.getInstance();
if (conf == null) {
this.showAs = FieldExpander.getEmpty();
} else {
this.showAs = conf.getShowAs();
public ListSQLRequest(final SQLRowValues graph, final Where where) {
super(graph, where);
if (!this.getPrimaryTable().isOrdered())
throw new IllegalArgumentException(this.getPrimaryTable() + " is not ordered.");
}
}
}
 
protected ListSQLRequest(ListSQLRequest req, final boolean freeze) {
super(req, freeze);
this.listFields = req.listFields;
this.showAs = req.showAs;
}
 
// wasFrozen() : our showAs might change but our fetcher won't, MAYBE remove final modifier and
94,12 → 73,9
return this.toUnmodifiableP(ListSQLRequest.class);
}
 
@Override
public ListSQLRequest clone() {
synchronized (this) {
return this.clone(false);
}
}
// can't implement Cloneable since the contract is to return an object of the same class. But
// this would either prevent the use of anonymous classes if we call clone(false), or require a
// call to super.clone() and less final fields.
 
@Override
protected ListSQLRequest clone(boolean forFreeze) {
106,23 → 82,9
return new ListSQLRequest(this, forFreeze);
}
 
// MAYBE use changeGraphToFetch()
@Override
protected FieldExpander getShowAs() {
return this.showAs;
}
 
/*
* (non-Javadoc)
*
* @see org.openconcerto.devis.request.BaseSQLRequest#getFields()
*/
@Override
public final List<SQLField> getFields() {
return this.listFields;
}
 
@Override
protected void customizeToFetch(SQLRowValues graphToFetch) {
protected final void customizeToFetch(SQLRowValues graphToFetch) {
super.customizeToFetch(graphToFetch);
addField(graphToFetch, getPrimaryTable().getCreationDateField());
addField(graphToFetch, getPrimaryTable().getCreationUserField());
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SQLElementDirectory.java
20,6 → 20,7
import org.openconcerto.sql.model.DBStructureItemNotFound;
import org.openconcerto.sql.model.SQLName;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.request.SQLFieldTranslator;
import org.openconcerto.utils.CollectionMap2;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.SetMap;
43,6 → 44,8
 
import org.jdom2.JDOMException;
 
import net.jcip.annotations.GuardedBy;
 
/**
* Directory of SQLElement by table.
*
82,6 → 85,8
private String phrasesPkgName;
private final Map<String, SQLElementNames> elementNames;
 
@GuardedBy("this")
private SQLFieldTranslator translator;
private final ShowAs showAs;
 
public SQLElementDirectory() {
104,6 → 109,16
return this.showAs;
}
 
public final synchronized void setTranslator(SQLFieldTranslator translator) {
if (translator.getDirectory() != this)
throw new IllegalArgumentException("Not for this : " + translator);
this.translator = translator;
}
 
public synchronized final SQLFieldTranslator getTranslator() {
return this.translator;
}
 
private static <K> SQLTable getSoleTable(SetMap<K, SQLTable> m, K key) throws IllegalArgumentException {
final Collection<SQLTable> res = m.getNonNull(key);
if (res.size() > 1)
121,6 → 136,7
if (!this.contains(elem.getTable()))
this.addSQLElement(elem);
}
this.translator.putAll(o.getTranslator());
}
 
/**
/trunk/OpenConcerto/src/org/openconcerto/sql/element/GroupSQLComponent.java
60,6 → 60,8
 
public class GroupSQLComponent extends BaseSQLComponent {
 
public static final String ITEM_RIGHT_CODE = "GROUP_ITEM_SHOW";
 
private final Group group;
private final int columns = 2;
private final Map<String, JComponent> labels = new HashMap<String, JComponent>();
462,8 → 464,8
JComponent label = this.labels.get(id);
if (label == null) {
label = createLabel(id);
if (!UserRightsManager.getCurrentUserRights().haveRight("GROUP_ITEM_SHOW", id)
|| !UserRightsManager.getCurrentUserRights().haveRight("GROUP_ITEM_SHOW", getElement().getTable().getName() + "." + id)) {
if (!UserRightsManager.getCurrentUserRights().haveRight(ITEM_RIGHT_CODE, id)
|| !UserRightsManager.getCurrentUserRights().haveRight(ITEM_RIGHT_CODE, getElement().getTable().getName() + "." + id)) {
label.setVisible(false);
}
 
475,7 → 477,7
return label;
}
 
private RowItemDesc getRIVDescForId(final String id) {
public RowItemDesc getRIVDescForId(final String id) {
if (TranslationManager.getInstance().getLocale() != null) {
final String t = TranslationManager.getInstance().getTranslationForItem(id);
if (t != null) {
501,8 → 503,8
JComponent editor = this.editors.get(id);
if (editor == null) {
editor = createEditor(id);
if (!UserRightsManager.getCurrentUserRights().haveRight("GROUP_ITEM_SHOW", id)
|| !UserRightsManager.getCurrentUserRights().haveRight("GROUP_ITEM_SHOW", getElement().getTable().getName() + "." + id)) {
if (!UserRightsManager.getCurrentUserRights().haveRight(ITEM_RIGHT_CODE, id)
|| !UserRightsManager.getCurrentUserRights().haveRight(ITEM_RIGHT_CODE, getElement().getTable().getName() + "." + id)) {
editor.setVisible(false);
}
this.editors.put(id, editor);
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SQLElement.java
18,6 → 18,7
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.FieldExpander;
import org.openconcerto.sql.Log;
import org.openconcerto.sql.PropsConfiguration;
import org.openconcerto.sql.TM;
import org.openconcerto.sql.element.SQLElementLink.LinkType;
import org.openconcerto.sql.element.TreesOfSQLRows.LinkToCut;
56,23 → 57,29
import org.openconcerto.sql.request.SQLCache;
import org.openconcerto.sql.request.SQLFieldTranslator;
import org.openconcerto.sql.sqlobject.SQLTextCombo;
import org.openconcerto.sql.ui.light.ConvertorModifer;
import org.openconcerto.sql.ui.light.GroupToLightUIConvertor;
import org.openconcerto.sql.ui.light.LightEditFrame;
import org.openconcerto.sql.ui.light.LightUIPanelFiller;
import org.openconcerto.sql.ui.light.SavableCustomEditorProvider;
import org.openconcerto.sql.users.rights.UserRightsManager;
import org.openconcerto.sql.utils.SQLUtils;
import org.openconcerto.sql.utils.SQLUtils.SQLFactory;
import org.openconcerto.sql.view.EditFrame;
import org.openconcerto.sql.view.EditPanel.EditMode;
import org.openconcerto.sql.view.list.IListeAction;
import org.openconcerto.sql.view.list.SQLTableModelColumn;
import org.openconcerto.sql.view.list.SQLTableModelColumnPath;
import org.openconcerto.sql.view.list.SQLTableModelSource;
import org.openconcerto.sql.view.list.SQLTableModelSourceOffline;
import org.openconcerto.sql.view.list.SQLTableModelSourceOnline;
import org.openconcerto.ui.group.Group;
import org.openconcerto.ui.light.ColumnsSpec;
import org.openconcerto.ui.light.ComboValueConvertor;
import org.openconcerto.ui.light.CustomEditorProvider;
import org.openconcerto.ui.light.IntValueConvertor;
import org.openconcerto.ui.light.LightUIComboBox;
import org.openconcerto.ui.light.LightUIComboBoxElement;
import org.openconcerto.ui.light.LightUIElement;
import org.openconcerto.ui.light.Row;
import org.openconcerto.ui.light.LightUIFrame;
import org.openconcerto.ui.light.LightUIPanel;
import org.openconcerto.ui.light.StringValueConvertor;
import org.openconcerto.utils.CollectionMap2Itf.SetMapItf;
import org.openconcerto.utils.CollectionUtils;
274,47 → 281,114
}
 
/**
* Create a Row to put in a TableContent
* Get the group based on the edit mode
*
* @param sqlRow SQLRowValues used to create the Row
* @param allCols Columns attach to this SQLElement
* @param columnsSpec Columns of the LightUITable attach to this SQLElement
* @param editMode
* @return
*/
public Group getEditGroup(final EditMode editMode) {
if (editMode.equals(EditMode.CREATION)) {
return this.getGroupForCreation();
} else {
return this.getGroupForModification();
}
}
 
/**
* Override this function in an element to show default values in edit frame
*
* @return New Row created from the SQLRowValues
* @param token - The security token of session
*
* @return a default SQLRowValues
*/
public final Row createRowFromSQLRow(final SQLRowValues sqlRow, final List<SQLTableModelColumn> allCols, final ColumnsSpec columnsSpec) {
final Row row = new Row(sqlRow.getID());
public SQLRowValues createDefaultRowValues(final String token) {
return new SQLRowValues(getTable());
}
 
final List<Object> values = new ArrayList<Object>();
final int colSize = allCols.size();
for (int i = 0; i < colSize; i++) {
final String columnId = columnsSpec.getColumn(i).getId();
final SQLTableModelColumn col = getColumnFromId(allCols, columnId);
this.configureConverter(col);
if (col != null) {
Object value = col.show(sqlRow);
if (col.getLightUIrenderer() != null) {
value = col.getLightUIrenderer().getLightUIElement(value, 0, i);
/**
* Create the edition frame for this SQLElement
*
* @param configuration current configuration
* @param parentFrame parent frame of the edit frame
* @param editMode edition mode (CREATION, MODIFICATION, READONLY)
* @param sqlRow SQLRowAccessor use for fill the edition frame
* @param sessionSecurityToken String, use for find session with an instance of LightServer
* @return the edition frame of this SQLElement
*/
public LightEditFrame createEditFrame(final PropsConfiguration configuration, final LightUIFrame parentFrame, final EditMode editMode, final SQLRowAccessor sqlRow,
final String sessionSecurityToken) {
final Group editGroup = this.getEditGroup(editMode);
if (editGroup == null) {
Log.get().severe("The edit group is null for this element : " + this);
return null;
}
values.add(value);
} else {
throw new IllegalArgumentException("column " + columnId + " is in ColumnsSpec but it is not found in SQLTableModelColumn");
 
final GroupToLightUIConvertor convertor = this.getGroupToLightUIConvertor(configuration, editMode, sqlRow, sessionSecurityToken);
final LightEditFrame editFrame = convertor.convert(editGroup, sqlRow, parentFrame, editMode);
 
if (editMode.equals(EditMode.CREATION)) {
editFrame.createTitlePanel(this.getCreationFrameTitle());
} else if (editMode.equals(EditMode.MODIFICATION)) {
editFrame.createTitlePanel(this.getModificationFrameTitle(sqlRow));
new LightUIPanelFiller(editFrame.getFirstChild(LightUIPanel.class)).fillFromRow(configuration, sqlRow);
} else if (editMode.equals(EditMode.READONLY)) {
editFrame.createTitlePanel(this.getReadOnlyFrameTitle(sqlRow));
new LightUIPanelFiller(editFrame.getFirstChild(LightUIPanel.class)).fillFromRow(configuration, sqlRow);
}
 
this.setEditFrameModifiers(editFrame, sessionSecurityToken);
 
return editFrame;
}
row.setValues(values);
return row;
 
/**
* Get title for read only mode
*
* @param sqlRow - SQLRowValues use for fill the edition frame
* @return The title for read only mode
*/
protected String getReadOnlyFrameTitle(final SQLRowAccessor sqlRow) {
return EditFrame.getReadOnlyMessage(this);
}
 
public final SQLTableModelColumn getColumnFromId(final List<SQLTableModelColumn> allCols, final String columnId) {
final int columnSize = allCols.size();
for (int i = 0; i < columnSize; i++) {
final SQLTableModelColumn tableModelColumn = allCols.get(i);
if (tableModelColumn.getIdentifier().equals(columnId)) {
return tableModelColumn;
/**
* Get title for modification mode
*
* @param sqlRow - SQLRowValues use for fill the edition frame
* @return The title for read only mode
*/
protected String getModificationFrameTitle(final SQLRowAccessor sqlRow) {
return EditFrame.getModifyMessage(this);
}
 
/**
* Get title for creation mode
*
* @param sqlRow - SQLRowValues use for fill the edition frame
* @return The title for read only mode
*/
protected String getCreationFrameTitle() {
return EditFrame.getCreateMessage(this);
}
return null;
 
/**
*
* @param configuration - The user SQL configuration
* @param editMode - Edit mode of the frame
* @param sqlRow - The row to update
* @param token - The session security token
*
* @return An initialized GroupToLightUIConvertor
*/
public GroupToLightUIConvertor getGroupToLightUIConvertor(final PropsConfiguration configuration, final EditMode editMode, final SQLRowAccessor sqlRow, final String token) {
final GroupToLightUIConvertor convertor = new GroupToLightUIConvertor(configuration);
if (editMode.equals(EditMode.CREATION)) {
convertor.putAllCustomEditorProvider(this.getCustomEditorProviderForCreation(configuration, token));
} else {
convertor.putAllCustomEditorProvider(this.getCustomEditorProviderForModification(configuration, sqlRow, token));
}
return convertor;
}
 
/**
* Override this function in an element and put new value in map for use ComboValueConvertor.
323,7 → 397,7
* @return Map which contains all ComboValueConvertors use for this SQLElement edition. Key: ID
* of group item / Value: ComboValueConvertor
*/
// FIXME: voir avec Sylvain pour les conversions
// TODO: use renderer instead of ValueConvertor
public Map<String, ComboValueConvertor<?>> getComboConvertors() {
return new HashMap<String, ComboValueConvertor<?>>();
}
332,81 → 406,167
* Override this function in an element and put new value in map for use ConvertorModifier. This
* one allow you to apply some change on LightUIElement before send it to client
*
* @return Map which contains all ConvertorModifier use for this SQLElement edition. Key: ID of
* group item / Value: ConvertorModeifier
*/
public Map<String, ConvertorModifer> getConvertorModifiers() {
return new HashMap<String, ConvertorModifer>();
// TODO: implement with IClosure
public void setEditFrameModifiers(final LightEditFrame frame, final String sessionToken) {
}
 
public Map<String, CustomEditorProvider> getCustomEditorProviderForCreation(final Configuration configuration, final long userId) {
return this.getDefaultCustomEditorProvider(configuration, null, userId);
public final Map<String, CustomEditorProvider> getCustomEditorProviderForCreation(final Configuration configuration, final String sessionToken) {
final Map<String, CustomEditorProvider> map = this.getDefaultCustomEditorProvider(configuration, null, sessionToken);
map.putAll(this._getCustomEditorProviderForCreation(configuration, sessionToken));
return map;
}
 
public Map<String, CustomEditorProvider> getCustomEditorProviderForModification(final Configuration configuration, final SQLRowValues sqlRow, final long userId) {
return this.getDefaultCustomEditorProvider(configuration, sqlRow, userId);
public final Map<String, CustomEditorProvider> getCustomEditorProviderForModification(final Configuration configuration, final SQLRowAccessor sqlRow, final String sessionToken) {
final Map<String, CustomEditorProvider> map = this.getDefaultCustomEditorProvider(configuration, sqlRow, sessionToken);
map.putAll(this._getCustomEditorProviderForModification(configuration, sqlRow, sessionToken));
return map;
}
 
protected Map<String, CustomEditorProvider> getDefaultCustomEditorProvider(final Configuration configuration, final SQLRowValues sqlRow, final long userId) {
protected Map<String, CustomEditorProvider> _getCustomEditorProviderForCreation(final Configuration configuration, final String sessionToken) {
return new HashMap<String, CustomEditorProvider>();
}
 
protected Map<String, CustomEditorProvider> _getCustomEditorProviderForModification(final Configuration configuration, final SQLRowAccessor sqlRow, final String sessionToken) {
return new HashMap<String, CustomEditorProvider>();
}
 
protected Map<String, CustomEditorProvider> _getDefaultCustomEditorProvider(final Configuration configuration, final SQLRowAccessor sqlRow, final String sessionToken) {
return new HashMap<String, CustomEditorProvider>();
}
 
private final Map<String, CustomEditorProvider> getDefaultCustomEditorProvider(final Configuration configuration, final SQLRowAccessor sqlRow, final String sessionToken) {
final Map<String, ComboValueConvertor<?>> comboConvertors = this.getComboConvertors();
final Map<String, CustomEditorProvider> result = new HashMap<String, CustomEditorProvider>();
for (final Entry<String, ComboValueConvertor<?>> entry : comboConvertors.entrySet()) {
result.put(entry.getKey(), new CustomEditorProvider() {
result.put(entry.getKey(), new SavableCustomEditorProvider() {
final ComboValueConvertor<?> convertor = entry.getValue();
 
@Override
public LightUIElement createUIElement(final String elementId) {
final LightUIComboBox uiCombo = new LightUIComboBox(elementId);
this.convertor.fillCombo(uiCombo);
 
if (sqlRow != null) {
if (sqlRow == null) {
this.convertor.fillCombo(uiCombo, null);
} else {
final SQLField field = configuration.getFieldMapper().getSQLFieldForItem(elementId);
LightUIComboBoxElement selectedValue = null;
if (this.convertor instanceof StringValueConvertor) {
final StringValueConvertor stringConvertor = (StringValueConvertor) this.convertor;
final String value = sqlRow.getString(field.getFieldName());
if (value != null) {
selectedValue = new LightUIComboBoxElement(stringConvertor.getIndexFromId(value));
selectedValue.setValue1(stringConvertor.convert(value));
}
((StringValueConvertor) this.convertor).fillCombo(uiCombo, sqlRow.getString(field.getFieldName()));
} else if (this.convertor instanceof IntValueConvertor) {
final IntValueConvertor intConvertor = (IntValueConvertor) this.convertor;
if (sqlRow.getObject(field.getFieldName()) != null) {
final int value = sqlRow.getInt(field.getFieldName());
selectedValue = new LightUIComboBoxElement(value);
selectedValue.setValue1(intConvertor.convert(value));
if (sqlRow.getObject(field.getFieldName()) == null) {
this.convertor.fillCombo(uiCombo, null);
} else {
((IntValueConvertor) this.convertor).fillCombo(uiCombo, sqlRow.getInt(field.getFieldName()));
}
}
uiCombo.setSelectedValue(selectedValue);
}
return uiCombo;
}
 
@Override
protected void _save(final SQLRowValues sqlRow, final SQLField sqlField, final LightUIElement uiElement) {
final LightUIComboBox combo = (LightUIComboBox) uiElement;
if (combo.hasSelectedValue()) {
if (this.convertor instanceof StringValueConvertor) {
sqlRow.put(sqlField.getName(), ((StringValueConvertor) this.convertor).getIdFromIndex(combo.getSelectedValue().getId()));
} else if (this.convertor instanceof IntValueConvertor) {
sqlRow.put(sqlField.getName(), combo.getSelectedValue().getId());
} else {
throw new IllegalArgumentException("the save is not implemented for the class: " + this.convertor.getClass().getName() + " - ui element id: " + uiElement.getId());
}
} else {
sqlRow.put(sqlField.getName(), null);
}
}
});
}
result.putAll(this._getDefaultCustomEditorProvider(configuration, sqlRow, sessionToken));
return result;
}
 
/**
* Add value converter on a SQLTableModelColumn to change the attached value in SQLRowValues
* when createRowFromRowValues is called
* Override this function in an element to execute some code just after inserted new row in
* database
*
* @param col SQLTableModelColumn that you want change the value
* @param editFrame - The edit frame of this SQLRow
* @param sqlRow - The row which was just inserted
* @param sessionToken Security token of session which allow to find session in LightServer
* instance
*
* @throws Exception
*/
protected void configureConverter(final SQLTableModelColumn col) {
public void doAfterLightInsert(final LightEditFrame editFrame, final SQLRow sqlRow, final String sessionToken) throws Exception {
 
}
 
/**
* Override this function in an element to execute some code just after inserted new row in
* Override this function in an element to execute some code just after deleted a row in
* database
*
* @param row The row which was just inserted
* @param frame - The current frame
* @param sqlRow - The row which was deleted
* @param sessionToken Security token of session which allow to find session in LightServer
* instance
*
* @throws Exception
*/
public void doAfterLightInsert(SQLRow row) {
public void doAfterLightDelete(final LightUIFrame frame, final SQLRowValues sqlRow, final String sessionToken) throws Exception {
 
}
 
/**
* Override this function in an element to execute some code before inserted new row in database
*
* @param frame - The current frame
* @param sqlRow - The row which will be deleted
* @param sessionToken - Security token of session which allow to find session in LightServer
* instance
*
* @throws Exception
*/
public void doBeforeLightDelete(final LightUIFrame frame, final SQLRowValues sqlRow, final String sessionToken) throws Exception {
 
}
 
/**
* Override this function in an element to execute some code before inserted new row in database
*
* @param editFrame - The edit frame of this SQLRowValues
* @param sqlRow - The row which was just inserted
* @param sessionToken - Security token of session which allow to find session in LightServer
* instance
*
* @throws Exception
*/
public void doBeforeLightInsert(final LightEditFrame editFrame, final SQLRowValues sqlRow, final String sessionToken) throws Exception {
 
}
 
/**
* Get ShowAs values of this SQLElement
*
* @param id - The id which you want to expand
*
* @return A SQLRowValues with data
*/
public SQLRowValues getValuesOfShowAs(final Number id) {
final SQLRowValues tmp = new SQLRowValues(this.getTable());
final ListMap<String, String> showAs = this.getShowAs();
 
for (final List<String> listStr : showAs.values()) {
tmp.putNulls(listStr);
}
this.getDirectory().getShowAs().expand(tmp);
 
final SQLRowValues fetched = SQLRowValuesListFetcher.create(tmp).fetchOne(id);
if (fetched == null) {
throw new IllegalArgumentException("Impossible to find Row in database - table: " + this.getTable().getName() + ", id: " + id);
}
 
return fetched;
}
 
/**
* Must be called if foreign/referent keys are added or removed.
*/
public synchronized void resetRelationships() {
1581,10 → 1741,19
}
}
 
protected ComboSQLRequest createComboRequest() {
return new ComboSQLRequest(this.getTable(), this.getComboFields());
public final ComboSQLRequest createComboRequest() {
return this.createComboRequest(null, null);
}
 
public final ComboSQLRequest createComboRequest(final List<String> fields, final Where w) {
final ComboSQLRequest res = new ComboSQLRequest(this.getTable(), fields == null ? this.getComboFields() : fields, w, this.getDirectory());
this._initComboRequest(res);
return res;
}
 
protected void _initComboRequest(final ComboSQLRequest req) {
}
 
// not all elements need to be displayed in combos so don't make this method abstract
protected List<String> getComboFields() {
return this.getListFields();
1668,34 → 1837,45
public final synchronized SQLTableModelSourceOnline getTableSource(final boolean create) {
if (!create) {
if (this.tableSrc == null) {
this.tableSrc = createAndInitTableSource();
this.tableSrc = createTableSource();
}
return this.tableSrc;
} else
return this.createAndInitTableSource();
return this.createTableSource();
}
 
public final SQLTableModelSourceOnline createTableSource() {
return createTableSource((Where) null);
}
 
public final SQLTableModelSourceOnline createTableSource(final List<String> fields) {
return initTableSource(new SQLTableModelSourceOnline(createListRequest(fields)));
return createTableSourceOnline(createListRequest(fields));
}
 
public final SQLTableModelSourceOnline createTableSource(final Where w) {
final SQLTableModelSourceOnline res = this.getTableSource(true);
res.getReq().setWhere(w);
return res;
return createTableSourceOnline(createListRequest(null, w, null));
}
 
private final SQLTableModelSourceOnline createAndInitTableSource() {
final SQLTableModelSourceOnline res = this.createTableSource();
public final SQLTableModelSourceOnline createTableSourceOnline(final ListSQLRequest req) {
return initTableSource(instantiateTableSourceOnline(req));
}
 
protected SQLTableModelSourceOnline instantiateTableSourceOnline(final ListSQLRequest req) {
return new SQLTableModelSourceOnline(req, this);
}
 
protected synchronized void _initTableSource(final SQLTableModelSource res) {
if (!this.additionalListCols.isEmpty())
res.getColumns().addAll(this.additionalListCols);
return initTableSource(res);
}
 
protected synchronized void _initTableSource(final SQLTableModelSourceOnline res) {
public final <S extends SQLTableModelSource> S initTableSource(final S res) {
return this.initTableSource(res, false);
}
 
public final synchronized SQLTableModelSourceOnline initTableSource(final SQLTableModelSourceOnline res) {
public final synchronized <S extends SQLTableModelSource> S initTableSource(final S res, final boolean minimal) {
// do init first since it can modify the columns
if (!minimal)
this._initTableSource(res);
// setEditable(false) on read only fields
// MAYBE setReadOnlyFields() on SQLTableModelSource, so that SQLTableModelLinesSource can
1708,12 → 1888,22
return res;
}
 
protected SQLTableModelSourceOnline createTableSource() {
// also create a new ListSQLRequest, otherwise it's a backdoor to change the behaviour of
// the new TableModelSource
return new SQLTableModelSourceOnline(this.createListRequest());
public final SQLTableModelSourceOffline createTableSourceOffline() {
return createTableSourceOfflineWithWhere(null);
}
 
public final SQLTableModelSourceOffline createTableSourceOfflineWithWhere(final Where w) {
return createTableSourceOffline(createListRequest(null, w, null));
}
 
public final SQLTableModelSourceOffline createTableSourceOffline(final ListSQLRequest req) {
return initTableSource(instantiateTableSourceOffline(req));
}
 
protected SQLTableModelSourceOffline instantiateTableSourceOffline(final ListSQLRequest req) {
return new SQLTableModelSourceOffline(req, this);
}
 
/**
* Whether to cache our tableSource.
*
2287,7 → 2477,7
if (parentLink.isJoin()) {
sel.addSelect(joinPK, null, "joinID");
} else {
sel.addRawSelect(syntax.cast("NULL", joinPK.getTypeDecl()), "joinID");
sel.addRawSelect(syntax.cast("NULL", joinPK.getType()), "joinID");
}
sel.addRawSelect(String.valueOf(listIter.previousIndex()), "fieldIndex");
sel.setArchivedPolicy(archiveMode);
/trunk/OpenConcerto/src/org/openconcerto/sql/element/BaseSQLComponent.java
24,8 → 24,6
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLSelect.LockStrength;
import org.openconcerto.sql.model.SQLType;
import org.openconcerto.sql.request.MutableRowItemView;
740,9 → 738,6
}
 
public void select(SQLRowAccessor r, Set<String> views) {
// FIXME force loading
if (r instanceof SQLRow)
((SQLRow) r).exists();
try {
// allow null to pass, ease some code (eg new ListOfSomething().getTable() even if we
// can't see the table)
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SQLComponent.java
52,7 → 52,7
final AlterTable res = new AlterTable(t);
if (!t.contains(READ_ONLY_FIELD)) {
// writable by default
res.addColumn(READ_ONLY_FIELD, "varchar(16) default " + t.getBase().quoteString(READ_WRITE_VALUE) + " NOT NULL");
res.addVarCharColumn(READ_ONLY_FIELD, 16, false, res.getSyntax().quoteString(READ_WRITE_VALUE), false);
res.addForeignColumn(READ_ONLY_USER_FIELD, UserManager.getInstance().getTable());
}
return res;
/trunk/OpenConcerto/src/org/openconcerto/sql/navigator/SQLBrowser.java
392,8 → 392,8
}
 
/**
* The selected rows of the last column which restricts the browser selection. Eg if the last
* column has "ALL" selected, return the selection of the previous column.
* The selected rows of the last column which restricts the browser selection. E.g. if the last
* column has "ALL" selected, return the selection of the previous column (except if searched).
*
* @return the last meaningful selection.
*/
403,21 → 403,25
}
 
/**
* The selected rows of all columns with meaningful selection, ie not ALL (except if searched)
* and not empty.
* The selected rows of the last column with a {@link #getLastMeaningfullRows() meaningful
* selection} and after that all of their ancestors.
*
* @return the selected rows of all columns.
* @return the selected rows and their ancestors, the root of the hierarchy last.
*/
public List<Set<SQLRow>> getMeaningfullRows() {
final SQLBrowserColumn<?, ?> col = getLastMeaningfullCol();
if (col == null)
if (col == null) {
return Collections.<Set<SQLRow>> emptyList();
else {
} else {
final List<Set<SQLRow>> res = new ArrayList<Set<SQLRow>>();
res.add(new HashSet<SQLRow>(col.getUserSelectedRows()));
SQLBrowserColumn<?, ?> c = col.previousRowsColumn();
while (c != null) {
res.add(c.getModel().getHighlighted());
// Use getHighlighted() instead of getUserSelectedRows() as we want ancestors, e.g.
// we don't want ALL but only the parent of the selection.
// highlighted becomes empty if the user selects a highlighted row.
final Set<SQLRow> highlighted = c.getModel().getHighlighted();
res.add(!highlighted.isEmpty() ? highlighted : new HashSet<SQLRow>(c.getUserSelectedRows()));
c = c.previousRowsColumn();
}
return res;
/trunk/OpenConcerto/src/org/openconcerto/sql/translation/messages_en.properties
129,6 → 129,9
infoPanel.dirs=Folders
infoPanel.logs=Logs
infoPanel.docs=Documents
infoPanel.dataDir=Data
infoPanel.prefsDir=Preferences
infoPanel.cacheDir=Cache
 
infoPanel.softwareTitle=Software
infoPanel.systemTitle=System information
/trunk/OpenConcerto/src/org/openconcerto/sql/translation/messages_fr.properties
130,6 → 130,9
infoPanel.dirs=Dossiers
infoPanel.logs=Journaux
infoPanel.docs=Documents
infoPanel.dataDir=Données
infoPanel.prefsDir=Préférences
infoPanel.cacheDir=Cache
 
infoPanel.softwareTitle=Logiciel
infoPanel.systemTitle=Informations système
/trunk/OpenConcerto/src/org/openconcerto/sql/model/MySQLBase.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/sql/model/PGSQLBase.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/sql/model/MSSQLBase.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/sql/model/StructureSource.java
169,7 → 169,7
// DatabaseMetaData param to avoid re-asking it
protected final Set<String> getJDBCSchemas(final DatabaseMetaData metaData) throws SQLException {
// getSchemas(this.getBase().getMDName(), null) not implemented by pg
final Set<String> res = new HashSet<String>((List) SQLDataSource.COLUMN_LIST_HANDLER.handle(metaData.getSchemas()));
final Set<String> res = new HashSet<String>(ColumnListHandlerGeneric.create(String.class).handle(metaData.getSchemas()));
// if db does not support schemas
if (res.isEmpty() && !this.getBase().getServer().getSQLSystem().getLevels().contains(HierarchyLevel.SQLSCHEMA))
res.add(null);
/trunk/OpenConcerto/src/org/openconcerto/sql/model/ColumnListHandlerGeneric.java
New file
0,0 → 1,116
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.sql.model;
 
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
 
import org.apache.commons.dbutils.ResultSetHandler;
 
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.Immutable;
 
/**
* <code>ResultSetHandler</code> implementation that converts one <code>ResultSet</code> column into
* a {@link List}.
*/
@Immutable
public class ColumnListHandlerGeneric<T> implements ResultSetHandler {
 
@GuardedBy("cache")
static private final Map<Class<?>, ColumnListHandlerGeneric<?>> cache = new LinkedHashMap<Class<?>, ColumnListHandlerGeneric<?>>(16, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<Class<?>, ColumnListHandlerGeneric<?>> eldest) {
return this.size() > 20;
}
};
 
public static <T> ColumnListHandlerGeneric<T> create(Class<T> clz) {
synchronized (cache) {
@SuppressWarnings("unchecked")
ColumnListHandlerGeneric<T> res = (ColumnListHandlerGeneric<T>) cache.get(clz);
if (res == null) {
res = create(1, clz);
assert res != null;
cache.put(clz, res);
}
return res;
}
}
 
// syntactic sugar
public static <T> ColumnListHandlerGeneric<T> create(int columnIndex, Class<T> clz) {
return new ColumnListHandlerGeneric<T>(columnIndex, clz);
}
 
/**
* The column number to retrieve.
*/
private final int columnIndex;
 
/**
* The column name to retrieve. Either columnName or columnIndex will be used but never both.
*/
private final String columnName;
 
private final Class<T> clz;
 
public ColumnListHandlerGeneric(int columnIndex, Class<T> clz) {
this(columnIndex, null, clz);
}
 
/**
* Creates a new instance of ColumnListHandler.
*
* @param columnName The name of the column to retrieve from the <code>ResultSet</code>.
*/
public ColumnListHandlerGeneric(String columnName, Class<T> clz) {
this(-1, columnName, clz);
}
 
protected ColumnListHandlerGeneric(int columnIndex, String columnName, Class<T> clz) {
final boolean noName = columnName == null;
final boolean noIndex = columnIndex <= 0;
if (noName && noIndex)
throw new IllegalArgumentException("Missing column information");
assert noName || noIndex : "A constructor passed more than one argument";
this.columnIndex = columnIndex;
this.columnName = columnName;
this.clz = clz;
}
 
/**
* Returns one <code>ResultSet</code> column as a <code>List</code>. The elements are added to
* the <code>List</code> via {@link SQLResultSet#getValue(ResultSet, Class, int)}.
*
* @return a <code>List</code>, never <code>null</code>.
* @throws SQLException if one object couldn't be retrieved from the result set.
*/
@Override
public final List<T> handle(ResultSet rs) throws SQLException {
final List<T> result = new ArrayList<T>();
while (rs.next()) {
if (this.columnName == null) {
result.add(SQLResultSet.getValue(rs, this.clz, this.columnIndex));
} else {
result.add(SQLResultSet.getValue(rs, this.clz, this.columnName));
}
}
return result;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/DBStructureItem.java
112,7 → 112,7
}
 
public final boolean contains(String childName) {
return this.getChildrenNames().contains(childName);
return this.getChildrenMap().containsKey(childName);
}
 
/**
176,15 → 176,21
}
 
private final D getAncestor(int level) {
if (level < 0)
if (level < 0) {
throw new IllegalArgumentException("negative level: " + level);
else if (level == 0)
} else if (level == 0) {
return thisAsD();
else if (this.getParent() == null)
} else {
// need to upcast parent since D extends this class and private methods aren't inherited
// http://stackoverflow.com/questions/7719843/type-parameterized-field-of-a-generic-class-becomes-invisible-after-upgrading-to
// http://stackoverflow.com/questions/15062841/java-casting-with-method-calls
final DBStructureItem<D> parentForJavac = this.getParent();
if (parentForJavac == null)
throw new IllegalArgumentException(this + " is the root, can't go up of " + level);
else
return this.getParent().getAncestor(level - 1);
return parentForJavac.getAncestor(level - 1);
}
}
 
/**
* The youngest common ancestor.
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSearchMode.java
13,35 → 13,66
package org.openconcerto.sql.model;
 
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
 
public abstract class SQLSearchMode {
 
static public final SQLSearchMode EQUALS = new SQLSearchMode() {
@Override
public String generateSQL(final DBRoot r, final String term) {
return " = " + r.getBase().quoteString(term);
public String generateSQL(final SQLSyntax s, final String term) {
return " = " + s.quoteString(term);
}
};
 
static public final SQLSearchMode CONTAINS = new SQLSearchMode() {
@Override
public String generateSQL(final DBRoot r, final String term) {
return " like " + r.getBase().quoteString("%" + SQLSyntax.get(r).getLitteralLikePattern(term) + "%");
public List<SQLSearchMode> getHigherModes() {
return Collections.emptyList();
}
};
static public final SQLSearchMode STARTS_WITH = new SQLSearchMode() {
@Override
public String generateSQL(final DBRoot r, final String term) {
return " like " + r.getBase().quoteString(SQLSyntax.get(r).getLitteralLikePattern(term) + "%");
public String generateSQL(final SQLSyntax s, final String term) {
return " like " + s.quoteString(s.getLitteralLikePattern(term) + "%");
}
 
@Override
public List<SQLSearchMode> getHigherModes() {
return Collections.singletonList(EQUALS);
}
};
 
static public final SQLSearchMode ENDS_WITH = new SQLSearchMode() {
@Override
public String generateSQL(final DBRoot r, final String term) {
return " like " + r.getBase().quoteString("%" + SQLSyntax.get(r).getLitteralLikePattern(term));
public String generateSQL(final SQLSyntax s, final String term) {
return " like " + s.quoteString("%" + s.getLitteralLikePattern(term));
}
 
@Override
public List<SQLSearchMode> getHigherModes() {
return Collections.singletonList(EQUALS);
}
};
 
public abstract String generateSQL(final DBRoot r, final String term);
private static final List<SQLSearchMode> CONTAINS_HIGHER_MODES = Arrays.asList(EQUALS, STARTS_WITH);
static public final SQLSearchMode CONTAINS = new SQLSearchMode() {
 
@Override
public String generateSQL(final SQLSyntax s, final String term) {
return " like " + s.quoteString("%" + s.getLitteralLikePattern(term) + "%");
}
 
@Override
public List<SQLSearchMode> getHigherModes() {
return CONTAINS_HIGHER_MODES;
}
};
 
public final String generateSQL(final DBRoot r, final String term) {
return this.generateSQL(SQLSyntax.get(r), term);
}
 
public abstract String generateSQL(final SQLSyntax s, final String term);
 
// from highest to lowest
public abstract List<SQLSearchMode> getHigherModes();
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLBase.java
52,11 → 52,11
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import org.apache.commons.dbutils.ResultSetHandler;
 
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;
 
import org.apache.commons.dbutils.ResultSetHandler;
 
/**
* Une base de donnée SQL. Une base est unique, pour obtenir une instance il faut passer par
* SQLServer. Une base permet d'accéder aux tables qui la composent, ainsi qu'à son graphe.
67,7 → 67,7
* @see #getGraph()
*/
@ThreadSafe
public class SQLBase extends SQLIdentifier {
public final class SQLBase extends SQLIdentifier {
 
/**
* Boolean system property, if <code>true</code> then the structure and the graph of SQL base
154,7 → 154,6
protected synchronized void onDrop() {
// allow schemas (and their descendants) to be gc'd even we aren't
this.schemas.clear();
SQLType.remove(this);
super.onDrop();
}
 
782,6 → 781,7
static private final Pattern percent = Pattern.compile("%.");
 
private final static String quote(final SQLBase b, final String pattern, Object... params) {
final SQLSyntax s = b == null ? null : SQLSyntax.get(b);
final Matcher m = percent.matcher(pattern);
final StringBuffer sb = new StringBuffer();
int i = 0;
794,7 → 794,7
} else {
final Object param = params[i++];
if (modifier == 's') {
replacement = quoteString(b, param.toString());
replacement = SQLSyntax.quoteString(s, param.toString());
} else if (modifier == 'i') {
if (param instanceof SQLName)
replacement = ((SQLName) param).quote();
832,11 → 832,11
* @see #quoteStringStd(String)
*/
public String quoteString(String s) {
return quoteStringStd(s);
return SQLSyntax.get(this).quoteString(s);
}
 
static private final Pattern singleQuote = Pattern.compile("'", Pattern.LITERAL);
static private final Pattern quotedPatrn = Pattern.compile("^'(('')|[^'])*'$");
static public final Pattern quotedPatrn = Pattern.compile("'(('')|[^'])*'");
static private final Pattern twoSingleQuote = Pattern.compile("''", Pattern.LITERAL);
 
/**
864,15 → 864,11
* @see #quoteStringStd(String)
*/
public final static String unquoteStringStd(String s) {
if (!quotedPatrn.matcher(s).find())
if (!quotedPatrn.matcher(s).matches())
throw new IllegalArgumentException("Invalid quoted string " + s);
return twoSingleQuote.matcher(s.substring(1, s.length() - 1)).replaceAll("'");
}
 
public final static String quoteString(SQLBase b, String s) {
return b == null ? quoteStringStd(s) : b.quoteString(s);
}
 
// * quoteIdentifier
 
static private final Pattern doubleQuote = Pattern.compile("\"");
/trunk/OpenConcerto/src/org/openconcerto/sql/model/ListListHandlerGeneric.java
New file
0,0 → 1,75
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.sql.model;
 
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
 
import org.apache.commons.dbutils.ResultSetHandler;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
 
import net.jcip.annotations.Immutable;
 
/**
* <code>ResultSetHandler</code> implementation that converts a <code>ResultSet</code> into a
* {@link List} of lists.
*
* @see ArrayListHandler
*/
@Immutable
public class ListListHandlerGeneric<A> implements ResultSetHandler {
 
public static ListListHandlerGeneric<Object> create(final List<Class<?>> classes) {
return create(Object.class, classes);
}
 
public static <A> ListListHandlerGeneric<A> create(final Class<A> arrayClass, final int colCount) {
return create(arrayClass, Collections.<Class<? extends A>> nCopies(colCount, arrayClass));
}
 
// syntactic sugar, MAYBE cache instances
public static <A> ListListHandlerGeneric<A> create(final Class<A> arrayClass, final List<Class<? extends A>> classes) {
return new ListListHandlerGeneric<A>(arrayClass, classes);
}
 
private final Class<A> arrayClass;
private final List<Class<? extends A>> classes;
 
public ListListHandlerGeneric(final Class<A> arrayClass, final List<Class<? extends A>> classes) {
if (arrayClass == null)
throw new NullPointerException("Missing array component class");
this.arrayClass = arrayClass;
this.classes = classes == null ? null : new ArrayList<Class<? extends A>>(classes);
}
 
@Override
public final List<List<A>> handle(ResultSet rs) throws SQLException {
final int cols = this.classes == null ? rs.getMetaData().getColumnCount() : this.classes.size();
final List<List<A>> result = new ArrayList<List<A>>();
while (rs.next()) {
final List<A> array = new ArrayList<A>();
for (int i = 0; i < cols; i++) {
if (this.classes == null)
array.add(this.arrayClass.cast(rs.getObject(i + 1)));
else
array.add(SQLResultSet.getValue(rs, this.classes.get(i), i + 1));
}
result.add(array);
}
return result;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/Trigger.java
13,17 → 13,17
package org.openconcerto.sql.model;
 
import org.openconcerto.xml.JDOMUtils;
import org.openconcerto.xml.JDOM2Utils;
import org.openconcerto.xml.XMLCodecUtils;
 
import java.util.HashMap;
import java.util.Map;
 
import org.jdom2.Element;
 
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.Immutable;
 
import org.jdom2.Element;
 
@Immutable
public final class Trigger {
 
82,7 → 82,7
public synchronized String toXML() {
// this is immutable so only compute once the XML
if (this.xml == null)
this.xml = "<trigger name=\"" + JDOMUtils.OUTPUTTER.escapeAttributeEntities(getName()) + "\">" + XMLCodecUtils.encodeSimple(this.m) + "</trigger>";
this.xml = "<trigger name=\"" + JDOM2Utils.OUTPUTTER.escapeAttributeEntities(getName()) + "\">" + XMLCodecUtils.encodeSimple(this.m) + "</trigger>";
return this.xml;
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/model/JDBCStructureSource.java
113,7 → 113,7
 
// create metadata table here to avoid a second refresh
// null if we shouldn't alter the base
final SQLCreateMoveableTable createMetadata = SQLSchema.getCreateMetadata(getBase().getServer().getSQLSystem().getSyntax());
final SQLCreateMoveableTable createMetadata = SQLSchema.getCreateMetadata(getBase().getDBSystemRoot().getSyntax());
final boolean useCache = getBase().getDBSystemRoot().useCache();
Statement stmt = null;
try {
271,7 → 271,7
}
// try to find out more about those procedures
if (proceduresBySchema.size() > 0) {
final String sel = system.getSyntax().getFunctionQuery(getBase(), proceduresBySchema.keySet());
final String sel = getBase().getDBSystemRoot().getSyntax().getFunctionQuery(getBase(), proceduresBySchema.keySet());
if (sel != null) {
// don't cache since we don't listen on system tables
for (final Object o : (List) getBase().getDataSource().execute(sel, new IResultSetHandler(SQLDataSource.MAP_LIST_HANDLER, false))) {
321,7 → 321,7
@Override
protected Object getQuery(SQLBase b, TablesMap tables) {
try {
return b.getServer().getSQLSystem().getSyntax().getTriggerQuery(b, tables);
return b.getDBSystemRoot().getSyntax().getTriggerQuery(b, tables);
} catch (SQLException e) {
return e;
}
340,7 → 340,7
 
@Override
protected String getQuery(SQLBase b, TablesMap tables) {
return b.getServer().getSQLSystem().getSyntax().getColumnsQuery(b, tables);
return b.getDBSystemRoot().getSyntax().getColumnsQuery(b, tables);
}
 
@Override
357,7 → 357,7
@Override
protected Object getQuery(SQLBase b, TablesMap tables) {
try {
return b.getServer().getSQLSystem().getSyntax().getConstraints(b, tables);
return b.getDBSystemRoot().getSyntax().getConstraints(b, tables);
} catch (Exception e) {
return e;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLFilter.java
71,10 → 71,11
 
private final SQLElementDirectory dir;
private final GraFFF filterGraph;
// the filter, from descendant to ancestor
@GuardedBy("filteredIDs")
private final List<Set<SQLRow>> filteredIDs;
// CopyOnWriteArrayList is simpler but rmListener() cannot be implemented
@GuardedBy("listeners")
@GuardedBy("this")
private List<SQLFilterListener> listeners;
 
public SQLFilter(SQLElementDirectory dir, final GraFFF filterGraph) {
192,7 → 193,7
}
 
final List<SQLFilterListener> dispatchingListeners;
synchronized (this.listeners) {
synchronized (this) {
dispatchingListeners = this.listeners;
}
for (final SQLFilterListener l : dispatchingListeners) {
208,7 → 209,7
}
 
public void addListener(SQLFilterListener l) {
synchronized (this.listeners) {
synchronized (this) {
final List<SQLFilterListener> newListeners = new ArrayList<SQLFilterListener>(this.listeners.size() + 1);
newListeners.addAll(this.listeners);
newListeners.add(l);
232,7 → 233,7
}
 
private void rmListener_(SQLFilterListener lToRm) {
synchronized (this.listeners) {
synchronized (this) {
final int stop = this.listeners.size();
int indexToRm = -1;
for (int i = 0; i < stop && indexToRm < 0; i++) {
/trunk/OpenConcerto/src/org/openconcerto/sql/model/HierarchyLevel.java
78,16 → 78,16
throw new IllegalArgumentException(clazz + " hierarchy unknown");
}
 
static private Map<Class<? extends DBStructureItemJDBC>, HierarchyLevel> byClass;
static private final Map<Class<? extends DBStructureItemJDBC>, HierarchyLevel> byClass;
 
static private final Map<Class<? extends DBStructureItemJDBC>, HierarchyLevel> getByClass() {
// java : Cannot refer to the static enum field HierarchyLevel.byClass within an initializer
// thus not in ctor but on demand.
if (byClass == null) {
static {
byClass = new HashMap<Class<? extends DBStructureItemJDBC>, HierarchyLevel>();
for (HierarchyLevel l : HierarchyLevel.values())
for (HierarchyLevel l : HierarchyLevel.values()) {
byClass.put(l.getJDBCClass(), l);
}
}
 
static private final Map<Class<? extends DBStructureItemJDBC>, HierarchyLevel> getByClass() {
return byClass;
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowValues.java
17,7 → 17,6
import org.openconcerto.sql.model.SQLRowValuesCluster.ValueChangeListener;
import org.openconcerto.sql.model.SQLTable.FieldGroup;
import org.openconcerto.sql.model.SQLTableEvent.Mode;
import org.openconcerto.sql.model.graph.DatabaseGraph;
import org.openconcerto.sql.model.graph.Link;
import org.openconcerto.sql.model.graph.Link.Direction;
import org.openconcerto.sql.model.graph.Path;
34,7 → 33,6
import org.openconcerto.utils.CopyUtils;
import org.openconcerto.utils.ExceptionUtils;
import org.openconcerto.utils.ListMap;
import org.openconcerto.utils.NumberUtils;
import org.openconcerto.utils.RecursionType;
import org.openconcerto.utils.SetMap;
import org.openconcerto.utils.Tuple2;
44,6 → 42,8
import org.openconcerto.utils.cc.LinkedIdentitySet;
import org.openconcerto.utils.cc.TransformedMap;
import org.openconcerto.utils.convertor.NumberConvertor;
import org.openconcerto.utils.convertor.ValueConvertor;
import org.openconcerto.utils.convertor.ValueConvertorFactory;
 
import java.math.BigDecimal;
import java.sql.Connection;
56,7 → 56,6
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.EventListener;
import java.util.EventObject;
import java.util.HashMap;
70,6 → 69,8
import java.util.Map.Entry;
import java.util.Set;
 
import net.jcip.annotations.GuardedBy;
 
/**
* A class that represent a row of a table that can be modified before being inserted or updated.
* The row might not actually exists in the database, and it might not define all the fields. One
149,21 → 150,77
}
};
 
private static boolean checkValidity = true;
static public enum ValidityCheck {
/**
* The check is never performed.
*/
FORBIDDEN {
@Override
public boolean shouldCheck(final Boolean asked) {
return false;
}
},
/**
* The check is only performed if requested.
*/
FALSE_BY_DEFAULT {
@Override
public boolean shouldCheck(Boolean asked) {
return asked == null ? false : asked.booleanValue();
}
},
/**
* The check is performed unless specified.
*/
TRUE_BY_DEFAULT {
@Override
public boolean shouldCheck(Boolean asked) {
return asked == null ? true : asked.booleanValue();
}
},
/**
* The check is always performed. This is not generally recommended as some methods of the
* framework will fail.
*/
FORCED {
@Override
public boolean shouldCheck(Boolean asked) {
return true;
}
};
 
public static void setValidityChecked(final boolean b) {
checkValidity = b;
public abstract boolean shouldCheck(final Boolean asked);
}
 
@GuardedBy("this")
private static ValidityCheck checkValidity;
 
/**
* Whether or not {@link #getInvalid()} is called before each data modification.
* Set how {@link #getInvalid()} should be called before each data modification. Initially set
* to {@link ValidityCheck#TRUE_BY_DEFAULT}. NOTE : that the check also makes sure that
* referenced rows are not archived, so if it isn't performed a row could point to an archived
* row.
*
* @param vc the new mode, <code>null</code> to set the default.
*/
public synchronized static void setValidityChecked(final ValidityCheck vc) {
checkValidity = vc == null ? ValidityCheck.TRUE_BY_DEFAULT : vc;
}
 
/**
* Whether or not {@link #getInvalid()} should be called.
*
* @param asked what the caller requested.
* @return <code>true</code> if the validity is checked.
*/
public static boolean isValidityChecked() {
return checkValidity;
public synchronized static boolean isValidityChecked(final Boolean asked) {
return checkValidity.shouldCheck(asked);
}
 
static {
setValidityChecked(null);
}
 
private static final boolean DEFAULT_ALLOW_BACKTRACK = true;
 
// i.e. no re-hash for up to 6 entries (8*0.8=6.4)
688,45 → 745,6
}
}
 
/**
* Returns the foreign table of <i>fieldName</i>.
*
* @param fieldName the name of a foreign field, e.g. "ID_ARTICLE_2".
* @return the table the field points to (never <code>null</code>), e.g. |ARTICLE|.
* @throws IllegalArgumentException if <i>fieldName</i> is not a foreign field.
*/
private final SQLTable getForeignTable(String fieldName) throws IllegalArgumentException {
return this.getForeignLink(Collections.singletonList(fieldName)).getTarget();
}
 
private final Link getForeignLink(final List<String> fieldsNames) throws IllegalArgumentException {
final DatabaseGraph graph = this.getTable().getDBSystemRoot().getGraph();
final Link foreignLink = graph.getForeignLink(this.getTable(), fieldsNames);
if (foreignLink == null)
throw new IllegalArgumentException(fieldsNames + " are not a foreign key of " + this.getTable());
return foreignLink;
}
 
@Override
public boolean isForeignEmpty(String fieldName) {
// don't use getForeign() to avoid creating a SQLRow
// keep getForeignTable at the 1st line since it does the check
final SQLTable foreignTable = this.getForeignTable(fieldName);
final Object val = this.getContainedObject(fieldName);
if (val instanceof SQLRowValues) {
return ((SQLRowValues) val).isUndefined();
} else {
final Number undefID = foreignTable.getUndefinedIDNumber();
return NumberUtils.areNumericallyEqual((Number) val, undefID);
}
}
 
@Override<