OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Comparer les révisions

Ignorer les espaces blanc Révision 141 → Révision 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/icudata_56.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/icudata_56.jar
Nouveau fichier
Changements de propriété:
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: trunk/OpenConcerto/lib/mime_util.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: trunk/OpenConcerto/lib/mime_util.jar
===================================================================
--- trunk/OpenConcerto/lib/mime_util.jar (revision 0)
+++ trunk/OpenConcerto/lib/mime_util.jar (revision 142)
/trunk/OpenConcerto/lib/mime_util.jar
Changements de propriété:
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: trunk/OpenConcerto/lib/jOpenCalendar.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: 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
Index: trunk/OpenConcerto/lib/mysql-connector-java-5.1.40-bin.jar
===================================================================
--- trunk/OpenConcerto/lib/mysql-connector-java-5.1.40-bin.jar (revision 0)
+++ trunk/OpenConcerto/lib/mysql-connector-java-5.1.40-bin.jar (revision 142)
/trunk/OpenConcerto/lib/mysql-connector-java-5.1.40-bin.jar
Changements de propriété:
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: trunk/OpenConcerto/lib/accessors-smart-1.1.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: trunk/OpenConcerto/lib/accessors-smart-1.1.jar
===================================================================
--- trunk/OpenConcerto/lib/accessors-smart-1.1.jar (revision 0)
+++ trunk/OpenConcerto/lib/accessors-smart-1.1.jar (revision 142)
/trunk/OpenConcerto/lib/accessors-smart-1.1.jar
Changements de propriété:
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: trunk/OpenConcerto/lib/jOpenDocument-1.4rc2.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: trunk/OpenConcerto/lib/DS_Desktop_Notify.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: trunk/OpenConcerto/lib/DS_Desktop_Notify.jar
===================================================================
--- trunk/OpenConcerto/lib/DS_Desktop_Notify.jar (revision 0)
+++ trunk/OpenConcerto/lib/DS_Desktop_Notify.jar (revision 142)
/trunk/OpenConcerto/lib/DS_Desktop_Notify.jar
Changements de propriété:
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: trunk/OpenConcerto/lib/json-smart-2.2.1.jar
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: trunk/OpenConcerto/lib/json-smart-2.2.1.jar
===================================================================
--- trunk/OpenConcerto/lib/json-smart-2.2.1.jar (revision 0)
+++ trunk/OpenConcerto/lib/json-smart-2.2.1.jar (revision 142)
/trunk/OpenConcerto/lib/json-smart-2.2.1.jar
Changements de propriété:
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: trunk/OpenConcerto/src/interpreterDJava/JavaEditor.java
===================================================================
--- trunk/OpenConcerto/src/interpreterDJava/JavaEditor.java (revision 141)
+++ trunk/OpenConcerto/src/interpreterDJava/JavaEditor.java (revision 142)
@@ -12,6 +12,7 @@
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.Writer;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
@@ -24,15 +25,15 @@
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
+import org.jedit.JEditTextArea;
+import org.jedit.JavaTokenMarker;
+import org.jedit.Token;
+
import koala.dynamicjava.interpreter.Interpreter;
import koala.dynamicjava.interpreter.InterpreterException;
import koala.dynamicjava.interpreter.TreeInterpreter;
import koala.dynamicjava.parser.wrapper.JavaCCParserFactory;
-import org.jedit.JEditTextArea;
-import org.jedit.JavaTokenMarker;
-import org.jedit.Token;
-
// FIXME afficher les infos sur les variables via uen popup
public class JavaEditor extends JPanel implements Scrollable {
@@ -208,7 +209,7 @@
* @param varName
* @param value
*/
- protected void defineVariable(final Interpreter interpret, final BufferedWriter b, final String varName, final Object value) {
+ protected void defineVariable(final Interpreter interpret, final Writer b, final String varName, final Object value) {
if (value == null) {
try {
/trunk/OpenConcerto/src/product.properties
1,2 → 1,5
NAME=OpenConcerto
VERSION=1.4.2
VERSION=1.5
ORGANIZATION_NAME=OpenConcerto
ORGANIZATION_ID=org.openconcerto
/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/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/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,6 → 808,22
 
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) {
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/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
Nouveau fichier
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/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,11 → 493,19
* @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() {
/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/light/NotSpecifedConvertor.java
Fichier supprimé
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIFilesWithSelectionContext.java
Fichier supprimé
/trunk/OpenConcerto/src/org/openconcerto/ui/light/IUserControl.java
Fichier supprimé
/trunk/OpenConcerto/src/org/openconcerto/ui/light/IUserControlContainer.java
Fichier supprimé
/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");
}
return this.listId.get(realIndex);
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) {
58,20 → 66,24
final int listIdSize = this.listId.size();
for (int i = 0; i < listIdSize; i++) {
if (this.listId.get(i).equals(id)) {
// values start at 1.
// values start at 1.
return i + 1;
}
}
return null;
}
 
@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,15 → 50,19
}
 
@Override
public void setValueFromContext(final Object value) {
public void _setValueFromContext(final Object value) {
final JSONObject jsonContext = (JSONObject) JSONConverter.getObjectFromJSON(value, JSONObject.class);
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()));
} else {
System.out.println("LightUITabbed.setValueFromContext() - Context doesn't contains value for UUID: " + child.getUUID() + " ID: " + child.getId());
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()));
} else {
System.out.println("LightUITabbed.setValueFromContext() - Context doesn't contains value for UUID: " + child.getUUID() + " ID: " + child.getId());
}
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUserControl.java
Nouveau fichier
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/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) {
46,6 → 46,15
child.setParent(this);
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) {
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/LightUISlider.java
Nouveau fichier
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/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
Nouveau fichier
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
Nouveau fichier
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;
53,11 → 54,11
public void addUrlParameter(final String key, final String value) {
this.urlParameters.put(key, value);
}
 
public void removeUrlParameter(final String key) {
this.urlParameters.remove(key);
}
 
public void clearUrlParameter() {
this.urlParameters.clear();
}
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,24 → 182,29
}
 
@Override
public void setValueFromContext(final Object value) {
public void _setValueFromContext(final Object value) {
final JSONObject jsonContext = (JSONObject) JSONConverter.getObjectFromJSON(value, JSONObject.class);
final int childCount = this.getChildrenCount();
for (int i = 0; i < childCount; i++) {
final LightUILine line = this.getChild(i, LightUILine.class);
final int lineChildCount = line.getChildrenCount();
for (int j = 0; j < lineChildCount; j++) {
final LightUIElement lineChild = line.getChild(j);
if (lineChild instanceof IUserControlContainer) {
if (!jsonContext.containsKey(lineChild.getUUID())) {
throw new IllegalArgumentException("Impossible to find key " + lineChild.getUUID() + " in context, LightUIElement id: " + lineChild.getId());
 
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);
final int lineChildCount = line.getChildrenCount();
for (int j = 0; j < lineChildCount; j++) {
final LightUIElement lineChild = line.getChild(j);
if (lineChild instanceof LightUserControlContainer) {
if (!jsonContext.containsKey(lineChild.getUUID())) {
System.err.println("LightUIPanel.setValueFromContext() - Impossible to find key " + lineChild.getUUID() + " in context, LightUIElement id: " + lineChild.getId());
}
((LightUserControlContainer) lineChild).setValueFromContext(jsonContext.get(lineChild.getUUID()));
} else if (lineChild instanceof LightUserControl) {
if (!jsonContext.containsKey(lineChild.getUUID())) {
System.err.println("LightUIPanel.setValueFromContext() - Impossible to find key " + lineChild.getUUID() + " in context, LightUIElement id: " + lineChild.getId());
}
((LightUserControl) lineChild).setValueFromContext(jsonContext.get(lineChild.getUUID()));
}
((IUserControlContainer) lineChild).setValueFromContext(jsonContext.get(lineChild.getUUID()));
} else if (lineChild instanceof IUserControl) {
if (!jsonContext.containsKey(lineChild.getUUID())) {
throw new IllegalArgumentException("Impossible to find key " + lineChild.getUUID() + " in context, LightUIElement id: " + lineChild.getId());
}
((IUserControl) lineChild).setValueFromContext(jsonContext.get(lineChild.getUUID()));
}
}
}
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
40,31 → 41,31
public RowSelectionSpec(final JSONObject json) {
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() {
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
23,13 → 23,17
public IntValueConvertor(final boolean hasNotSpecified) {
super(hasNotSpecified);
}
 
@Override
public void fillComboWithValue(final LightUIComboBox combo) {
for(final Entry<Integer, String> entry : this.values.entrySet()) {
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/ComboValueConvertor.java
36,19 → 36,31
 
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);
}
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);
protected abstract void fillComboWithValue(final LightUIComboBox combo, final K selectedValue);
 
@Override
public String convert(final K key) {
/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((JSONObject) jsonValue));
this.values.add(new LightUIComboBoxElement(JSONConverter.getObjectFromJSON(jsonValue, JSONObject.class)));
}
}
}
 
@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/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;
 
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);
}
};
}
 
@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());
for (final LightUIListRow row : rows) {
this.addChild(row);
}
}
 
@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
Nouveau fichier
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
Nouveau fichier
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,10 → 29,10
 
public class Row implements Externalizable, JSONAble {
 
private long id;
private Number id;
private String extendId;
private List<Object> values;
 
private Boolean fillWidth = false;
private Boolean toggleable = false;
private Boolean visible = true;
45,79 → 45,75
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) {
for(int i = 0; i < valueCount; i++) {
for (int i = 0; i < valueCount; i++) {
this.values.add(null);
}
}
}
 
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();
141,32 → 137,32
 
result.put("class", "Row");
result.put("id", this.id);
if(this.extendId != null) {
if (this.extendId != null) {
result.put("extend-id", this.extendId);
}
if(!this.values.isEmpty()) {
if (!this.values.isEmpty()) {
result.put("values", JSONConverter.getJSON(this.values));
}
if(this.fillWidth) {
if (this.fillWidth) {
result.put("fill-width", true);
}
if(this.toggleable) {
if (this.toggleable) {
result.put("toggleable", true);
}
if(!this.visible) {
if (!this.visible) {
result.put("visible", false);
}
 
return result;
}
 
@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,27 → 172,17
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");
}
objValue = JSONToLightUIConvertorManager.getInstance().createUIElementFromJSON(jsonValue);
} else {
if(objValue instanceof String) {
if (objValue instanceof String) {
objValue = JSONConverter.getObjectFromJSON(objValue, String.class);
} else if(objValue instanceof Integer) {
} else if (objValue instanceof Integer) {
objValue = JSONConverter.getObjectFromJSON(objValue, Integer.class);
} else if(objValue instanceof Long) {
} else if (objValue instanceof Long) {
objValue = JSONConverter.getObjectFromJSON(objValue, Long.class);
} else if(objValue instanceof Boolean) {
} else if (objValue instanceof Boolean) {
objValue = JSONConverter.getObjectFromJSON(objValue, Boolean.class);
} else if(objValue != null) {
} else if (objValue != null) {
throw new IllegalArgumentException("unknow type: " + objValue.getClass().getName());
}
}
204,9 → 190,9
}
}
}
 
@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 {
35,11 → 36,29
public LightUIComboBoxElement(final int id) {
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;
}
 
public String getValue1() {
return this.value1;
}
/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();
}
final JSONObject jsonEditors = (JSONObject) JSONConverter.getParameterFromJSON(json, "editors", JSONObject.class);
if (numMaxWidth != null) {
this.maxWidth = numMaxWidth.doubleValue();
}
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/LightUIFileUpload.java
Nouveau fichier
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
Nouveau fichier
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/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/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/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;
}
}
return -1;
this.setTableSpec(tableSpec);
}
 
@Override
public void setId(final String id) {
super.setId(id);
this.tableSpec.setId(id);
this.tableSpec.getSelection().setTableId(id);
}
 
public String getElementCode() {
return this.elementCode;
}
if (this.tableSpec != null) {
this.tableSpec.setId(id);
 
public void setElementCode(final String elementCode) {
this.elementCode = elementCode;
if (this.tableSpec.getSelection() != null) {
this.tableSpec.getSelection().setTableId(id);
}
 
if (this.tableSpec.getContent() != null) {
this.tableSpec.getContent().setTableId(id);
}
}
}
 
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,90 → 280,179
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) {
final List<Object> rowValues = row.getValues();
for (final Object value : rowValues) {
if (value instanceof LightUIContainer) {
final LightUIContainer panel = (LightUIContainer) value;
final T element = panel.findChild(searchParam, byUUID, objectClass);
if (element != null) {
return element;
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) {
final LightUIContainer panel = (LightUIContainer) value;
final T element = panel.findChild(searchParam, byUUID, objectClass);
if (element != null) {
return element;
}
} else if (value instanceof LightUIElement) {
final LightUIElement element = (LightUIElement) value;
if (byUUID) {
if (element.getUUID().equals(searchParam)) {
if (objectClass.isAssignableFrom(element.getClass())) {
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());
}
} else if (value instanceof LightUIElement) {
final LightUIElement element = (LightUIElement) value;
if (byUUID) {
if (element.getUUID().equals(searchParam)) {
if (objectClass.isAssignableFrom(element.getClass())) {
return (T) element;
} else {
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 objectClass.cast(element);
} else {
if (element.getId().equals(searchParam)) {
if (objectClass.isAssignableFrom(element.getClass())) {
return (T) 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());
}
}
}
 
if (element instanceof LightUITable) {
final T resultElement = ((LightUITable) element).findElement(searchParam, byUUID, objectClass);
if (resultElement != null) {
return resultElement;
}
}
if (element instanceof LightUITable) {
final T resultElement = ((LightUITable) element).findElement(searchParam, byUUID, objectClass);
if (resultElement != null) {
return resultElement;
}
}
}
} else {
System.out.println("LightUITable.getElementById() - No rows for table: " + this.getId());
}
} else {
System.out.println("LightUITable.getElementById() - Null TableContent for table: " + this.getId());
}
} 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;
}
 
public void addSelectionListener(final ActionListener selectionListener) {
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() - No rows for table: " + this.getId());
}
 
return result;
}
 
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,45 → 489,49
}
}
 
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);
final JSONObject jsonLineContext = (JSONObject) JSONConverter.getObjectFromJSON(jsonContext.get(i), JSONObject.class);
final Long rowId = (Long) JSONConverter.getParameterFromJSON(jsonLineContext, "row.id", Long.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 (row.isFillWidth()) {
if (!row.getValues().isEmpty() && row.getValues().get(0) instanceof IUserControl) {
final LightUIElement element = (LightUIElement) row.getValues().get(0);
if (element instanceof IUserControl) {
if (jsonLineContext.containsKey(element.getUUID())) {
((IUserControl) element).setValueFromContext(jsonLineContext.get(element.getUUID()));
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 Number rowId = JSONConverter.getParameterFromJSON(jsonLineContext, "row.id", Number.class);
final String rowExtendId = (String) JSONConverter.getParameterFromJSON(jsonLineContext, "row.extend.id", String.class);
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 LightUserControl) {
final LightUIElement element = (LightUIElement) row.getValues().get(0);
if (element instanceof LightUserControl) {
if (jsonLineContext.containsKey(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());
}
}
}
} else {
for (int k = 0; k < editorsIndex.size(); k++) {
final Object objEditor = row.getValues().get(editorsIndex.get(k));
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 LightUserControl && jsonLineContext.containsKey(editor.getUUID())) {
((LightUserControl) editor)._setValueFromContext(jsonLineContext.get(editor.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());
throw new IllegalArgumentException(
"Impossible to find value for editor: " + editor.getId() + " for row: " + rowId.toString() + " at position: " + String.valueOf(k));
}
}
}
} else {
for (int k = 0; k < editorsIndex.size(); k++) {
final Object objEditor = row.getValues().get(editorsIndex.get(k));
if (!(objEditor instanceof IUserControl)) {
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()));
} else {
throw new IllegalArgumentException(
"Impossible to find value for editor: " + editor.getId() + " for row: " + rowId.toString() + " at position: " + String.valueOf(k));
}
}
throw new IllegalArgumentException("Impossible to find row: " + rowId.toString());
}
} else {
throw new IllegalArgumentException("Impossible to find row: " + rowId.toString());
}
}
}
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/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,21 → 23,21
 
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;
}
 
public SearchSpec(final JSONObject json) {
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>();
}
this.rows = rows;
}
 
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/light/LightUIListRow.java
Nouveau fichier
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,29 → 275,18
 
@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");
}
this.columns.add(new ColumnSpec((JSONObject) objColumnSpec));
for (final JSONObject jsonColumn : jsonColumns) {
this.columns.add(new ColumnSpec(jsonColumn));
}
}
}
/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/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/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/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,12 → 485,16
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
ConnexionPanel.this.reloadPanel.setMode(ReloadPanel.MODE_BLINK);
JOptionPane.showMessageDialog(ConnexionPanel.this, TM.getTM().translate("loginPanel." + error, userName));
// Guillaume wants this for the Nego
if (Login.UNKNOWN_USER.equals(error))
ConnexionPanel.this.textLogin.setValue(ConnexionPanel.this.adminLogin);
setConnecting(false);
try {
ConnexionPanel.this.reloadPanel.setMode(ReloadPanel.MODE_BLINK);
JOptionPane.showMessageDialog(ConnexionPanel.this, TM.getTM().translate("loginPanel." + error, userName));
// Guillaume wants this for the Nego
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/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());
}
res.put(Info.DB_URL, conf.getSystemRoot().getDataSource().getUrl());
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);
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);
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,10 → 49,11
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);
this.list.addSearchItem(new TextSearchSpec(text, mode));
this.texts.add(text);
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);
}
}
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/LightAutoCompleteComboBox.java
Nouveau fichier
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());
}
 
for (final SQLRowValues vals : fetchedRows) {
LightUIComboBoxElement value = ElementComboBoxUtils.createLightUIItem(expanded, vals);
combo.setSelectedValue(value);
}
} else {
element.setValue(null);
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());
}
}
combo.setSelectedValue(value);
} 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
Nouveau fichier
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
Nouveau fichier
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);
}
}
 
for (int i = 0; i < size; i++) {
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");
}
}
this.autoCommit = autoCommit;
public final void removeTableModelListener(final TableModelListener tableModelListener) {
this.tableModelListeners.remove(tableModelListener);
this.model.removeTableModelListener(tableModelListener);
}
 
/**
* Permet de charger les lignes et de lier les nouvelles lignes
*
*/
public void setParentSQLRow(final Configuration configuration, final SQLElement sqlElement, final SQLRowAccessor sqlRow) {
public ITableModel getModel() {
return this.model;
}
 
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);
 
return row;
}
 
public SQLRowAccessor getRefRow() {
return this.refRow;
public final SQLRowAccessor getFirstSelectedSqlRow() {
final List<Row> selectedRows = this.getSelectedRows();
if (selectedRows.isEmpty()) {
return null;
} else {
return ((LightListSqlRow) selectedRows.get(0)).getSqlRow();
}
}
 
public void clearRowValues(){
this.getTableSpec().getContent().getRows().clear();
this.listRowValues.clear();
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();
}
});
} catch (final Exception ex) {
throw new IllegalStateException(ex);
}
}
public void removeRowValuesAt(int index) {
final TableContent content = this.getTableSpec().getContent();
content.getRows().remove(index);
this.listRowValues.remove(index);
 
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 addRowValues(final Configuration configuration, final SQLRowValues rowValues) {
final TableContent content = this.getTableSpec().getContent();
this.listRowValues.add(rowValues);
/**
* 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;
 
content.getRows().add(this.createRowFromRowValues(configuration, rowValues, this.listRowValues.size() - 1));
w3cDoc = configuration.getXMLConf(userId, this.getId());
if (w3cDoc != null) {
columnsPrefs = in.build(w3cDoc);
}
return columnsPrefs;
}
 
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));
}
/**
* 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>();
 
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);
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;
}
}
}
 
public void addDeletedId(final int idToDelete) {
this.deletedIds.add(idToDelete);
}
 
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());
columnsSpec.add(new ColumnSpec(columnId, valueClass, sqlColumn.getName(), null, false, null));
}
 
final SQLTableModelSourceOnline tableSource = element.getTableSource(true);
final List<SQLTableModelColumn> allCols = tableSource.getColumns();
// TODO : recuperer l'info sauvegardée sur le serveur par user (à coder)
sortedIds.add(columnsSpec.get(0).getId());
 
final Row row = element.createRowFromSQLRow(sqlRow, allCols, this.getTableSpec().getColumns());
row.setId(index);
return row;
}
final ColumnsSpec cSpec = new ColumnsSpec(this.getId(), columnsSpec, possibleColumnIds, sortedIds);
cSpec.setAllowMove(true);
cSpec.setAllowResize(true);
 
private void init() {
if (this.getTableSpec().getContent() == null) {
this.getTableSpec().setContent(new TableContent(this.getId()));
final Document xmlColumnsPref = this.getColumnsSpecUserPerfs(configuration, userId);
if (!cSpec.setUserPrefs(xmlColumnsPref)) {
configuration.removeXMLConf(userId, this.getId());
}
 
return cSpec;
}
 
// TODO: merge with OpenConcerto List search system
public abstract void doSearch(final Configuration configuration, final SearchSpec searchSpec, final int offset);
 
@Override
public LightUIElement clone() {
return new LightRowValuesTable(this);
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);
}
}
return super.getRowById(rowId);
}
 
/**
* Create default columns preferences from the SQLTableModelLinesSourceOnline
*
* @return XML document with columns preferences
*/
@Override
public JSONObject toJSON() {
final JSONObject json = super.toJSON();
public Document createDefaultXmlPreferences() {
final Element rootElement = new Element("list");
final List<SQLTableModelColumn> columns = this.getModelColumns();
 
// TODO: implement row values JSONAble
if (this.listRowValues != null && !this.listRowValues.isEmpty()) {
json.put("list-row-values", null);
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);
}
if (this.deletedIds != null && !this.deletedIds.isEmpty()) {
json.put("deleted-ids", JSONConverter.getJSON(this.deletedIds));
}
final Document xmlConf = new Document(rootElement);
 
return json;
return xmlConf;
}
 
@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 void destroy() {
super.destroy();
 
if (this.getTableSpec().getContent() != null) {
this.getTableSpec().getContent().getRows().clear();
// 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);
}
 
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));
}
}
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,26 → 166,8
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");
}
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());
if (!uiElement.isNotSaved()) {
this.putValueFromUserControl(configuration, sqlElement, field, uiElement, customEditors);
}
} else {
Log.get().warning("No field attached to " + item.getId());
191,57 → 176,77
}
}
 
final protected void putValueFromUserControl(final Configuration conf, final SQLField field, final LightUIElement uiElement) {
final Class<?> fieldType = field.getType().getJavaType();
if (field.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");
}
final LightUIComboBox combo = (LightUIComboBox) uiElement;
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: " + 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());
if (combo.hasSelectedValue()) {
this.sqlRow.put(fieldName, combo.getSelectedValue().getId());
} else {
this.sqlRow.put(fieldName, null);
}
} else {
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: " + fieldName + ". When field is Date, UI Element must be a LightUIDate");
}
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: " + fieldName + ". When field is Boolean, UI Element must be a LightUICheckBox");
}
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: " + fieldName + ". When field is Date, UI Element must be a LightUIDate");
}
this.sqlRow.put(fieldName, ((LightUIDate) uiElement).getValueAsDate());
} else if (fieldType.equals(Integer.class)) {
if (value != null && !value.trim().isEmpty()) {
if (!value.matches("^-?\\d+$")) {
throw new IllegalArgumentException("Invalid value for field: " + fieldName + " value: " + value);
}
this.sqlRow.put(fieldName, Integer.parseInt(value));
} else {
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);
}
 
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());
} else {
this.sqlRow.put(fieldName, null);
}
} else {
Log.get().warning("unsupported type " + fieldName);
}
}
}
 
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());
} 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");
}
this.sqlRow.put(field.getFieldName(), ((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");
}
this.sqlRow.put(field.getFieldName(), ((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");
}
this.sqlRow.put(field.getFieldName(), ((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());
}
this.sqlRow.put(field.getFieldName(), Integer.parseInt(uiElement.getValue()));
} else {
this.sqlRow.put(field.getFieldName(), null);
}
} else {
Log.get().warning("unsupported type " + fieldType.getName());
}
}
 
253,37 → 258,33
* @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());
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);
}
} catch (SQLException e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
}
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);
}
rowValsTable.archiveDeletedRows(configuration);
final Future<?> fCommit = foreignTable.commitRows();
 
try {
fCommit.get();
} catch (final Exception ex) {
throw new IllegalArgumentException(ex);
}
}
}
}
291,7 → 292,6
 
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
Nouveau fichier
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
Nouveau fichier
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
Nouveau fichier
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/sqlobject/ElementComboBoxUtils.java
Fichier supprimé
/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/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,19 → 224,25
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();
}
}
}
 
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,15 → 516,19
*/
@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;
}
 
/**
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,66 → 611,75
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 :
// 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
// 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
final ComboSQLRequest comboSQLRequest = this.req.clone();
comboSQLRequest.setFilterEnabled(false);
comboSQLRequest.setWhere(null);
final ITransformer<SQLSelect, SQLSelect> transf = comboSQLRequest.getSelectTransf();
if (transf != null)
comboSQLRequest.setSelectTransf(new ITransformer<SQLSelect, SQLSelect>() {
@Override
public SQLSelect transformChecked(SQLSelect input) {
final SQLSelect res = transf.transformChecked(input);
res.setWhere(null);
return res;
}
});
IComboSelectionItem newItem = comboSQLRequest.getComboItem(id);
if (newItem != null) {
newItem.setFlag(IComboSelectionItem.WARNING_FLAG);
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
// 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 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);
final ITransformer<SQLSelect, SQLSelect> transf = comboSQLRequest.getSelectTransf();
if (transf != null)
comboSQLRequest.setSelectTransf(new ITransformer<SQLSelect, SQLSelect>() {
@Override
public SQLSelect transformChecked(SQLSelect input) {
final SQLSelect res = transf.transformChecked(input);
res.setWhere(null);
return res;
}
});
IComboSelectionItem newItem = comboSQLRequest.getComboItem(id);
if (newItem != null) {
newItem.setFlag(IComboSelectionItem.WARNING_FLAG);
} else {
// TODO y faire un cran plus haut pour savoir quelle table référence
// cette erreur
new IllegalStateException("ID " + id + " cannot be found in " + this.req).printStackTrace();
final SQLRow row = new SQLRow(this.req.getPrimaryTable(), id);
final String error;
if (!row.exists())
error = " inexistante";
else if (row.isArchived())
error = " archivée";
else
error = " existe mais est non atteignable: " + row.findDistantArchived(2);
newItem = new IComboSelectionItem(row, "ERREUR !!! " + row + error);
newItem.setFlag(IComboSelectionItem.ERROR_FLAG);
}
this.addItem(newItem);
this.setSelectedItem(newItem);
} else {
// TODO y faire un cran plus haut pour savoir quelle table référence
// cette erreur
new IllegalStateException("ID " + id + " cannot be found in " + this.req).printStackTrace();
final SQLRow row = new SQLRow(this.req.getPrimaryTable(), id);
final String error;
if (!row.exists())
error = " inexistante";
else if (row.isArchived())
error = " archivée";
else
error = " existe mais est non atteignable: " + row.findDistantArchived(2);
newItem = new IComboSelectionItem(row, "ERREUR !!! " + row + error);
newItem.setFlag(IComboSelectionItem.ERROR_FLAG);
this.setSelectedItem(item);
}
this.addItem(newItem);
this.setSelectedItem(newItem);
} else {
this.setSelectedItem(item);
}
}
}
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) {
synchronized (this) {
this.runnables.add(r);
// 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) {
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/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/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/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);
}
}
return newFile;
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");
}
 
// 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();
return docBuilder.parse(new InputSource(new StringReader(rqResult.get(0).getString("VALUE"))));
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/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);
public ListSQLRequest(SQLTable table, List<String> fieldss, Where where, final FieldExpander showAs) {
this(computeGraph(table, fieldss, getExpander(showAs)), where);
}
 
public ListSQLRequest(final SQLRowValues graph, final Where where) {
super(graph, 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);
}
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();
}
}
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/request/ComboSQLRequestUtils.java
Nouveau fichier
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;
}
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,15 → 249,7
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.graphToFetch = tmp.toImmutable();
this.setGraphToFetch(tmp, true);
}
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,58 → 418,43
* @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;
}
}
}
if (res)
this.fireWhereChange();
return res;
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;
}
 
public final synchronized void setLockSelect(boolean lockSelect) {
checkFrozen();
this.lockSelect = lockSelect;
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/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,46 → 281,113
}
 
/**
* 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);
}
values.add(value);
} else {
throw new IllegalArgumentException("column " + columnId + " is in ColumnsSpec but it is not found in SQLTableModelColumn");
}
/**
* 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;
}
row.setValues(values);
return row;
 
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;
}
 
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 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);
}
 
/**
* 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);
}
 
/**
*
* @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 null;
return convertor;
}
 
/**
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 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
*
* @param col SQLTableModelColumn that you want change the value
* @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 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
*
* @param row The row which was just inserted
* @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() {
416,8 → 576,8
l.getOwned().resetRelationshipsOf(this);
}
}
this.ownedLinks = this instanceof JoinSQLElement ? new SQLElementLinks(SetMap.<LinkType, SQLElementLink>empty()) : null;
this.otherLinks = this instanceof JoinSQLElement ? new SQLElementLinks(SetMap.<LinkType, SQLElementLink>empty()) : null;
this.ownedLinks = this instanceof JoinSQLElement ? new SQLElementLinks(SetMap.<LinkType, SQLElementLink> empty()) : null;
this.otherLinks = this instanceof JoinSQLElement ? new SQLElementLinks(SetMap.<LinkType, SQLElementLink> empty()) : null;
this.parentFF = null;
}
 
758,7 → 918,7
}
 
public final UpdateScript update(final SQLRowValues from, final SQLRowValues to, final boolean allowedToChangeTo) {
return this.update(from, to, allowedToChangeTo, Transformer.<SQLRowValues>nopTransformer());
return this.update(from, to, allowedToChangeTo, Transformer.<SQLRowValues> nopTransformer());
}
 
private final UpdateScript update(final SQLRowValues from, SQLRowValues to, boolean allowedToChangeTo, ITransformer<SQLRowValues, SQLRowValues> copy2originalRows) {
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,35 → 1837,46
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();
res.getColumns().addAll(this.additionalListCols);
return initTableSource(res);
public final SQLTableModelSourceOnline createTableSourceOnline(final ListSQLRequest req) {
return initTableSource(instantiateTableSourceOnline(req));
}
 
protected synchronized void _initTableSource(final SQLTableModelSourceOnline res) {
protected SQLTableModelSourceOnline instantiateTableSourceOnline(final ListSQLRequest req) {
return new SQLTableModelSourceOnline(req, this);
}
 
public final synchronized SQLTableModelSourceOnline initTableSource(final SQLTableModelSourceOnline res) {
protected synchronized void _initTableSource(final SQLTableModelSource res) {
if (!this.additionalListCols.isEmpty())
res.getColumns().addAll(this.additionalListCols);
}
 
public final <S extends SQLTableModelSource> S initTableSource(final S res) {
return this.initTableSource(res, false);
}
 
public final synchronized <S extends SQLTableModelSource> S initTableSource(final S res, final boolean minimal) {
// do init first since it can modify the columns
this._initTableSource(res);
if (!minimal)
this._initTableSource(res);
// setEditable(false) on read only fields
// MAYBE setReadOnlyFields() on SQLTableModelSource, so that SQLTableModelLinesSource can
// check in commit()
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/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/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
Fichier supprimé
/trunk/OpenConcerto/src/org/openconcerto/sql/model/PGSQLBase.java
Fichier supprimé
/trunk/OpenConcerto/src/org/openconcerto/sql/model/MSSQLBase.java
Fichier supprimé
/trunk/OpenConcerto/src/org/openconcerto/sql/model/DBRoot.java
150,7 → 150,7
@Override
public Object handle(SQLDataSource ds) throws SQLException {
// don't create foreign constraints now, so we can insert undefined with cycles
final List<List<String>> createTablesSQL = ChangeTable.cat(undefinedNonDefaultValues.keySet(), getName(), EnumSet.of(ConcatStep.ADD_FOREIGN));
final List<List<String>> createTablesSQL = ChangeTable.cat(undefinedNonDefaultValues.keySet(), getName(), EnumSet.of(ConcatStep.ADD_CONSTRAINT));
for (final String sql : createTablesSQL.get(0))
ds.execute(sql);
final Map<SQLCreateTableBase<?>, Number> newUndefIDs;
312,16 → 312,20
this.getBase().fetchTables(TablesMap.createFromTables(this.getSchema().getName(), tableNames));
}
 
public final SQLCreateRoot getDefinitionSQL(final SQLSystem sys) {
return this.getDefinitionSQL(sys, true);
public final SQLCreateRoot getDefinitionSQL() {
return this.getDefinitionSQL(getDBSystemRoot().getSyntax());
}
 
public final SQLCreateRoot getDefinitionSQL(final SQLSystem sys, final boolean withTables) {
final SQLCreateRoot res = new SQLCreateRoot(sys.getSyntax(), this.getName());
public final SQLCreateRoot getDefinitionSQL(final SQLSyntax s) {
return this.getDefinitionSQL(s, true);
}
 
public final SQLCreateRoot getDefinitionSQL(final SQLSyntax s, final boolean withTables) {
final SQLCreateRoot res = new SQLCreateRoot(s, this.getName());
if (withTables) {
// order by name to be able to do diffs
for (final SQLTable table : new TreeMap<String, SQLTable>(this.getTablesMap()).values()) {
res.addTable(table.getCreateTable(sys));
res.addTable(table.getCreateTable(s));
}
}
return res;
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRequestLogModel.java
30,14 → 30,18
 
@Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex < 5)
if (columnIndex == 9) {
return Integer.class;
}
if (columnIndex < 5) {
return Long.class;
}
return String.class;
}
 
@Override
public int getColumnCount() {
return 9;
return 10;
}
 
@Override
61,6 → 65,8
return "Connection";
case 8:
return "Thread";
case 9:
return "Returned rows";
}
return "??";
}
96,6 → 102,8
if (l.isInSwing())
return "Swing";
return l.getThreadId();
case 9:
return l.getResultCount();
}
return "";
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/DBSystemRoot.java
76,6 → 76,9
private boolean incoherentPath;
private final PropertyChangeListener coherenceListener;
 
@GuardedBy("this")
private SQLSyntax syntax;
 
private final LoadingChangeSupport loadingListenersSupp;
 
DBSystemRoot(DBStructureItemJDBC delegate) {
97,6 → 100,7
}
};
this.loadingListenersSupp = new LoadingChangeSupport(this);
this.syntax = null;
}
 
private synchronized void rootsChanged(PropertyChangeEvent evt) {
271,7 → 275,7
public synchronized final void setUseCache(final boolean useCache) throws SQLException {
if (this.hasDataSource() && useCache) {
// null if we shouldn't alter the base
final SQLCreateMoveableTable createMetadata = SQLSchema.getCreateMetadata(this.getServer().getSQLSystem().getSyntax());
final SQLCreateMoveableTable createMetadata = SQLSchema.getCreateMetadata(this.getSyntax());
final TablesMap m = new TablesMap();
for (final DBRoot r : this.getChildrenMap().values()) {
// works because when created a root is always fully loaded (we don't allow roots
390,6 → 394,12
return this.ds != null;
}
 
public synchronized final SQLSyntax getSyntax() {
if (this.syntax == null)
this.syntax = SQLSyntax.create(this);
return this.syntax;
}
 
@Override
protected synchronized void onDrop() {
this.rmChildrenListener(this.coherenceListener);
536,7 → 546,7
* @see #addRoot(String)
*/
public final DBRoot createRoot(final String rootName) throws SQLException {
for (final String s : new SQLCreateRoot(SQLSyntax.get(this), rootName).asList(rootName, false, true))
for (final String s : new SQLCreateRoot(this.getSyntax(), rootName).asList(rootName, false, true))
getDataSource().execute(s);
return this.addRoot(rootName);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSelect.java
208,6 → 208,10
return getSystemRoot().getServer().getSQLSystem();
}
 
public final SQLSyntax getSyntax() {
return getSystemRoot().getSyntax();
}
 
public String asString() {
final SQLSystem sys = this.getSQLSystem();
 
231,7 → 235,7
archive = Where.and(getUndefWhere(fromTable, alias), archive);
}
// archive == null si pas d'archive et pas d'undefined
if (archive != null && archive.getClause() != "") {
if (archive != null) {
result.append("\n WHERE ");
result.append(archive.getClause());
}
430,19 → 434,22
}
 
/**
* Add an ORDER BY {@link SQLTable#getOrderField() t.ORDER}.
* Add an ORDER BY for the passed table.
*
* @param t the table.
* @param fieldMustExist if <code>true</code> then <code>t</code> must be
* {@link SQLTable#isOrdered() ordered}.
* {@link SQLTable#isOrdered() ordered} or have a {@link SQLTable#isRowable() numeric
* primary key}.
* @return this.
* @throws IllegalArgumentException if <code>t</code> isn't ordered and <code>mustExist</code>
* is <code>true</code>.
* @throws IllegalArgumentException if <code>t</code> has no usable order field and
* <code>mustExist</code> is <code>true</code>.
*/
public SQLSelect addOrder(final TableRef t, final boolean fieldMustExist) {
final SQLField orderField = t.getTable().getOrderField();
if (orderField != null)
this.addFieldOrder(t.getField(orderField.getName()));
else if (t.getTable().isRowable())
this.addFieldOrder(t.getKey());
else if (fieldMustExist)
throw new IllegalArgumentException("table is not ordered : " + t);
return this;
977,6 → 984,14
return current;
}
 
public final FieldRef followFieldPath(final IFieldPath fp) {
return this.followFieldPath(fp.getPath().getFirst().getAlias(), fp);
}
 
public final FieldRef followFieldPath(final String tableAlias, final IFieldPath fp) {
return this.followPath(tableAlias, fp.getPath()).getField(fp.getFieldName());
}
 
public boolean isExcludeUndefined() {
return this.generalExcludeUndefined;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSyntaxH2.java
28,6 → 28,7
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
36,8 → 37,15
 
class SQLSyntaxH2 extends SQLSyntax {
 
static protected final IdentityHashMap<String, String> DATE_SPECS;
 
static {
DATE_SPECS = new IdentityHashMap<String, String>(DateProp.JAVA_DATE_SPECS_PURE);
DATE_SPECS.put(DateProp.MICROSECOND, "SSS000");
}
 
SQLSyntaxH2() {
super(SQLSystem.H2);
super(SQLSystem.H2, DATE_SPECS);
this.typeNames.addAll(Boolean.class, "boolean", "bool", "bit");
this.typeNames.addAll(Integer.class, "integer", "int", "int4", "mediumint");
this.typeNames.addAll(Byte.class, "tinyint");
47,7 → 55,8
this.typeNames.addAll(Float.class, "real");
this.typeNames.addAll(Double.class, "double precision", "float", "float4", "float8");
this.typeNames.addAll(Timestamp.class, "timestamp", "smalldatetime", "datetime");
this.typeNames.addAll(java.util.Date.class, "date");
this.typeNames.addAll(java.sql.Date.class, "date");
this.typeNames.addAll(java.sql.Time.class, "time");
this.typeNames.addAll(Blob.class, "blob", "tinyblob", "mediumblob", "longblob", "image",
// byte[]
"bytea", "raw", "varbinary", "longvarbinary", "binary");
167,7 → 176,7
@Override
public void _loadData(final File f, final SQLTable t) {
checkServerLocalhost(t);
final String quotedPath = t.getBase().quoteString(f.getAbsolutePath());
final String quotedPath = quoteString(f.getAbsolutePath());
t.getDBSystemRoot().getDataSource().execute("insert into " + t.getSQLName().quote() + " select * from CSVREAD(" + quotedPath + ", NULL, 'UTF8', ',', '\"', '\\', '\\N') ;");
}
 
174,8 → 183,8
@Override
protected void _storeData(final SQLTable t, final File f) {
checkServerLocalhost(t);
final String quotedPath = t.getBase().quoteString(f.getAbsolutePath());
final String quotedSel = t.getBase().quoteString(SQLSyntaxPG.selectAll(t).asString());
final String quotedPath = quoteString(f.getAbsolutePath());
final String quotedSel = quoteString(SQLSyntaxPG.selectAll(t).asString());
t.getBase().getDataSource().execute("CALL CSVWRITE(" + quotedPath + ", " + quotedSel + ", 'UTF8', ',', '\"', '\\', '\\N', '\n');");
}
 
204,10 → 213,25
}
 
@Override
public String getDayOfWeek(String sqlTS) {
return "DAY_OF_WEEK(" + sqlTS + ")";
}
 
@Override
public String getFormatTimestamp(String sqlTS, boolean basic) {
return "FORMATDATETIME(" + sqlTS + ", " + SQLBase.quoteStringStd(basic ? TS_BASIC_JAVA_FORMAT : TS_EXTENDED_JAVA_FORMAT) + ")";
return this.getFormatTimestamp(sqlTS, SQLBase.quoteStringStd(basic ? TS_BASIC_JAVA_FORMAT : TS_EXTENDED_JAVA_FORMAT));
}
 
@Override
public final String getFormatTimestamp(String sqlTS, String format) {
return "FORMATDATETIME(" + sqlTS + ", " + format + ")";
}
 
@Override
public String quoteForTimestampFormat(String text) {
return SQLBase.quoteStringStd(text);
}
 
// (SELECT "C1" as "num", "C2" as "name" FROM VALUES(1, 'Hello'), (2, 'World')) AS V;
@Override
public String getConstantTable(List<List<String>> rows, String alias, List<String> columnsAlias) {
235,28 → 259,28
// src can be null since H2 supports alias to Java static functions
// perhaps join on FUNCTION_COLUMNS to find out parameters' types
final String src = "coalesce(\"SOURCE\", \"JAVA_CLASS\" || '.' || \"JAVA_METHOD\" ||' parameter(s): ' || \"COLUMN_COUNT\")";
return "SELECT ALIAS_SCHEMA as \"schema\", ALIAS_NAME as \"name\", " + src + " as \"src\" FROM \"INFORMATION_SCHEMA\".FUNCTION_ALIASES where ALIAS_CATALOG=" + b.quoteString(b.getMDName())
+ " and ALIAS_SCHEMA in (" + quoteStrings(b, schemas) + ")";
return "SELECT ALIAS_SCHEMA as \"schema\", ALIAS_NAME as \"name\", " + src + " as \"src\" FROM \"INFORMATION_SCHEMA\".FUNCTION_ALIASES where ALIAS_CATALOG=" + this.quoteString(b.getMDName())
+ " and ALIAS_SCHEMA in (" + quoteStrings(schemas) + ")";
}
 
@Override
public String getTriggerQuery(SQLBase b, TablesMap tables) {
return "SELECT \"TRIGGER_NAME\", \"TABLE_SCHEMA\", \"TABLE_NAME\", \"JAVA_CLASS\" as \"ACTION\", \"SQL\" from INFORMATION_SCHEMA.TRIGGERS " + getTablesMapJoin(b, tables) + " where "
return "SELECT \"TRIGGER_NAME\", \"TABLE_SCHEMA\", \"TABLE_NAME\", \"JAVA_CLASS\" as \"ACTION\", \"SQL\" from INFORMATION_SCHEMA.TRIGGERS " + getTablesMapJoin(tables) + " where "
+ getInfoSchemaWhere(b);
}
 
private String getTablesMapJoin(final SQLBase b, final TablesMap tables) {
return getTablesMapJoin(b, tables, SQLBase.quoteIdentifier("TABLE_SCHEMA"), SQLBase.quoteIdentifier("TABLE_NAME"));
private String getTablesMapJoin(final TablesMap tables) {
return getTablesMapJoin(tables, SQLBase.quoteIdentifier("TABLE_SCHEMA"), SQLBase.quoteIdentifier("TABLE_NAME"));
}
 
private final String getInfoSchemaWhere(SQLBase b) {
return "\"TABLE_CATALOG\" = " + b.quoteString(b.getMDName());
return "\"TABLE_CATALOG\" = " + this.quoteString(b.getMDName());
}
 
@Override
public String getColumnsQuery(SQLBase b, TablesMap tables) {
return "SELECT \"" + INFO_SCHEMA_NAMES_KEYS.get(0) + "\", \"" + INFO_SCHEMA_NAMES_KEYS.get(1) + "\", \"" + INFO_SCHEMA_NAMES_KEYS.get(2)
+ "\" , \"CHARACTER_SET_NAME\", \"COLLATION_NAME\", \"SEQUENCE_NAME\" from INFORMATION_SCHEMA.\"COLUMNS\" " + getTablesMapJoin(b, tables) + " where " + getInfoSchemaWhere(b);
+ "\" , \"CHARACTER_SET_NAME\", \"COLLATION_NAME\", \"SEQUENCE_NAME\" from INFORMATION_SCHEMA.\"COLUMNS\" " + getTablesMapJoin(tables) + " where " + getInfoSchemaWhere(b);
}
 
@Override
266,7 → 290,7
//
+ "case \"CONSTRAINT_TYPE\" when 'REFERENTIAL' then 'FOREIGN KEY' else \"CONSTRAINT_TYPE\" end as \"CONSTRAINT_TYPE\", \"COLUMN_LIST\", \"CHECK_EXPRESSION\" AS \"DEFINITION\"\n"
//
+ "FROM INFORMATION_SCHEMA.CONSTRAINTS " + getTablesMapJoin(b, tables)
+ "FROM INFORMATION_SCHEMA.CONSTRAINTS " + getTablesMapJoin(tables)
// where
+ " where " + getInfoSchemaWhere(b);
// don't cache since we don't listen on system tables
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLType.java
18,7 → 18,7
 
import org.openconcerto.utils.CompareUtils;
import org.openconcerto.utils.StringUtils;
import org.openconcerto.xml.JDOMUtils;
import org.openconcerto.xml.JDOM2Utils;
 
import java.math.BigDecimal;
import java.math.BigInteger;
30,16 → 30,14
import java.sql.Types;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
 
import org.jdom2.Element;
 
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;
 
import org.jdom2.Element;
 
/**
* The type of a SQL field. Allow one to convert a Java object to its SQL serialization.
*
50,7 → 48,7
@ThreadSafe
public abstract class SQLType {
 
private static Class<?> getClass(int type, final int size) {
private static Class<?> getClass(int type, final int size, SQLSyntax s) {
switch (type) {
case Types.BIT:
// As of MySQL 5.0.3, BIT is for storing bit-field values
74,13 → 72,22
case Types.TIMESTAMP:
return Timestamp.class;
case Types.DATE:
return java.util.Date.class;
return java.sql.Date.class;
case Types.TIME:
return java.sql.Time.class;
case Types.INTEGER:
return Integer.class;
case Types.SMALLINT:
/*
* http://download.oracle.com/otndocs/jcp/jdbc-4_1-mrel-spec/index.html Appendix B : The
* JDBC 1.0 specification defined the Java object mapping for the SMALLINT and TINYINT
* JDBC types to be Integer. The Java language did not include the Byte and Short data
* types when the JDBC 1.0 specification was finalized. The mapping of SMALLINT and
* TINYINT to Integer is maintained to preserve backwards compatibility
*/
return s.getSystem() == SQLSystem.H2 ? Short.class : Integer.class;
case Types.TINYINT:
return Integer.class;
return s.getSystem() == SQLSystem.H2 ? Byte.class : Integer.class;
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
109,21 → 116,21
 
// useful when no SQLBase is known
public static SQLType getBoolean(final SQLSyntax s) {
// TODO use get() once it accepts a SQLSyntax
final SQLType res = new BooleanType(Types.BOOLEAN, 1, null, Boolean.class);
res.setSyntax(s);
return res;
return getFromSyntax(s, Types.BOOLEAN, 1);
}
 
public static SQLType get(final int type, final int size) {
return get(null, type, size, null, null);
public static SQLType getFromSyntax(final SQLSyntax s, final int type, final int size) {
return get(s, type, size, null, s.getTypeNames(getClass(type, size, s)).iterator().next());
}
 
public static SQLType get(final SQLBase base, final int type, final int size) {
return getFromSyntax(SQLSyntax.get(base), type, size);
}
 
/**
* Get the corresponding type.
*
* @param base the base of this type, can be <code>null</code> but {@link #toString(Object)}
* will have to use standard SQL which might not be valid for all bases (eg escapes).
* @param base the base of this type, cannot be <code>null</code>.
* @param type a value from java.sql.Types.
* @param size the size as COLUMN_SIZE is defined in
* {@link java.sql.DatabaseMetaData#getColumns(java.lang.String, java.lang.String, java.lang.String, java.lang.String)}
133,11 → 140,15
* @throws IllegalStateException if type is unknown.
*/
public static SQLType get(final SQLBase base, final int type, final int size, Integer decDigits, final String typeName) {
final List<String> typeID = Arrays.asList(base == null ? null : base.getURL(), type + "", size + "", String.valueOf(decDigits), typeName);
return get(SQLSyntax.get(base), type, size, decDigits, typeName);
}
 
public static SQLType get(final SQLSyntax s, final int type, final int size, Integer decDigits, final String typeName) {
final List<String> typeID = Arrays.asList(s.toString(), type + "", size + "", String.valueOf(decDigits), typeName);
synchronized (instances) {
SQLType res = instances.get(typeID);
if (res == null) {
final Class<?> clazz = getClass(type, size);
final Class<?> clazz = getClass(type, size, s);
if (Boolean.class.isAssignableFrom(clazz))
res = new BooleanType(type, size, typeName, clazz);
else if (Number.class.isAssignableFrom(clazz))
154,7 → 165,8
else
// BLOB & CLOB and the rest
res = new UnknownType(type, size, typeName, clazz);
res.setBase(base);
if (s != null)
res.setSyntax(s);
instances.put(typeID, res);
}
return res;
170,17 → 182,6
return get(base, type, size, decDigits, typeName);
}
 
static void remove(final SQLBase base) {
synchronized (instances) {
final Iterator<Entry<List<String>, SQLType>> iter = instances.entrySet().iterator();
while (iter.hasNext()) {
final Entry<List<String>, SQLType> e = iter.next();
if (e.getValue().getBase() == base)
iter.remove();
}
}
}
 
// *** instance
 
// a value from java.sql.Types
195,8 → 196,6
private final Class<?> javaType;
 
@GuardedBy("this")
private SQLBase base;
@GuardedBy("this")
private SQLSyntax syntax;
 
@GuardedBy("this")
232,6 → 231,11
return this.decimalDigits;
}
 
/**
* The type returned from the DB.
*
* @return the java class returned by {@link SQLResultSet#getObject(int)}.
*/
public Class<?> getJavaType() {
return this.javaType;
}
245,34 → 249,19
return this.typeName;
}
 
// TODO remove once quoteString() is in SQLSyntax
private synchronized final void setBase(SQLBase base) {
// set only once
assert this.base == null;
if (base != null) {
this.base = base;
this.setSyntax(this.base.getServer().getSQLSystem().getSyntax());
}
}
 
private synchronized final void setSyntax(SQLSyntax s) {
// set only once
assert this.syntax == null;
if (s != null) {
this.syntax = s;
}
if (this.syntax != null)
throw new IllegalStateException("Already set to " + this.syntax);
this.syntax = s;
}
 
private synchronized final SQLBase getBase() {
return this.base;
}
 
public synchronized final SQLSyntax getSyntax() {
return this.syntax;
}
 
protected final String quoteString(String s) {
return SQLBase.quoteString(this.getBase(), s);
return SQLSyntax.quoteString(this.getSyntax(), s);
}
 
@Override
350,7 → 339,7
sb.append(this.decimalDigits);
}
sb.append("\" typeName=\"");
sb.append(JDOMUtils.OUTPUTTER.escapeAttributeEntities(this.typeName));
sb.append(JDOM2Utils.OUTPUTTER.escapeAttributeEntities(this.typeName));
sb.append("\"/>");
this.xml = sb.toString();
}
406,10 → 395,11
}
 
/**
* Test whether o is valid for this type. Ie does o is an instance of getJavaType().
* Test whether the passed parameter is valid for this type.
*
* @param o the object to test.
* @return <code>true</code> if o can be passed to {@link #toString(Object)}.
* @return <code>true</code> if o can be passed to {@link #toString(Object)}, always
* <code>true</code> for an instance of {@link #getJavaType()}.
* @see #check(Object)
*/
public boolean isValid(Object o) {
503,7 → 493,7
@Override
public final String toStringRaw(Object o) {
// time has no special characters to escape
return "'" + toCSVRaw(o) + "'";
return getSyntax().cast("'" + toCSVRaw(o) + "'", this);
}
 
static protected long getTime(Object o) {
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowValuesListFetcher.java
28,6 → 28,7
import org.openconcerto.utils.RecursionType;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.utils.cc.Transformer;
 
import java.sql.ResultSet;
import java.sql.SQLException;
788,18 → 789,25
}
 
public final SQLSelect getReq() {
return this.getReq(null);
return this.getReq(null, null);
}
 
// see fetch() comment as to why the Where parameter isn't public.
private synchronized final SQLSelect getReq(final Where w) {
static private final SQLSelect checkTr(final List<String> origSelect, final SQLSelect tr) {
if (!origSelect.equals(tr.getSelect()))
throw new IllegalArgumentException("Select clause cannot be modified");
return tr;
}
 
public synchronized final SQLSelect getReq(final Where w, final ITransformer<SQLSelect, SQLSelect> selTransf) {
checkTable(w);
final boolean isNopTransf = selTransf == null || selTransf == Transformer.<SQLSelect> nopTransformer();
if (this.isFrozen()) {
if (w == null) {
if (w == null && isNopTransf) {
return this.frozen;
} else {
final SQLSelect res = new SQLSelect(this.frozen);
res.andWhere(w);
return res;
final SQLSelect copy = new SQLSelect(this.frozen);
final SQLSelect res = isNopTransf ? copy : checkTr(copy.getSelect(), selTransf.transformChecked(copy));
return res.andWhere(w);
}
}
 
841,11 → 849,14
 
if (this.getSelID() != null)
sel.andWhere(getIDWhere(t, this.getSelID()));
final List<String> origSel = new ArrayList<String>(sel.getSelect());
SQLSelect res = sel;
for (final ITransformer<SQLSelect, SQLSelect> tr : this.getSelectTransformers()) {
res = tr.transformChecked(res);
}
return res.andWhere(w);
if (!isNopTransf)
res = selTransf.transformChecked(res);
return checkTr(origSel, res).andWhere(w);
}
 
static private Where getIDWhere(final SQLTable t, final Number id) {
1147,17 → 1158,20
* table.
*/
public final List<SQLRowValues> fetch(final Where w, final Boolean unmodifiableRows) throws IllegalArgumentException {
return this.fetch(true, w, unmodifiableRows);
return this.fetch(w, null, unmodifiableRows);
}
 
private final List<SQLRowValues> fetch(final boolean merge, final Where w, final Boolean unmodifiableRows) throws IllegalArgumentException {
checkTable(w);
public final List<SQLRowValues> fetch(final Where w, final ITransformer<SQLSelect, SQLSelect> selTransf, final Boolean unmodifiableRows) throws IllegalArgumentException {
return this.fetch(true, w, selTransf, unmodifiableRows);
}
 
private final List<SQLRowValues> fetch(final boolean merge, final Where w, final ITransformer<SQLSelect, SQLSelect> selTransf, final Boolean unmodifiableRows) throws IllegalArgumentException {
final SQLSelect req;
final Map<Path, Map<Path, SQLRowValuesListFetcher>> grafts;
final boolean freezeRows;
// the only other internal state used is this.descendantPath which is final immutable
synchronized (this) {
req = this.getReq(w);
req = this.getReq(w, selTransf);
grafts = this.getGrafts();
freezeRows = unmodifiableRows == null ? this.areReturnedRowsUnmodifiable() : unmodifiableRows.booleanValue();
}
1164,9 → 1178,7
// getName() would take 5% of ResultSetHandler.handle()
final List<FieldRef> selectFields = req.getSelectFields();
final int selectFieldsSize = selectFields.size();
final List<String> selectFieldsNames = new ArrayList<String>(selectFieldsSize);
for (final FieldRef f : selectFields)
selectFieldsNames.add(f.getField().getName());
final List<String> selectFieldsNames = req.getSelectNames();
final SQLTable table = getGraph().getTable();
 
// create a flat list of the graph nodes, we just need the table, field count and the index
1198,8 → 1210,8
// otherwise walk() would already have thrown an exception
assert fieldIndex.get() <= selectFieldsSize;
if (fieldIndex.get() != selectFieldsSize) {
throw new IllegalStateException(
"Fields have been added to the select (which is useless, since only fields specified by rows are returned) : " + selectFields.subList(fieldIndex.get(), selectFieldsSize));
throw new IllegalStateException("Items have been added to the select (which is useless, since only fields specified by rows are returned and WHERE cannot access SELECT columns) : "
+ selectFields.subList(fieldIndex.get(), selectFieldsSize));
}
assert l.size() == graphSize : "All nodes weren't explored once : " + l.size() + " != " + graphSize + "\n" + this.getGraph().printGraph();
 
1245,7 → 1257,7
final SQLRowValuesListFetcher graft = e.getValue();
 
// don't merge then...
final List<SQLRowValues> referentVals = graft.fetch(false, new Where(graft.getGraph().getTable().getKey(), ids), false);
final List<SQLRowValues> referentVals = graft.fetch(false, new Where(graft.getGraph().getTable().getKey(), ids), null, false);
// ...but now
merge(merged, referentVals, byRows, descendantPath);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSyntaxMS.java
27,7 → 27,6
import org.openconcerto.utils.RTInterruptedException;
import org.openconcerto.utils.StringUtils;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.IClosure;
import org.openconcerto.utils.cc.ITransformer;
 
import java.io.BufferedReader;
47,6 → 46,7
import java.sql.Types;
import java.util.Arrays;
import java.util.BitSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
54,8 → 54,23
 
class SQLSyntaxMS extends SQLSyntax {
 
static private final IdentityHashMap<String, String> DATE_SPECS;
 
static {
DATE_SPECS = new IdentityHashMap<String, String>();
DATE_SPECS.put(DateProp.YEAR, "yyyy");
DATE_SPECS.put(DateProp.MONTH_NAME, "MMMM");
DATE_SPECS.put(DateProp.MONTH_NUMBER, "MM");
DATE_SPECS.put(DateProp.DAY_IN_MONTH, "dd");
DATE_SPECS.put(DateProp.DAY_NAME_IN_WEEK, "dddd");
DATE_SPECS.put(DateProp.HOUR, "HH");
DATE_SPECS.put(DateProp.MINUTE, "mm");
DATE_SPECS.put(DateProp.SECOND, "ss");
DATE_SPECS.put(DateProp.MICROSECOND, "ffffff");
}
 
SQLSyntaxMS() {
super(SQLSystem.MSSQL);
super(SQLSystem.MSSQL, DATE_SPECS);
this.typeNames.addAll(Boolean.class, "bit");
// tinyint is unsigned
this.typeNames.addAll(Short.class, "smallint", "tinyint");
63,12 → 78,12
this.typeNames.addAll(Long.class, "bigint");
this.typeNames.addAll(BigDecimal.class, "decimal", "numeric", "smallmoney", "money");
this.typeNames.addAll(Float.class, "real");
this.typeNames.addAll(Double.class, "float");
this.typeNames.addAll(Timestamp.class, "smalldatetime", "datetime");
this.typeNames.addAll(Double.class, "double precision", "float");
this.typeNames.addAll(Timestamp.class, "datetime2", "datetime", "smalldatetime");
this.typeNames.addAll(java.sql.Date.class, "date");
this.typeNames.addAll(java.sql.Time.class, "time");
this.typeNames.addAll(Blob.class, "image",
// byte[]
// byte[]
"varbinary", "binary");
this.typeNames.addAll(Clob.class, "text", "ntext", "unitext");
this.typeNames.addAll(String.class, "char", "varchar", "nchar", "nvarchar", "unichar", "univarchar");
75,6 → 90,22
}
 
@Override
public final String quoteString(String s) {
final String res = super.quoteString(s);
if (s == null)
return res;
// only use escape form if needed (=> equals with other systems most of the time)
boolean simpleASCII = true;
final int l = s.length();
for (int i = 0; simpleASCII && i < l; i++) {
final char c = s.charAt(i);
simpleASCII = c <= 0xFF;
}
// see http://msdn.microsoft.com/fr-fr/library/ms191200(v=sql.105).aspx
return simpleASCII ? res : "N" + res;
}
 
@Override
public int getMaximumIdentifierLength() {
// https://msdn.microsoft.com/en-us/library/ms143432.aspx
return 128;
81,11 → 112,6
}
 
@Override
SQLBase createBase(SQLServer server, String name, final IClosure<? super DBSystemRoot> systemRootInit, String login, String pass, IClosure<? super SQLDataSource> dsInit) {
return new MSSQLBase(server, name, systemRootInit, login, pass, dsInit);
}
 
@Override
public String getInitSystemRoot() {
final String sql;
try {
108,16 → 134,6
}
 
@Override
public String getDateAndTimeType() {
return "datetime2";
}
 
@Override
public String getBooleanType() {
return "bit";
}
 
@Override
public int getMaximumVarCharLength() {
// http://msdn.microsoft.com/en-us/library/ms176089(v=sql.105).aspx
return 8000;
165,7 → 181,7
final String s = enable ? "with check check constraint all" : "nocheck constraint all";
return "exec sp_MSforeachtable @command1 = 'ALTER TABLE ? " + s + "' , @whereand = " +
//
b.getBase().quoteString("and schema_id = SCHEMA_ID( " + b.getBase().quoteString(b.getName()) + " )");
quoteString("and schema_id = SCHEMA_ID( " + quoteString(b.getName()) + " )");
}
 
@Override
193,7 → 209,7
//
" join [test].[sys].[columns] cols on t.object_id = cols.object_id and cols.column_id = indexCols.column_id \n" +
//
" where schema_name(t.schema_id) = " + t.getBase().quoteString(t.getSchema().getName()) + " and t.name = " + t.getBase().quoteString(t.getName()) + "\n"
" where schema_name(t.schema_id) = " + quoteString(t.getSchema().getName()) + " and t.name = " + quoteString(t.getName()) + "\n"
//
+ "ORDER BY \"NON_UNIQUE\", \"TYPE\", \"INDEX_NAME\", \"ORDINAL_POSITION\";";
// don't cache since we don't listen on system tables
218,7 → 234,7
public boolean isUniqueException(SQLException exn) {
return SQLUtils.findWithSQLState(exn).getErrorCode() == 2601;
}
 
@Override
public boolean isDeadLockException(SQLException exn) {
return SQLUtils.findWithSQLState(exn).getErrorCode() == 1205;
490,10 → 506,10
@Override
public String getFunctionQuery(SQLBase b, Set<String> schemas) {
return " select name, schema_name(schema_id) as \"schema\", cast(OBJECT_DEFINITION(object_id) as varchar(4096)) as \"src\"\n"
//
//
+ " FROM " + new SQLName(b.getName(), "sys", "objects") + "\n"
// scalar, inline table-valued, table-valued
+ " where type IN ('FN', 'IF', 'TF') and SCHEMA_NAME( schema_id ) in (" + quoteStrings(b, schemas) + ") ";
+ " where type IN ('FN', 'IF', 'TF') and SCHEMA_NAME( schema_id ) in (" + quoteStrings(schemas) + ") ";
}
 
@Override
505,7 → 521,7
//
+ "join " + new SQLName(b.getName(), "sys", "objects") + " tabl on trig.parent_id = tabl.object_id\n"
// requested tables
+ getTablesMapJoin(b, tables, "SCHEMA_NAME( tabl.schema_id )", "tabl.name");
+ getTablesMapJoin(tables, "SCHEMA_NAME( tabl.schema_id )", "tabl.name");
}
 
@Override
518,20 → 534,18
return "SELECT TABLE_SCHEMA as \"" + INFO_SCHEMA_NAMES_KEYS.get(0) + "\", TABLE_NAME as \"" + INFO_SCHEMA_NAMES_KEYS.get(1) + "\", COLUMN_NAME as \"" + INFO_SCHEMA_NAMES_KEYS.get(2)
+ "\" , CHARACTER_SET_NAME as \"CHARACTER_SET_NAME\", COLLATION_NAME as \"COLLATION_NAME\" from INFORMATION_SCHEMA.COLUMNS\n" +
// requested tables
getTablesMapJoin(b, tables, "TABLE_SCHEMA", "TABLE_NAME");
getTablesMapJoin(tables, "TABLE_SCHEMA", "TABLE_NAME");
}
 
@Override
public List<Map<String, Object>> getConstraints(SQLBase b, TablesMap tables) throws SQLException {
final String where = getTablesMapJoin(b, tables, "SCHEMA_NAME(t.schema_id)", "t.name");
final String where = getTablesMapJoin(tables, "SCHEMA_NAME(t.schema_id)", "t.name");
final String sel = "SELECT SCHEMA_NAME(t.schema_id) AS \"TABLE_SCHEMA\", t.name AS \"TABLE_NAME\", k.name AS \"CONSTRAINT_NAME\", case k.type when 'UQ' then 'UNIQUE' when 'PK' then 'PRIMARY KEY' end as \"CONSTRAINT_TYPE\", col_name(c.object_id, c.column_id) AS \"COLUMN_NAME\", c.key_ordinal AS \"ORDINAL_POSITION\", null AS [DEFINITION]\n"
+ "FROM sys.key_constraints k\n"
//
+ "JOIN sys.index_columns c ON c.object_id = k.parent_object_id AND c.index_id = k.unique_index_id\n"
//
+ "JOIN sys.tables t ON t.object_id = k.parent_object_id\n"
+ where
+ "\nUNION ALL\n"
+ "JOIN sys.tables t ON t.object_id = k.parent_object_id\n" + where + "\nUNION ALL\n"
//
+ "SELECT SCHEMA_NAME(t.schema_id) AS \"TABLE_SCHEMA\", t.name AS \"TABLE_NAME\", k.name AS \"CONSTRAINT_NAME\", 'CHECK' as \"CONSTRAINT_TYPE\", col.name AS \"COLUMN_NAME\", 1 AS \"ORDINAL_POSITION\", k.[definition] AS [DEFINITION]\n"
+ "FROM sys.check_constraints k\n"
540,8 → 554,7
//
+ "left join sys.columns col on k.parent_column_id = col.column_id and col.object_id = t.object_id\n"
//
+ where
+ "\nUNION ALL\n"
+ where + "\nUNION ALL\n"
//
+ "SELECT SCHEMA_NAME(t.schema_id) AS [TABLE_SCHEMA], t.name AS [TABLE_NAME], k.name AS [CONSTRAINT_NAME], 'DEFAULT' as [CONSTRAINT_TYPE], col.name AS [COLUMN_NAME], 1 AS [ORDINAL_POSITION], k.[definition] AS [DEFINITION]\n"
+ "FROM sys.[default_constraints] k\n"
576,8 → 589,18
}
 
@Override
public String getDayOfWeek(String sqlTS) {
return "SELECT DATEPART(dw, " + sqlTS + ")";
}
 
@Override
public String getMonth(String sqlTS) {
return "SELECT DATEPART(month, " + sqlTS + ")";
}
 
@Override
public String getFormatTimestamp(String sqlTS, boolean basic) {
final String extended = "CONVERT(nvarchar(30), CAST(" + sqlTS + " as datetime), 126) + '000'";
final String extended = "CONVERT(nvarchar(30), " + sqlTS + ", 126) + '000'";
if (basic) {
return "replace( replace( " + extended + ", '-', ''), ':' , '' )";
} else {
584,4 → 607,14
return extended;
}
}
 
@Override
public String getFormatTimestamp(String sqlTS, String nativeFormat) {
return "FORMAT(" + sqlTS + ", " + nativeFormat + ")";
}
 
@Override
public String quoteForTimestampFormat(String text) {
return StringUtils.doubleQuote(text);
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLTable.java
13,8 → 13,11
package org.openconcerto.sql.model;
 
import static org.openconcerto.xml.JDOM2Utils.OUTPUTTER;
 
import org.openconcerto.sql.Log;
import org.openconcerto.sql.model.SQLSelect.ArchiveMode;
import org.openconcerto.sql.model.SQLSelect.LockStrength;
import org.openconcerto.sql.model.SQLSyntax.ConstraintType;
import org.openconcerto.sql.model.SQLTableEvent.Mode;
import org.openconcerto.sql.model.graph.DatabaseGraph;
38,7 → 41,6
import org.openconcerto.utils.cc.CopyOnWriteMap;
import org.openconcerto.utils.cc.CustomEquals;
import org.openconcerto.utils.change.CollectionChangeEventCreator;
import org.openconcerto.xml.JDOMUtils;
 
import java.math.BigDecimal;
import java.sql.DatabaseMetaData;
214,7 → 216,7
l.add(Arrays.asList(b.quoteString(tableName), undefSQL));
}
}
final SQLSyntax syntax = system.getSyntax();
final SQLSyntax syntax = schema.getDBSystemRoot().getSyntax();
if (toInsert.size() > 0) {
// INSERT
SQLRowValues.insertCount(undefT, "(" + SQLSyntax.quoteIdentifiers(Arrays.asList(UNDEF_TABLE_TABLENAME_FIELD, UNDEF_TABLE_ID_FIELD)) + ") " + syntax.getValues(toInsert, 2));
310,7 → 312,7
private String version;
private final CopyOnWriteMap<String, SQLField> fields;
@GuardedBy("this")
private final Set<SQLField> primaryKeys;
private Set<SQLField> primaryKeys;
// the vast majority of our code use getKey(), so cache it for performance
@GuardedBy("this")
private SQLField primaryKey;
363,8 → 365,7
}
};
assert isOrdered(this.fields);
// order matters (eg for indexes)
this.primaryKeys = new LinkedHashSet<SQLField>();
this.primaryKeys = Collections.emptySet();
this.primaryKey = null;
this.primaryKeyOK = true;
this.keys = null;
512,7 → 513,7
 
// must be called in setState() after fields have been set (for isRowable())
private int fetchUndefID() {
final int res;
int res;
final SQLField pk;
synchronized (this) {
pk = isRowable() ? this.getKey() : null;
520,8 → 521,14
if (pk != null) {
final Tuple2<Boolean, Number> currentValue = getUndefID(this.getSchema(), this.getName());
if (!currentValue.get0()) {
// no row
res = this.findMinID(pk);
try {
// no row
res = this.findMinID(pk);
} catch (Exception e) {
// we ***** don't care
e.printStackTrace();
res = SQLRow.NONEXISTANT_ID;
}
} else {
// a row
final Number id = currentValue.get1();
546,8 → 553,9
// empty table
throw new IllegalStateException(this + " is empty, can not infer UNDEFINED_ID");
} else {
final String update = SQLSyntax.get(this).getInsertOne(new SQLName(this.getDBRoot().getName(), undefTable), Arrays.asList(UNDEF_TABLE_TABLENAME_FIELD, UNDEF_TABLE_ID_FIELD),
getBase().quoteString(this.getName()), String.valueOf(undef));
final SQLSyntax syntax = SQLSyntax.get(this);
final String update = syntax.getInsertOne(new SQLName(this.getDBRoot().getName(), undefTable), Arrays.asList(UNDEF_TABLE_TABLENAME_FIELD, UNDEF_TABLE_ID_FIELD),
syntax.quoteString(this.getName()), String.valueOf(undef));
Log.get().config("the first row (which should be the undefined):\n" + update);
return undef.intValue();
}
630,9 → 638,11
}
}
 
this.primaryKeys.clear();
// order matters (e.g. for indexes)
final Set<SQLField> newPK = new LinkedHashSet<SQLField>();
for (final String pk : primaryKeys)
this.primaryKeys.add(this.getField(pk));
newPK.add(this.getField(pk));
this.primaryKeys = Collections.unmodifiableSet(newPK);
this.primaryKey = primaryKeys.size() == 1 ? this.getField(primaryKeys.get(0)) : null;
this.primaryKeyOK = primaryKeys.size() <= 1;
 
762,7 → 772,7
* @return the fields (SQLField) which are the keys of this table, can be empty.
*/
public synchronized Set<SQLField> getPrimaryKeys() {
return Collections.unmodifiableSet(this.primaryKeys);
return this.primaryKeys;
}
 
public final Set<Link> getForeignLinks() {
1117,7 → 1127,7
}
}
}
return res;
return Collections.unmodifiableSet(res);
}
 
public final Set<String> getFieldsNames(final VirtualFields vfs) {
1128,6 → 1138,23
return res;
}
 
public final List<SQLField> getFields(final Collection<String> names) {
return this.getFields(names, new ArrayList<SQLField>());
}
 
public final <T extends Collection<SQLField>> T getFields(final Collection<String> names, final T res) {
return this.getFields(names, res, true);
}
 
public final <T extends Collection<SQLField>> T getFields(final Collection<String> names, final T res, final boolean required) {
for (final String name : names) {
final SQLField f = required ? this.getField(name) : this.getFieldRaw(name);
if (f != null)
res.add(f);
}
return res;
}
 
/**
* Retourne les champs du contenu de cette table. C'est à dire ni la clef primaire, ni les
* champs d'archive et d'ordre.
1267,7 → 1294,7
* archivée.
*/
public SQLRow checkValidity(int ID) {
SQLRow row = this.getUncheckedRow(ID);
final SQLRow row = SQLRow.createFromSelect(this, VirtualFields.PRIMARY_KEY.union(VirtualFields.ARCHIVE), ID, LockStrength.SHARE);
// l'inverse de getValidRow()
return row.isValid() ? null : row;
}
1684,13 → 1711,13
public synchronized String toXML() {
final StringBuilder sb = new StringBuilder(16000);
sb.append("<table name=\"");
sb.append(JDOMUtils.OUTPUTTER.escapeAttributeEntities(this.getName()));
sb.append(OUTPUTTER.escapeAttributeEntities(this.getName()));
sb.append("\"");
 
final String schemaName = this.getSchema().getName();
if (schemaName != null) {
sb.append(" schema=\"");
sb.append(JDOMUtils.OUTPUTTER.escapeAttributeEntities(schemaName));
sb.append(OUTPUTTER.escapeAttributeEntities(schemaName));
sb.append('"');
}
 
1698,7 → 1725,7
 
if (getType() != null) {
sb.append(" type=\"");
sb.append(JDOMUtils.OUTPUTTER.escapeAttributeEntities(getType()));
sb.append(OUTPUTTER.escapeAttributeEntities(getType()));
sb.append('"');
}
 
1706,7 → 1733,7
 
if (this.getComment() != null) {
sb.append("<comment>");
sb.append(JDOMUtils.OUTPUTTER.escapeElementEntities(this.getComment()));
sb.append(OUTPUTTER.escapeElementEntities(this.getComment()));
sb.append("</comment>\n");
}
for (SQLField field : this.fields.values()) {
1924,11 → 1951,11
}
 
public final SQLCreateMoveableTable getCreateTable() {
return this.getCreateTable(this.getServer().getSQLSystem());
return this.getCreateTable(SQLSyntax.get(this));
}
 
public synchronized final SQLCreateMoveableTable getCreateTable(final SQLSystem system) {
final SQLSyntax syntax = SQLSyntax.get(system);
public synchronized final SQLCreateMoveableTable getCreateTable(final SQLSyntax syntax) {
final SQLSystem system = syntax.getSystem();
final SQLCreateMoveableTable res = new SQLCreateMoveableTable(syntax, this.getDBRoot().getName(), this.getName());
for (final SQLField f : this.getOrderedFields()) {
res.addColumn(f);
1954,7 → 1981,6
// not null" in addUniqueConstraint(). Thus when converting to another system we must
// parse indexes to recreate actual constraints.
final boolean convertMSIndex = this.getServer().getSQLSystem() == SQLSystem.MSSQL && system != SQLSystem.MSSQL;
final Set<List<SQLField>> foreignKeysFields = getForeignKeysFields();
for (final Index i : this.getIndexes(true)) {
Value<String> msWhere = null;
if (convertMSIndex && (msWhere = i.getMSUniqueWhere()).hasValue()) {
1961,12 → 1987,12
if (msWhere.getValue() != null)
Log.get().warning("MS filter might not be valid in " + system + " : " + msWhere.getValue());
res.addUniqueConstraint(i.getName(), i.getCols(), msWhere.getValue());
} else if (!system.autoCreatesFKIndex() || !foreignKeysFields.contains(i.getFields())) {
} else {
// partial unique index sometimes cannot be handled natively by the DB system
if (i.isUnique() && i.getFilter() != null && !system.isIndexFilterConditionSupported())
res.addUniqueConstraint(i.getName(), i.getCols(), i.getFilter());
else
res.addOutsideClause(syntax.getCreateIndex(i));
res.addIndex(i);
}
}
} catch (SQLException e) {
2040,13 → 2066,13
* automatically.
*
* @return the list of indexes.
* @throws SQLException if an error occurs.
* @throws SQLException if an error occurs while accessing the DB.
*/
public synchronized final List<Index> getIndexes() throws SQLException {
public final List<Index> getIndexes() throws SQLException {
return this.getIndexes(false);
}
 
protected synchronized final List<Index> getIndexes(final boolean normalized) throws SQLException {
public synchronized final List<Index> getIndexes(final boolean normalized) throws SQLException {
// in pg, a unique constraint creates a unique index that is not removeable
// (except of course if we drop the constraint)
// in mysql unique constraints and indexes are one and the same thing
2065,7 → 2091,7
 
final List<Index> indexes = new ArrayList<Index>();
Index currentIndex = null;
for (final Map<String, Object> norm : this.getServer().getSQLSystem().getSyntax().getIndexInfo(this)) {
for (final Map<String, Object> norm : this.getDBSystemRoot().getSyntax().getIndexInfo(this)) {
final Index index = new Index(norm);
final short seq = ((Number) norm.get("ORDINAL_POSITION")).shortValue();
if (seq == 1) {
/trunk/OpenConcerto/src/org/openconcerto/sql/model/DBFileCache.java
15,6 → 15,7
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.graph.DatabaseGraph;
import org.openconcerto.utils.BaseDirs;
import org.openconcerto.utils.FileUtils;
import org.openconcerto.utils.ProductInfo;
import org.openconcerto.utils.StringUtils;
50,18 → 51,18
private static final String FILE_STRUCT_VERSION = "20080904-1411";
 
static private final File getFwkSaveDir() {
final File confDir;
final BaseDirs confDir;
// require at least an application name, since settings might influence what is to be saved
// MAYBE pass the server and allow it to have an ID
// (this would handle the case when one app needs two different connections to a server)
if (Configuration.getInstance() != null) {
confDir = Configuration.getInstance().getConfDir();
confDir = Configuration.getInstance().getBaseDirs();
} else if (ProductInfo.getInstance() != null) {
confDir = new File(Configuration.getDefaultConfDir(), ProductInfo.getInstance().getName());
confDir = BaseDirs.create(ProductInfo.getInstance());
} else {
return null;
}
return new File(confDir, "DBCache/" + FILE_STRUCT_VERSION);
return new File(confDir.getCacheFolder(), "DBCache/" + FILE_STRUCT_VERSION);
}
 
static private final File getValidFwkSaveDir() {
/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/DBStructureItem.java
112,7 → 112,7
}
 
public final boolean contains(String childName) {
return this.getChildrenNames().contains(childName);
return this.getChildrenMap().containsKey(childName);
}
 
/**
176,14 → 176,20
}
 
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)
throw new IllegalArgumentException(this + " is the root, can't go up of " + level);
else
return this.getParent().getAncestor(level - 1);
} 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 parentForJavac.getAncestor(level - 1);
}
}
 
/**
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRow.java
18,6 → 18,7
 
import org.openconcerto.sql.Log;
import org.openconcerto.sql.model.SQLSelect.ArchiveMode;
import org.openconcerto.sql.model.SQLSelect.LockStrength;
import org.openconcerto.sql.model.SQLTable.VirtualFields;
import org.openconcerto.sql.model.graph.Link;
import org.openconcerto.sql.model.graph.Link.Direction;
186,6 → 187,25
return res;
}
 
static final SQLRow createFromSelect(final SQLTable t, final VirtualFields vfs, final int id, final LockStrength l) {
final SQLSelect sel = new SQLSelect(true).addAllSelect(t.getFields(vfs));
sel.setLockStrength(l);
sel.setWhere(new Where(t.getKey(), "=", id));
return new SQLRow(t, id, t.getDBSystemRoot().getDataSource().execute1(sel.asString()));
}
 
/**
* Create an empty existing row (without checking the DB).
*
* @param t the table.
* @param id the ID.
* @return a new {@link #exists() existing} {@link #isFilled() filled} {@link #getFields()
* empty} row.
*/
static final SQLRow createEmpty(final SQLTable t, final int id) {
return new SQLRow(t, id, Collections.<String, Object> emptyMap());
}
 
private final int ID;
private final Number idNumber;
private Map<String, Object> values;
226,7 → 246,7
private SQLRow(SQLTable table, final Number id, Map<String, ?> values) {
this(table, id == null ? getID(values, table, false) : id);
// faire une copie, sinon backdoor pour changer les valeurs sans qu'on s'en aperçoive
this.setValues(new HashMap<String, Object>(values));
this.setValues(values == null ? null : new HashMap<String, Object>(values));
}
 
// return ID, must always be present but may be null if <code>nullAllowed</code>
428,18 → 448,6
return this.getForeignRow(fieldName);
}
 
@Override
public Number getForeignIDNumber(String fieldName) throws IllegalArgumentException {
final SQLRow foreignRow = this.getForeignRow(fieldName, SQLRowMode.NO_CHECK);
return foreignRow == null ? null : foreignRow.getIDNumber();
}
 
@Override
public boolean isForeignEmpty(String fieldName) {
final SQLRow foreignRow = this.getForeignRow(fieldName, SQLRowMode.NO_CHECK);
return foreignRow == null || foreignRow.isUndefined();
}
 
/**
* Retourne la ligne sur laquelle pointe le champ passé. Elle peut être archivé ou indéfinie.
*
/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/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/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/SQLRowValuesCluster.java
360,20 → 360,16
}
 
public final StoreResult insert() throws SQLException {
return this.insert(false, false);
return this.store(StoreMode.INSERT);
}
 
public final StoreResult insert(boolean insertPK, boolean insertOrder) throws SQLException {
return this.store(new Insert(insertPK, insertOrder));
}
 
public final StoreResult store(final StoreMode mode) throws SQLException {
return this.store(mode, true);
return this.store(mode, null);
}
 
// checkValidity false useful when we want to avoid loading the graph
public final StoreResult store(final StoreMode mode, final boolean checkValidity) throws SQLException {
return this.store(mode, null, null, checkValidity);
public final StoreResult store(final StoreMode mode, final Boolean checkValidity) throws SQLException {
return this.store(mode, null, null, checkValidity, true);
}
 
/**
383,12 → 379,15
* @param start when storing a subset, the start of <code>pruneGraph</code> in this, can be
* <code>null</code>.
* @param pruneGraph the maximum graph to store, can be <code>null</code>.
* @param checkValidity <code>true</code> to check if foreign keys point to valid rows.
* @param checkValidity whether to ask for checking if foreign keys point to valid rows, see
* {@link SQLRowValues#setValidityChecked(SQLRowValues.ValidityCheck)}.
* @param fireEvent <code>false</code> if stored rows shouldn't be fetched and
* {@link SQLTableEvent} should not be fired.
* @return the store result.
* @throws SQLException if an exception occurs.
* @see {@link #prune(SQLRowValues, SQLRowValues)}
*/
public final StoreResult store(final StoreMode mode, final SQLRowValues start, final SQLRowValues pruneGraph, final boolean checkValidity) throws SQLException {
public final StoreResult store(final StoreMode mode, final SQLRowValues start, final SQLRowValues pruneGraph, final Boolean checkValidity, final boolean fireEvent) throws SQLException {
final Map<SQLRowValues, SQLRowValues> prune2orig;
final SQLRowValuesCluster toStore;
final boolean prune = pruneGraph != null;
412,7 → 411,7
}
// check validity first, avoid beginning a transaction for nothing
// do it after reset otherwise check previous values
if (checkValidity)
if (SQLRowValues.isValidityChecked(checkValidity))
for (final Node n : nodes.values()) {
n.noLink.checkValidity();
}
482,9 → 481,9
 
if (n.isStored()) {
// if there's a cycle, we have to update an already inserted row
res.add(n.update());
res.add(n.update(fireEvent));
} else {
res.add(n.store(mode));
res.add(n.store(fireEvent, mode));
final SQLRow r = n.getStoredRow();
 
// fill the noLink of referent nodes with the new ID
499,7 → 498,7
// link together the new values
// if there is a cycle not all foreign keys can be stored at the same time, so
// wait for the last DB access
if (lastDBAccess)
if (lastDBAccess) {
for (final Map.Entry<String, SQLRowValues> e : toStore.getSrc().getForeigns().entrySet()) {
final SQLRowValues foreign = nodes.get(e.getValue()).getStoredValues();
assert foreign != null : "since this the last db access for this row, all foreigns should have been inserted";
508,15 → 507,23
throw new IllegalStateException("stored " + n.getStoredValues().getObject(e.getKey()) + " but foreign is " + SQLRowValues.trim(foreign));
n.getStoredValues().put(e.getKey(), foreign);
}
}
}
// all nodes share the same graph, so pick any and freeze the graph
// null if !fireEvent or if non-rowable table
final SQLRowValues graphFetched = nodes.values().iterator().next().getStoredValues();
if (graphFetched != null)
graphFetched.getGraph().freeze();
return res;
}
});
// fire events
for (final SQLTableEvent n : events) {
// MAYBE put a Map<SQLRowValues, SQLTableEvent> to know how our fellow values have been
// affected
n.getTable().fire(n);
if (fireEvent) {
for (final SQLTableEvent n : events) {
// MAYBE put a Map<SQLRowValues, SQLTableEvent> to know how our fellow values have
// been affected
n.getTable().fire(n);
}
}
 
return new StoreResult(res);
963,6 → 970,7
return map;
}
 
// TODO handle referents (and decide how to handle multiple paths to the same node)
final void grow(final SQLRowValues start, final SQLRowValues toGrow, final boolean checkFields) {
this.containsCheck(start);
if (!start.getTable().equals(toGrow.getTable()))
975,7 → 983,6
final SQLRowValues leaf = toGrow.assurePath(input.getPath());
if (leaf.hasID()) {
final SQLRowValuesListFetcher fetcher = new SQLRowValuesListFetcher(input.getCurrent());
fetcher.setSelID(leaf.getID());
fetcher.setSelTransf(new ITransformer<SQLSelect, SQLSelect>() {
@Override
public SQLSelect transformChecked(SQLSelect input) {
985,7 → 992,7
return input;
}
});
final SQLRowValues fetched = CollectionUtils.getSole(fetcher.fetch());
final SQLRowValues fetched = fetcher.fetchOne(leaf.getIDNumber());
if (fetched == null)
throw new IllegalArgumentException("no row for " + fetcher);
leaf.load(fetched, null);
1524,12 → 1531,19
this.noLink = new SQLRowValues(vals, ForeignCopyMode.NO_COPY);
}
 
private SQLTableEvent store(StoreMode mode) throws SQLException {
private SQLTableEvent store(final boolean fetchStoredRow, StoreMode mode) throws SQLException {
return this.store(fetchStoredRow, mode, true);
}
 
private SQLTableEvent store(final boolean fetchStoredRow, StoreMode mode, final boolean setRowValues) throws SQLException {
assert !this.isStored();
return this.addEvent(mode.execOn(this.noLink));
final SQLTableEvent evt = this.addEvent(mode.execOn(this.noLink, fetchStoredRow));
if (fetchStoredRow && evt.getRow() != null && setRowValues)
evt.setRowValues(evt.getRow().asRowValues());
return evt;
}
 
private SQLTableEvent update() throws SQLException {
private SQLTableEvent update(final boolean fetchStoredRow) throws SQLException {
assert this.isStored();
 
// fields that have been updated since last store
1540,13 → 1554,15
final SQLRowValues updatingVals = this.getStoredRow().createEmptyUpdateRow();
updatingVals.load(this.noLink, fieldsToUpdate);
 
final SQLTableEvent evt = new Node(updatingVals).store(StoreMode.COMMIT);
final SQLTableEvent evt = new Node(updatingVals).store(fetchStoredRow, StoreMode.COMMIT, false);
// Update previous rowValues, and use it for the new event
// that way there's only one graph of rowValues (with the final values) for all events.
// Load all fields since updating 1 field might change the value of another (e.g.
// with a trigger).
this.getStoredValues().load(evt.getRow(), null);
evt.setRowValues(this.getStoredValues());
if (fetchStoredRow && evt.getRow() != null) {
this.getStoredValues().load(evt.getRow(), null);
evt.setRowValues(this.getStoredValues());
}
return this.addEvent(evt);
}
 
1574,7 → 1590,8
}
 
private final SQLTableEvent addEvent(SQLTableEvent evt) {
assert evt != null;
if (evt == null)
throw new IllegalStateException("Couldn't update missing row " + this.noLink);
this.modif.add(evt);
return evt;
}
1591,9 → 1608,11
* @author Sylvain
*/
public static abstract class StoreMode {
abstract SQLTableEvent execOn(SQLRowValues vals) throws SQLException;
abstract SQLTableEvent execOn(SQLRowValues vals, final boolean fetchStoredRow) throws SQLException;
 
public static final StoreMode COMMIT = new Commit();
public static final StoreMode INSERT = new Insert(false, false);
public static final StoreMode INSERT_VERBATIM = new Insert(true, true);
}
 
public static class Insert extends StoreMode {
1608,20 → 1627,21
}
 
@Override
SQLTableEvent execOn(SQLRowValues vals) throws SQLException {
SQLTableEvent execOn(SQLRowValues vals, final boolean fetchStoredRow) throws SQLException {
final Set<SQLField> autoFields = new HashSet<SQLField>();
if (!this.insertPK)
autoFields.addAll(vals.getTable().getPrimaryKeys());
if (!this.insertOrder)
autoFields.add(vals.getTable().getOrderField());
return vals.insertJustThis(autoFields);
return vals.insertJustThis(fetchStoredRow, autoFields);
}
}
 
public static class Commit extends StoreMode {
 
@Override
SQLTableEvent execOn(SQLRowValues vals) throws SQLException {
return vals.commitJustThis();
SQLTableEvent execOn(SQLRowValues vals, final boolean fetchStoredRow) throws SQLException {
return vals.commitJustThis(fetchStoredRow);
}
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/model/Where.java
14,6 → 14,7
package org.openconcerto.sql.model;
 
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.StringUtils;
import org.openconcerto.utils.cc.ITransformer;
 
import java.util.ArrayList;
24,10 → 25,10
import java.util.Map;
import java.util.Map.Entry;
 
import org.apache.commons.collections.functors.InstanceofPredicate;
 
import net.jcip.annotations.Immutable;
 
import org.apache.commons.collections.functors.InstanceofPredicate;
 
/**
* Une clause WHERE dans une requete SQL. Une clause peut être facilement combinée avec d'autre,
* exemple : prenomPasVide.and(pasIndéfini).and(age_sup_3.or(assez_grand)).
102,6 → 103,10
return AndCombiner.combine(where1, where2);
}
 
static public Where or(final Where where1, final Where where2) {
return OrCombiner.combine(where1, where2);
}
 
static public Where isNull(final FieldRef ref) {
return new Where(ref, "is", (Object) null);
}
144,7 → 149,7
 
static private final String comparison(final FieldRef ref, final String op, final String y) {
if (op == NULL_IS_DATA_EQ || op == NULL_IS_DATA_NEQ) {
return ref.getField().getServer().getSQLSystem().getSyntax().getNullIsDataComparison(ref.getFieldRef(), op == NULL_IS_DATA_EQ, y);
return ref.getField().getDBSystemRoot().getSyntax().getNullIsDataComparison(ref.getFieldRef(), op == NULL_IS_DATA_EQ, y);
} else {
return ref.getFieldRef() + " " + op + " " + y;
}
261,6 → 266,8
 
// raw ctor, see static methods
private Where(final String clause, final Collection<? extends FieldRef> refs) {
if (StringUtils.isEmpty(clause, true))
throw new IllegalArgumentException("No clause");
this.fields = Collections.unmodifiableList(new ArrayList<FieldRef>(refs));
this.clause = clause;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/Constraint.java
15,7 → 15,7
 
import org.openconcerto.sql.model.SQLSyntax.ConstraintType;
import org.openconcerto.utils.cc.HashingStrategy;
import org.openconcerto.xml.JDOMUtils;
import org.openconcerto.xml.JDOM2Utils;
import org.openconcerto.xml.XMLCodecUtils;
 
import java.util.HashMap;
22,11 → 22,11
import java.util.List;
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 Constraint {
 
89,7 → 89,7
public synchronized String toXML() {
// this is immutable so only compute once the XML
if (this.xml == null)
this.xml = "<constraint name=\"" + JDOMUtils.OUTPUTTER.escapeAttributeEntities(getName()) + "\" >" + XMLCodecUtils.encodeSimple(this.m) + "</constraint>";
this.xml = "<constraint name=\"" + JDOM2Utils.OUTPUTTER.escapeAttributeEntities(getName()) + "\" >" + XMLCodecUtils.encodeSimple(this.m) + "</constraint>";
return this.xml;
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLServer.java
247,8 → 247,7
final Set<String> allCats;
final Connection conn = this.getDS().getNewConnection();
try {
@SuppressWarnings("unchecked")
final List<String> allCatsList = (List<String>) SQLDataSource.COLUMN_LIST_HANDLER.handle(conn.getMetaData().getCatalogs());
final List<String> allCatsList = ColumnListHandlerGeneric.create(String.class).handle(conn.getMetaData().getCatalogs());
allCats = new HashSet<String>(allCatsList);
} finally {
this.getDS().returnConnection(conn);
417,8 → 416,8
final DBSystemRoot sysRoot = this.getDBSystemRoot();
if (sysRoot != null && !sysRoot.createNode(this, baseName))
throw new IllegalStateException(baseName + " is filtered, you must add it to rootsToMap");
final SQLBase base = this.getSQLSystem().getSyntax()
.createBase(this, baseName, coalesce(systemRootInit, this.systemRootInit), login == null ? this.login : login, pass == null ? this.pass : pass, coalesce(dsInit, this.dsInit));
final SQLBase base = new SQLBase(this, baseName, coalesce(systemRootInit, this.systemRootInit), login == null ? this.login : login, pass == null ? this.pass : pass,
coalesce(dsInit, this.dsInit));
return this.putBase(baseName, base, readCache);
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSyntaxMySQL.java
26,7 → 26,6
import org.openconcerto.utils.ListMap;
import org.openconcerto.utils.StringUtils;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.IClosure;
import org.openconcerto.utils.cc.ITransformer;
 
import java.io.BufferedReader;
48,6 → 47,8
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
63,12 → 64,50
*
* @author Sylvain CUAZ
*/
class SQLSyntaxMySQL extends SQLSyntax {
public class SQLSyntaxMySQL extends SQLSyntax {
 
private static final Pattern INT_PATTERN = Pattern.compile("(bigint|smallint|int)");
final static SQLSyntax create(DBSystemRoot sysRoot) {
final boolean noBackslashEscapes;
if (sysRoot == null) {
noBackslashEscapes = false;
} else {
final String modes = (String) sysRoot.getDataSource().executeScalar("SELECT @@global.sql_mode;");
noBackslashEscapes = modes.contains("NO_BACKSLASH_ESCAPES");
}
return new SQLSyntaxMySQL(noBackslashEscapes);
}
 
SQLSyntaxMySQL() {
super(SQLSystem.MYSQL);
static private final IdentityHashMap<String, String> DATE_SPECS;
static private final Map<Class<?>, String> CAST_TYPES;
 
static {
DATE_SPECS = new IdentityHashMap<String, String>();
DATE_SPECS.put(DateProp.YEAR, "%Y");
DATE_SPECS.put(DateProp.MONTH_NAME, "%M");
DATE_SPECS.put(DateProp.MONTH_NUMBER, "%m");
DATE_SPECS.put(DateProp.DAY_IN_MONTH, "%d");
DATE_SPECS.put(DateProp.DAY_NAME_IN_WEEK, "%W");
DATE_SPECS.put(DateProp.HOUR, "%H");
DATE_SPECS.put(DateProp.MINUTE, "%i");
DATE_SPECS.put(DateProp.SECOND, "%S");
DATE_SPECS.put(DateProp.MICROSECOND, "%f");
CAST_TYPES = new HashMap<Class<?>, String>();
CAST_TYPES.put(Short.class, "signed integer");
CAST_TYPES.put(Integer.class, "signed integer");
CAST_TYPES.put(Long.class, "signed integer");
CAST_TYPES.put(BigDecimal.class, "decimal");
CAST_TYPES.put(Timestamp.class, "datetime");
CAST_TYPES.put(java.sql.Date.class, "date");
CAST_TYPES.put(java.sql.Time.class, "time");
CAST_TYPES.put(Blob.class, "binary");
CAST_TYPES.put(String.class, "char");
}
 
private final boolean noBackslashEscapes;
 
public SQLSyntaxMySQL(final boolean noBackslashEscapes) {
super(SQLSystem.MYSQL, DATE_SPECS);
this.noBackslashEscapes = noBackslashEscapes;
this.typeNames.addAll(Boolean.class, "boolean", "bool", "bit");
this.typeNames.addAll(Short.class, "smallint");
this.typeNames.addAll(Integer.class, "integer", "int");
76,8 → 115,9
this.typeNames.addAll(BigDecimal.class, "decimal", "numeric");
this.typeNames.addAll(Float.class, "float");
this.typeNames.addAll(Double.class, "double precision", "real");
this.typeNames.addAll(Timestamp.class, "timestamp");
this.typeNames.addAll(java.util.Date.class, "time");
this.typeNames.addAll(Timestamp.class, "datetime", "timestamp");
this.typeNames.addAll(java.sql.Date.class, "date");
this.typeNames.addAll(java.sql.Time.class, "time");
this.typeNames.addAll(Blob.class, "blob", "tinyblob", "mediumblob", "longblob", "varbinary", "binary");
this.typeNames.addAll(Clob.class, "text", "tinytext", "mediumtext", "longtext", "varchar", "char");
this.typeNames.addAll(String.class, "varchar", "char");
84,6 → 124,21
}
 
@Override
public final String quoteString(String s) {
final String res = super.quoteString(s);
if (s == null)
return res;
// ATTN if noBackslashEscapes is changed for the session,
// then SQL can be injected :
// toto \'; drop table ;
// is quoted to :
// 'toto \''; drop table ;'
// and since DDL is not transactional in MySQL the table is forever dropped.
// escape \ by replacing them with \\
return !this.noBackslashEscapes ? SQLSyntaxPG.BACKSLASH_PATTERN.matcher(res).replaceAll(SQLSyntaxPG.TWO_BACKSLASH_REPLACEMENT) : res;
}
 
@Override
public int getMaximumIdentifierLength() {
// http://dev.mysql.com/doc/refman/5.7/en/identifiers.html
return 64;
104,11 → 159,6
}
 
@Override
public String getDateAndTimeType() {
return "datetime";
}
 
@Override
protected String getAutoDateType(SQLField f) {
return "timestamp";
}
125,9 → 175,9
}
 
@Override
public String cast(String expr, String type) {
public String cast(String expr, Class<?> javaType) {
// MySQL doesn't use types but keywords
return super.cast(expr, INT_PATTERN.matcher(type).replaceAll("integer").replace("integer", "signed integer"));
return this.cast(expr, CAST_TYPES.get(javaType));
}
 
@Override
276,13 → 326,12
// MySQL dumps strings in binary, so fields must be consistent otherwise the
// file is invalid
throw new IllegalArgumentException(t + " has more than on character set : " + charsets);
final SQLBase base = t.getBase();
// if no string cols there should only be values within ASCII (eg dates, ints, etc)
final String charset = charsets.size() == 0 ? "UTF8" : charsets.keySet().iterator().next();
final String cols = CollectionUtils.join(t.getOrderedFields(), ",", new ITransformer<SQLField, String>() {
@Override
public String transformChecked(SQLField input) {
return base.quoteString(input.getName());
return quoteString(input.getName());
}
});
final File tmp = File.createTempFile(SQLSyntaxMySQL.class.getSimpleName() + "storeData", ".txt");
292,7 → 341,7
tmp.delete();
final SQLSelect sel = new SQLSelect(true).addSelectStar(t);
// store the data in the temp file
base.getDataSource().execute("SELECT " + cols + " UNION " + sel.asString() + " INTO OUTFILE " + base.quoteString(tmp.getAbsolutePath()) + " " + getDATA_OPTIONS(base) + ";");
t.getDBSystemRoot().getDataSource().execute("SELECT " + cols + " UNION " + sel.asString() + " INTO OUTFILE " + quoteString(tmp.getAbsolutePath()) + " " + getDATA_OPTIONS() + ";");
// then read it to remove superfluous escape char and convert to utf8
final BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(tmp), charset));
Writer w = null;
342,8 → 391,8
}
}
 
private static String getDATA_OPTIONS(final SQLBase b) {
return "FIELDS TERMINATED BY ',' ENCLOSED BY '\"' ESCAPED BY " + b.quoteString("\\") + " LINES TERMINATED BY '\n' ";
private String getDATA_OPTIONS() {
return "FIELDS TERMINATED BY ',' ENCLOSED BY '\"' ESCAPED BY " + quoteString("\\") + " LINES TERMINATED BY '\n' ";
}
 
@Override
367,7 → 416,7
throw new IllegalStateException("the database charset is not utf8 and this version doesn't support specifying another one : " + dbCharset);
}
}
ds.execute(t.getBase().quote("LOAD DATA LOCAL INFILE %s INTO TABLE %f ", f.getAbsolutePath(), t) + charsetClause + getDATA_OPTIONS(t.getBase()) + " IGNORE 1 LINES;");
ds.execute(t.getBase().quote("LOAD DATA LOCAL INFILE %s INTO TABLE %f ", f.getAbsolutePath(), t) + charsetClause + getDATA_OPTIONS() + " IGNORE 1 LINES;");
return null;
}
});
377,11 → 426,6
}
 
@Override
SQLBase createBase(SQLServer server, String name, final IClosure<? super DBSystemRoot> systemRootInit, String login, String pass, IClosure<? super SQLDataSource> dsInit) {
return new MySQLBase(server, name, systemRootInit, login, pass, dsInit);
}
 
@Override
public String getNullIsDataComparison(String x, boolean eq, String y) {
final String nullSafe = x + " <=> " + y;
if (eq)
391,10 → 435,34
}
 
@Override
public String getDayOfWeek(String sqlTS) {
return "DAYOFWEEK(" + sqlTS + ")";
}
 
@Override
public String getFormatTimestamp(String sqlTS, boolean basic) {
return "DATE_FORMAT(" + sqlTS + ", " + SQLBase.quoteStringStd(basic ? "%Y%m%dT%H%i%s.%f" : "%Y-%m-%dT%H:%i:%s.%f") + ")";
return this.getFormatTimestamp(sqlTS, SQLBase.quoteStringStd(basic ? "%Y%m%dT%H%i%s.%f" : "%Y-%m-%dT%H:%i:%s.%f"));
}
 
@Override
public String getFormatTimestamp(String sqlTS, String nativeFormat) {
return "DATE_FORMAT(" + sqlTS + ", " + nativeFormat + ")";
}
 
static private final Pattern PERCENT_PATTERN = Pattern.compile("(%+)");
 
@Override
public String quoteForTimestampFormat(String text) {
return PERCENT_PATTERN.matcher(text).replaceAll("$1$1");
}
 
@Override
public String getConstantTableStatement(List<List<String>> rows, int colCount) {
if (colCount < 0)
colCount = rows.get(0).size();
return getConstantTable(rows, null, Collections.<String> nCopies(colCount, null));
}
 
private final void getRow(StringBuilder sb, List<String> row, final int requiredColCount, List<String> columnsAlias) {
// should be OK since requiredColCount is computed from columnsAlias in getConstantTable()
assert columnsAlias == null || requiredColCount == columnsAlias.size();
403,7 → 471,7
throw new IllegalArgumentException("Wrong number of columns, should be " + requiredColCount + " but row is " + row);
for (int i = 0; i < actualColCount; i++) {
sb.append(row.get(i));
if (columnsAlias != null) {
if (columnsAlias != null && columnsAlias.get(i) != null) {
sb.append(" as ");
sb.append(SQLBase.quoteIdentifier(columnsAlias.get(i)));
}
421,7 → 489,9
if (colCount < 1)
throw new IllegalArgumentException("Empty columns will cause a syntax error");
final StringBuilder sb = new StringBuilder(rows.size() * 64);
sb.append("( SELECT ");
if (alias != null)
sb.append("( ");
sb.append("SELECT ");
// aliases needed only for the first row
getRow(sb, rows.get(0), colCount, columnsAlias);
for (int i = 1; i < rowCount; i++) {
428,8 → 498,10
sb.append("\nUNION ALL\nSELECT ");
getRow(sb, rows.get(i), colCount, null);
}
sb.append(" ) as ");
sb.append(SQLBase.quoteIdentifier(alias));
if (alias != null) {
sb.append(" ) as ");
sb.append(SQLBase.quoteIdentifier(alias));
}
return sb.toString();
}
 <