OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Compare Revisions

Regard whitespace Rev 21 → Rev 25

/trunk/OpenConcerto/Configuration/Template/Default/VenteFacture.xml
95,8 → 95,8
<element location="B63" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE" name="ID_MODE_REGLEMENT">
 
<field base="Societe" table="MODE_REGLEMENT" name="NOM" prefix="Règlement souhaité" conditionField="COMPTANT" conditionExpValue="false" display="false"/>
<field base="Societe" table="MODE_REGLEMENT" name="NOM" prefix="Facture acquitée par" conditionField="COMPTANT" conditionExpValue="true" display="false"/>
<field base="Societe" table="MODE_REGLEMENT" name="NOM" prefix="Règlement souhaité" conditionField="COMPTANT" conditionExpValue="true" display="false"/>
<field base="Societe" table="MODE_REGLEMENT" name="NOM" prefix="Facture acquitée par" conditionField="COMPTANT" conditionExpValue="false" display="false"/>
 
<field base="Societe" table="MODE_REGLEMENT" name="ID_TYPE_REGLEMENT">
<field base="Societe" table="TYPE_REGLEMENT" name="NOM" valuesExpected="Indéfini"/>
112,6 → 112,10
 
<element location="B64" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE" name="DATE" type="DateEcheance" prefix="Règlement de cette facture au plus tard le " valuesExpected=" "/>
</element>
 
<element location="B64" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE" name="ID_MODE_REGLEMENT">
 
<field base="Societe" table="MODE_REGLEMENT" name="NOM" prefix="Règlement à date de réception de facture" conditionField="COMPTANT" conditionExpValue="false" display="false"/>
118,11 → 122,7
</field>
</element>
 
<element location="B64" type="fill">
<field base="Societe" table="SAISIE_VENTE_FACTURE" name="DATE" type="DateEcheance" prefix="Règlement de cette facture au plus tard le " valuesExpected=" "/>
</element>
 
<table endPageLine="65" firstLine="63" endLine="65" lastColumn="I" base="Societe" table="TVA">
<element location="I" name="NOM" prefix="Total ">
</element>
/trunk/OpenConcerto/src/org/openconcerto/map/ui/ITextComboVilleViewer.java
14,9 → 14,9
package org.openconcerto.map.ui;
 
import org.openconcerto.map.model.Ville;
import org.openconcerto.ui.PopupMouseListener;
import org.openconcerto.ui.component.ComboLockedMode;
import org.openconcerto.ui.component.ITextSelector;
import org.openconcerto.ui.component.IComboCacheListModel;
import org.openconcerto.ui.component.combo.ISearchableTextCombo;
import org.openconcerto.ui.component.text.DocumentComponent;
import org.openconcerto.ui.component.text.TextComponent;
import org.openconcerto.ui.state.WindowStateManager;
27,6 → 27,7
import org.openconcerto.utils.checks.EmptyObjectHelper;
import org.openconcerto.utils.checks.ValidListener;
import org.openconcerto.utils.checks.ValidState;
import org.openconcerto.utils.text.SimpleDocumentListener;
 
import java.awt.BorderLayout;
import java.awt.Dimension;
39,12 → 40,9
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.WindowConstants;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
 
55,7 → 53,7
* Selecteur de Ville
*/
private static final long serialVersionUID = 3397210337907076649L;
private final ITextSelector text = new ITextSelector(ComboLockedMode.ITEMS_LOCKED);
private final ISearchableTextCombo text = new ISearchableTextCombo(ComboLockedMode.UNLOCKED);
private final JButton button = new JButton("Afficher sur la carte");
private Ville currentVille = null;
private final EmptyObjectHelper emptyHelper;
81,7 → 79,7
});
 
this.cache = new ITextComboCacheVille();
this.text.initCache(this.cache);
new IComboCacheListModel(this.cache).initCacheLater(this.text);
this.add(this.text, BorderLayout.CENTER);
 
this.add(this.button, BorderLayout.EAST);
108,44 → 106,16
}
}
});
this.text.getDocument().addDocumentListener(new DocumentListener() {
 
public void changedUpdate(final DocumentEvent e) {
ITextComboVilleViewer.this.currentVille = Ville.getVilleFromVilleEtCode(ITextComboVilleViewer.this.text.getValue());
this.text.getDocument().addDocumentListener(new SimpleDocumentListener() {
@Override
public void update(DocumentEvent e) {
ITextComboVilleViewer.this.currentVille = Ville.getVilleFromVilleEtCode(getText(e.getDocument()));
ITextComboVilleViewer.this.button.setEnabled(ITextComboVilleViewer.this.currentVille != null && ITextComboVilleViewer.this.isEnabled());
 
}
 
public void insertUpdate(final DocumentEvent e) {
this.changedUpdate(e);
}
 
public void removeUpdate(final DocumentEvent e) {
this.changedUpdate(e);
}
});
final JPopupMenu popupMenu = new JPopupMenu();
// FIXME ajouter la possibilité de supprimer une ville précédemment enregistrée
final JMenuItem menuItem = new JMenuItem("Enregistrer cette ville");
menuItem.addActionListener(new ActionListener() {
 
public void actionPerformed(final ActionEvent e) {
final String t = ITextComboVilleViewer.this.text.getTextComp().getText();
ITextComboVilleViewer.this.cache.addToCache(t);
final Ville createVilleFrom = ITextComboVilleViewer.this.cache.createVilleFrom(t);
if (createVilleFrom != null) {
final String villeEtCode = createVilleFrom.getVilleEtCode();
ITextComboVilleViewer.this.setValue(villeEtCode);
ITextComboVilleViewer.this.firePropertyChange("value", null, villeEtCode);
}
}
});
popupMenu.add(menuItem);
 
this.text.getTextComp().addMouseListener(new PopupMouseListener(popupMenu));
 
}
 
@Override
public void addEmptyListener(final EmptyListener l) {
this.emptyHelper.addListener(l);
198,6 → 168,9
 
@Override
public ValidState getValidState() {
// TODO listen to Ville list, otherwise if we type a city that doesn't exist, the value
// change and we're invalid, then we add the city but this does not change the value of the
// combo and thus we're still invalid even though the city is now in the list
final Ville villeFromVilleEtCode = Ville.getVilleFromVilleEtCode(this.getValue());
final boolean b = villeFromVilleEtCode != null;
if (b) {
224,7 → 197,6
public void setEnabled(final boolean enabled) {
super.setEnabled(enabled);
this.text.setEnabled(enabled);
this.text.setEditable(enabled);
this.button.setEnabled(enabled);
}
 
/trunk/OpenConcerto/src/org/openconcerto/map/ui/ITextComboCacheVille.java
21,7 → 21,6
 
import javax.swing.JOptionPane;
 
 
public class ITextComboCacheVille implements ITextComboCache {
final ArrayList<String> villesNames = Ville.getVillesNames();
private Ville lastGood;
44,6 → 43,11
return v;
}
 
@Override
public boolean isValid() {
return this.villesNames.size() > 0;
}
 
public void addToCache(String string) {
Ville v = this.createVilleFrom(string);
if (v != null) {
55,6 → 59,9
}
 
public void deleteFromCache(String string) {
final Ville v = Ville.getVilleFromVilleEtCode(string);
if (v != null)
Ville.removeVille(v);
}
 
public List<String> getCache() {
64,7 → 71,8
return villesNames;
}
 
public List<String> loadCache() {
@Override
public List<String> loadCache(final boolean readCache) {
return villesNames;
}
 
/trunk/OpenConcerto/src/org/openconcerto/map/ui/StatusPanel.java
16,7 → 16,8
import org.openconcerto.map.model.Ville;
import org.openconcerto.ui.component.ComboLockedMode;
import org.openconcerto.ui.component.ITextComboCache;
import org.openconcerto.ui.component.ITextSelector;
import org.openconcerto.ui.component.combo.ISearchableTextCombo;
import org.openconcerto.utils.model.DefaultIListModel;
 
import java.awt.Dimension;
import java.awt.GridBagConstraints;
37,7 → 38,6
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
 
 
public class StatusPanel extends JPanel implements VilleRendererListener, ZoomListener {
 
/**
81,7 → 81,7
 
}
/* final JButton button = new JButton("Centrer"); */
ITextSelector txt = new ITextSelector("", ComboLockedMode.ITEMS_LOCKED, 40);
ISearchableTextCombo txt = new ISearchableTextCombo(ComboLockedMode.ITEMS_LOCKED, 1, 40);
txt.addValueListener(new PropertyChangeListener() {
 
public void propertyChange(PropertyChangeEvent evt) {
100,8 → 100,7
txt.setMinimumSearch(0);
txt.setMaximumResult(200);
 
ITextComboCache cache = new ITextComboCacheVille();
txt.initCache(cache);
txt.initCache(new DefaultIListModel<String>(new ITextComboCacheVille().getCache()));
c.weightx = 1;
c.gridx++;
this.add(txt, c);
/trunk/OpenConcerto/src/org/openconcerto/map/model/DatabaseAccessor.java
18,6 → 18,7
public interface DatabaseAccessor {
 
public void store(Ville v);
public void delete(Ville v);
 
public List<Ville> read();
}
/trunk/OpenConcerto/src/org/openconcerto/map/model/Ville.java
32,7 → 32,6
import javax.swing.JFrame;
import javax.swing.UIManager;
 
 
public class Ville {
 
private static Map<String, Ville> map = new HashMap<String, Ville>();
130,6 → 129,16
// FIXME: fire missing
}
 
public static synchronized void removeVille(final Ville v) {
villes.remove(v);
final String villeEtCode = v.getVilleEtCode();
villesNames.remove(villeEtCode);
map.remove(villeEtCode);
 
accessor.delete(v);
// FIXME: fire missing
}
 
// ** getter
 
private static final synchronized void await() {
/trunk/OpenConcerto/src/org/openconcerto/openoffice/StyleDesc.java
186,18 → 186,74
}
 
/**
* Resolve the passed style name.
* Resolve the passed style name. Note: this always return the style named <code>name</code>,
* possibly ignoring conditions.
*
* @param pkg the package of the searched for style.
* @param doc the document of the searched for style.
* @param name the name of the style.
* @return a corresponding StyleStyle.
* @return the corresponding style, <code>null</code> if not found.
* @see #findStyleForNode(StyledNode, String)
*/
public final S findStyle(final ODPackage pkg, final Document doc, final String name) {
final Element styleElem = pkg.getStyle(doc, this, name);
return styleElem == null ? null : this.create(pkg, styleElem);
public final S findStyleWithName(final ODPackage pkg, final Document doc, final String name) {
return this.findStyle(pkg, doc, name, null);
}
 
/**
* Find the style for the passed node. Depending on conditions the returned style might not be
* named <code>name</code>.
*
* @param styledNode needed to evaluate conditions, not <code>null</code>.
* @param name the name of the style.
* @return the corresponding style, <code>null</code> if not found.
* @see #findStyleWithName(ODPackage, Document, String)
*/
public final S findStyleForNode(final StyledNode<S, ?> styledNode, final String name) {
return this.findStyleForNode(styledNode.getODDocument().getPackage(), styledNode.getElement().getDocument(), styledNode, name);
}
 
public final S findStyleForNode(final ODPackage pkg, final Document doc, final StyledNode<S, ?> styledNode, final String name) {
if (styledNode == null)
throw new NullPointerException("null node");
return this.findStyle(pkg, doc, name, styledNode);
}
 
/**
* Resolve the passed style name. If <code>styledNode</code> is <code>null</code> the returned
* style will be the one named <code>name</code> otherwise depending on conditions it can be
* another one.
*
* @param pkg the package of the searched for style.
* @param doc the document of the searched for style.
* @param name the name of the style.
* @param styledNode needed to evaluate conditions, can be <code>null</code>.
* @return the corresponding style, <code>null</code> if not found.
*/
private final S findStyle(final ODPackage pkg, final Document doc, final String name, final StyledNode<S, ?> styledNode) {
Element styleElem = pkg.getStyle(doc, this, name);
if (styleElem == null)
return null;
if (styledNode != null && supportConditions()) {
@SuppressWarnings("unchecked")
final List<Element> styleMaps = styleElem.getChildren("map", getVersion().getSTYLE());
final Element styleMap = evaluateConditions(styledNode, styleMaps);
if (styleMap != null) {
if (styleElem != styleMap.getParent())
throw new IllegalStateException("map element not in " + styleElem);
styleElem = pkg.getStyle(doc, this, styleMap.getAttributeValue("apply-style-name", getVersion().getSTYLE()));
}
}
return this.create(pkg, styleElem);
}
 
protected boolean supportConditions() {
return false;
}
 
protected Element evaluateConditions(final StyledNode<S, ?> styledNode, final List<Element> styleMaps) {
return null;
}
 
public final S createAutoStyle(final ODPackage pkg) {
return this.createAutoStyle(pkg, getBaseName());
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/ImmutableDocStyledNode.java
42,7 → 42,11
* @param styleClass our class of style, cannot be <code>null</code>.
*/
public ImmutableDocStyledNode(D parent, Element local, final Class<S> styleClass) {
super(local, styleClass);
this(parent, local, getStyleDesc(local, styleClass));
}
 
protected ImmutableDocStyledNode(D parent, Element local, StyleDesc<S> styleDesc) {
super(local, styleDesc);
this.parent = parent;
assert getDocuments(this.parent.getPackage()).contains(local.getDocument()) : "Local not in parent: " + parent;
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/Style.java
105,6 → 105,7
if (elemName2Desc.get(desc.getVersion()).put(desc.getElementName(), desc) != null)
throw new IllegalStateException(desc.getElementName() + " duplicate element name");
}
assert desc != null : "Will need containsKey() in getStyleDesc()";
if (class2Desc.get(desc.getVersion()).put(desc.getStyleClass(), desc) != null)
throw new IllegalStateException(desc.getStyleClass() + " duplicate");
}
184,7 → 185,7
 
public static <S extends Style> S getStyle(final ODPackage pkg, final Class<S> clazz, final String name) {
final StyleDesc<S> styleDesc = getStyleDesc(clazz, pkg.getVersion());
return styleDesc.create(pkg, pkg.getStyle(styleDesc, name));
return styleDesc.findStyleWithName(pkg, pkg.getContent().getDocument(), name);
}
 
/**
227,16 → 228,14
return (StyleStyleDesc<S>) getStyleDesc(clazz, version);
}
 
@SuppressWarnings("unchecked")
private static <S extends Style> StyleDesc<S> getStyleDesc(Class<S> clazz, final XMLVersion version, final boolean mustExist) {
loadDescs();
final Map<Class<? extends Style>, StyleDesc<?>> map = class2Desc.get(version);
if (map.containsKey(clazz))
return (StyleDesc<S>) map.get(clazz);
else if (mustExist)
@SuppressWarnings("unchecked")
final StyleDesc<S> res = (StyleDesc<S>) map.get(clazz);
if (res == null && mustExist)
throw new IllegalArgumentException("unregistered " + clazz + " for version " + version);
else
return null;
return res;
}
 
protected static <S extends Style> StyleDesc<S> getNonNullStyleDesc(final Class<S> clazz, final XMLVersion version, final Element styleElem, final String styleName) {
/trunk/OpenConcerto/src/org/openconcerto/openoffice/ODDocument.java
13,15 → 13,84
package org.openconcerto.openoffice;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.text.ParseException;
 
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
 
/**
* An ODF document, like a spreadsheet or a text file.
*
* @author Sylvain
*/
public interface ODDocument {
public XMLVersion getVersion();
public abstract class ODDocument {
 
public XMLFormatVersion getFormatVersion();
private final ODPackage pkg;
private ODEpoch epoch;
 
public ODPackage getPackage();
protected ODDocument(final ODPackage orig) {
// don't want multiple document per package.
if (orig.hasODDocument())
throw new IllegalStateException("ODPackage already has an ODDocument");
this.pkg = orig;
}
 
public final XMLVersion getVersion() {
return this.getFormatVersion().getXMLVersion();
}
 
public final XMLFormatVersion getFormatVersion() {
return this.getPackage().getFormatVersion();
}
 
public final ODPackage getPackage() {
return this.pkg;
}
 
public final Document getContentDocument() {
return this.getPackage().getContent().getDocument();
}
 
protected final Element getBody() {
return getPackage().getContentType().getBody(this.getContentDocument());
}
 
private final String findEpoch() throws ParseException {
final Namespace tableNS = getVersion().getTABLE();
final Element settings = this.getBody().getChild("calculation-settings", tableNS);
if (settings != null) {
final Element nullDateElem = settings.getChild("null-date", tableNS);
if (nullDateElem != null)
return nullDateElem.getAttributeValue("date-value", tableNS);
}
return null;
}
 
public final ODEpoch getEpoch() {
return this.getEpoch(false);
}
 
public final ODEpoch getEpoch(final boolean updateFromXML) {
if (this.epoch == null || updateFromXML) {
try {
this.epoch = ODEpoch.getInstance(this.findEpoch());
} catch (ParseException e) {
// quite rare
throw new IllegalStateException("Unable to parse the epoch of " + this, e);
}
}
assert this.epoch != null;
return this.epoch;
}
 
// *** Files
 
public File saveAs(File file) throws FileNotFoundException, IOException {
this.getPackage().setFile(file);
return this.getPackage().save();
}
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/ODValueType.java
14,6 → 14,7
package org.openconcerto.openoffice;
 
import org.openconcerto.utils.FormatGroup;
import org.openconcerto.utils.TimeUtils;
import org.openconcerto.utils.XMLDateFormat;
 
import java.math.BigDecimal;
25,8 → 26,6
import java.util.Date;
import java.util.List;
 
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
 
/**
109,12 → 108,7
return o.toString();
} else {
final Calendar cal = (Calendar) o;
// adjust the format TZ to the calendar's
// that way even you pass a non default Calendar, if you did
// myCal.set(HOUR_OF_DAY, 22), the string will have "22H"
final SimpleDateFormat fmt = (SimpleDateFormat) TIME_FORMAT.clone();
fmt.setTimeZone(cal.getTimeZone());
return fmt.format(cal.getTime());
return TimeUtils.timePartToDuration(cal).toString();
}
}
 
123,7 → 117,7
if (date.length() == 0)
return null;
else {
return getTypeFactory().newDuration(date);
return TimeUtils.getTypeFactory().newDuration(date);
}
}
 
206,8 → 200,11
*
* @param o the object.
* @return a value type capable of formatting <code>o</code> or <code>null</code>.
* @throws NullPointerException if <code>o</code> is <code>null</code>.
*/
public static ODValueType forObject(Object o) {
public static ODValueType forObject(Object o) throws NullPointerException {
if (o == null)
throw new NullPointerException();
if (o instanceof Number)
return FLOAT;
else if (o instanceof Boolean)
214,7 → 211,7
return BOOLEAN;
else if (o instanceof String)
return STRING;
else if (o instanceof Duration || o instanceof Calendar && !((Calendar) o).isSet(Calendar.DATE))
else if (o instanceof Duration)
return TIME;
else if (DATE.canFormat(o.getClass()))
return DATE;
224,8 → 221,6
 
// see http://www.w3.org/TR/2004/REC-xmlschema-2-20041028/#isoformats
 
// time means Duration for OpenDocument (see 6.7.1)
static private final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("'PT'HH'H'mm'M'ss.S'S'");
static private final Format DATE_FORMAT;
static {
// first date and time so we don't loose time information on format() or parse()
232,16 → 227,4
// MAYBE add HH':'mm':'ss,SSS for OOo 1
DATE_FORMAT = new FormatGroup(new XMLDateFormat(), new SimpleDateFormat("yyyy-MM-dd'T'HH':'mm':'ss"), new SimpleDateFormat("yyyy-MM-dd"));
}
 
static private DatatypeFactory typeFactory = null;
 
static public final DatatypeFactory getTypeFactory() {
if (typeFactory == null)
try {
typeFactory = DatatypeFactory.newInstance();
} catch (DatatypeConfigurationException e) {
throw new IllegalStateException(e);
}
return typeFactory;
}
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/text/TextNode.java
13,7 → 13,7
package org.openconcerto.openoffice.text;
 
import org.openconcerto.openoffice.ODSingleXMLDocument;
import org.openconcerto.openoffice.ODDocument;
import org.openconcerto.openoffice.StyleStyle;
import org.openconcerto.openoffice.StyledNode;
 
26,28 → 26,32
*
* @param <S> type of style.
*/
public abstract class TextNode<S extends StyleStyle> extends StyledNode<S, ODSingleXMLDocument> {
public abstract class TextNode<S extends StyleStyle> extends StyledNode<S, TextDocument> {
 
protected ODSingleXMLDocument parent;
protected TextDocument parent;
 
public TextNode(Element local, final Class<S> styleClass) {
this(local, styleClass, null);
}
 
protected TextNode(Element local, final Class<S> styleClass, final TextDocument parent) {
super(local, styleClass);
this.parent = null;
this.parent = parent;
}
 
@Override
public final ODSingleXMLDocument getODDocument() {
public final TextDocument getODDocument() {
return this.parent;
}
 
public final void setDocument(ODSingleXMLDocument doc) {
public final void setDocument(TextDocument doc) {
if (doc != this.parent) {
if (doc == null) {
this.parent = null;
this.getElement().detach();
} else if (doc.getDocument() != this.getElement().getDocument())
} else if (doc.getContentDocument() != this.getElement().getDocument()) {
doc.add(this);
else {
} else {
this.checkDocument(doc);
this.parent = doc;
}
54,5 → 58,5
}
}
 
protected abstract void checkDocument(ODSingleXMLDocument doc);
protected abstract void checkDocument(ODDocument doc);
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/text/TextDocument.java
New file
0,0 → 1,92
/*
* 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.openoffice.text;
 
import org.openconcerto.openoffice.ContentType;
import org.openconcerto.openoffice.ContentTypeVersioned;
import org.openconcerto.openoffice.ODDocument;
import org.openconcerto.openoffice.ODPackage;
import org.openconcerto.openoffice.XMLFormatVersion;
 
import java.io.File;
import java.io.IOException;
 
import org.jdom.Element;
 
public class TextDocument extends ODDocument {
 
public static TextDocument createFromFile(File f) throws IOException {
return new ODPackage(f).getTextDocument();
}
 
/**
* This method should be avoided, use {@link ODPackage#getTextDocument()}.
*
* @param fd a package.
* @return the text document.
*/
public static TextDocument get(final ODPackage fd) {
return fd.hasODDocument() ? fd.getTextDocument() : new TextDocument(fd);
}
 
public static TextDocument createEmpty(String s) throws IOException {
return createEmpty(s, XMLFormatVersion.getDefault());
}
 
public static TextDocument createEmpty(String s, XMLFormatVersion ns) throws IOException {
final ContentTypeVersioned ct = ContentType.TEXT.getVersioned(ns.getXMLVersion());
final TextDocument res = ct.createPackage(ns).getTextDocument();
final Element textP = Paragraph.createEmpty(ns.getXMLVersion());
textP.addContent(s);
res.getBody().addContent(textP);
return res;
}
 
private TextDocument(final ODPackage orig) {
super(orig);
}
 
public final Paragraph getParagraph(int i) {
final Element proto = Paragraph.createEmpty(getVersion());
final Element child = (Element) this.getBody().getChildren(proto.getName(), proto.getNamespace()).get(i);
return new Paragraph(child, this);
}
 
/**
* Append a paragraph or a heading.
*
* @param p paragraph to add.
*/
public synchronized void add(TextNode<?> p) {
this.add(p, null, -1);
}
 
public synchronized void add(TextNode<?> p, Element where, int index) {
// add it first to avoid infinite loop, since setDocument() can call this method
final Element addToElem = where == null ? this.getBody() : where;
if (index < 0)
addToElem.addContent(p.getElement());
else
addToElem.addContent(index, p.getElement());
 
try {
p.setDocument(this);
} catch (RuntimeException e) {
// the paragraph can throw an exception to notify that is not compatible with us (eg
// missing styles), in that case remove it
p.getElement().detach();
throw e;
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/text/Paragraph.java
13,8 → 13,8
package org.openconcerto.openoffice.text;
 
import org.openconcerto.openoffice.ODDocument;
import org.openconcerto.openoffice.XMLVersion;
import org.openconcerto.openoffice.ODSingleXMLDocument;
 
import java.util.HashSet;
import java.util.Set;
55,8 → 55,12
return res;
}
 
Paragraph(Element elem, TextDocument parent) {
super(elem, ParagraphStyle.class, parent);
}
 
public Paragraph(Element elem) {
super(elem, ParagraphStyle.class);
this(elem, null);
}
 
public Paragraph(XMLVersion ns) {
72,6 → 76,21
addContent(text);
}
 
// MAYBE add updateStyle() which evaluates the conditions in style:map of the conditional style
// to update style-name
/**
* A style containing conditions and maps to other styles.
*
* @return the conditional style or <code>null</code> if none or if this isn't in a document.
*/
public final ParagraphStyle getConditionalStyle() {
final String condName = this.getElement().getAttributeValue("cond-style-name", this.getElement().getNamespace());
if (condName == null)
return null;
else
return getStyle(condName);
}
 
public final void setStyle(String styleName) {
getElement().setAttribute("style-name", styleName, getElement().getNamespace());
}
99,11 → 118,12
return getTextStyles(getElement());
}
 
protected void checkDocument(ODSingleXMLDocument doc) {
if (this.getStyleName() != null && getStyle(doc.getPackage(), doc.getDocument()) == null)
@Override
protected void checkDocument(ODDocument doc) {
if (this.getStyleName() != null && getStyle(doc.getPackage(), doc.getContentDocument()) == null)
throw new IllegalArgumentException("unknown style " + getStyleName() + " in " + doc);
for (final String styleName : this.getUsedTextStyles()) {
if (doc.getStyle(TextStyle.DESC, styleName) == null) {
if (doc.getPackage().getStyle(TextStyle.DESC, styleName) == null) {
throw new IllegalArgumentException(this + " is using a text:span with an undefined style : " + styleName);
}
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/StyledNode.java
26,6 → 26,10
*/
public abstract class StyledNode<S extends Style, D extends ODDocument> extends ODNode {
 
static protected final <S extends Style> StyleDesc<S> getStyleDesc(final Element local, final Class<S> styleClass) {
return Style.getStyleDesc(styleClass, XMLVersion.getVersion(local));
}
 
private final StyleDesc<S> styleClass;
 
/**
36,10 → 40,18
* @param styleClass our class of style, cannot be <code>null</code>.
*/
public StyledNode(Element local, final Class<S> styleClass) {
this(local, getStyleDesc(local, styleClass));
}
 
// allow to pass StyleDesc since Style.getStyleDesc() was the longest operation of this
// constructor, and this constructor is called for every Table, Column, Row and Cell, i.e.
// up to millions of times.
protected StyledNode(Element local, final StyleDesc<S> styleDesc) {
super(local);
if (styleClass == null)
throw new NullPointerException("null style class");
this.styleClass = Style.getStyleDesc(styleClass, XMLVersion.getVersion(getElement()));
if (styleDesc == null)
throw new NullPointerException("null style desc");
this.styleClass = styleDesc;
assert styleDesc.getVersion().equals(XMLVersion.getVersion(local));
assert this.styleClass.getRefElements().contains(this.getElement().getQualifiedName()) : this.getElement().getQualifiedName() + " not in " + this.styleClass;
}
 
47,14 → 59,23
public abstract D getODDocument();
 
public final S getStyle() {
// null avoid getting styleName if we haven't any Document
return this.getStyle(null);
}
 
protected final S getStyle(final String styleName) {
final D doc = this.getODDocument();
return doc == null ? null : this.getStyle(doc.getPackage(), getElement().getDocument());
return doc == null ? null : this.getStyle(doc.getPackage(), getElement().getDocument(), styleName == null ? getStyleName() : styleName);
}
 
protected final S getStyle(final ODPackage pkg, final Document doc) {
return this.styleClass.findStyle(pkg, doc, getStyleName());
return this.getStyle(pkg, doc, getStyleName());
}
 
protected final S getStyle(final ODPackage pkg, final Document doc, final String styleName) {
return this.styleClass.findStyleForNode(pkg, doc, this, styleName);
}
 
/**
* Assure that this node's style is only referenced by this. I.e. after this method returns the
* style of this node can be safely modified without affecting other nodes.
/trunk/OpenConcerto/src/org/openconcerto/openoffice/ODPackage.java
15,15 → 15,21
 
import static org.openconcerto.openoffice.ODPackage.RootElement.CONTENT;
import static org.openconcerto.openoffice.ODPackage.RootElement.META;
import static org.openconcerto.openoffice.ODPackage.RootElement.SETTINGS;
import static org.openconcerto.openoffice.ODPackage.RootElement.STYLES;
import org.openconcerto.openoffice.spreadsheet.SpreadSheet;
import org.openconcerto.openoffice.text.ParagraphStyle;
import org.openconcerto.openoffice.text.TextDocument;
import org.openconcerto.utils.CollectionMap;
import org.openconcerto.utils.CopyUtils;
import org.openconcerto.utils.ExceptionUtils;
import org.openconcerto.utils.FileUtils;
import org.openconcerto.utils.StreamUtils;
import org.openconcerto.utils.StringInputStream;
import org.openconcerto.utils.Tuple3;
import org.openconcerto.utils.Zip;
import org.openconcerto.utils.ZippedFilesProcessor;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.xml.JDOMUtils;
import org.openconcerto.xml.Validator;
 
import java.io.BufferedInputStream;
37,14 → 43,19
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.zip.ZipEntry;
 
import org.jdom.Attribute;
import org.jdom.DocType;
import org.jdom.Document;
import org.jdom.Element;
62,6 → 73,7
 
// use raw format, otherwise spaces are added to every spreadsheet cell
private static final XMLOutputter OUTPUTTER = new XMLOutputter(Format.getRawFormat());
static final String MIMETYPE_ENTRY = "mimetype";
/** Normally mimetype contains only ASCII characters */
static final Charset MIMETYPE_ENC = Charset.forName("UTF-8");
 
89,6 → 101,10
return EnumSet.of(CONTENT, STYLES, META, SETTINGS);
}
 
public final static RootElement fromDocument(final Document doc) {
return fromElementName(doc.getRootElement().getName());
}
 
public final static RootElement fromElementName(final String name) {
for (final RootElement e : values()) {
if (e.getElementName().equals(name))
98,8 → 114,7
}
 
static final Document createSingle(final Document from) {
final XMLFormatVersion version = XMLFormatVersion.get(from);
return SINGLE_CONTENT.createDocument(version.getXMLVersion(), version.getOfficeVersion());
return SINGLE_CONTENT.createDocument(XMLFormatVersion.get(from));
}
 
private final String nsPrefix;
120,20 → 135,25
return this.name;
}
 
public final Document createDocument(final XMLVersion version, final String officeVersion) {
public final Document createDocument(final XMLFormatVersion fv) {
final XMLVersion version = fv.getXMLVersion();
final Element root = new Element(getElementName(), version.getNS(getElementNSPrefix()));
// 19.388 office:version identifies the version of ODF specification
if (officeVersion != null)
root.setAttribute("version", officeVersion, version.getOFFICE());
if (fv.getOfficeVersion() != null)
root.setAttribute("version", fv.getOfficeVersion(), version.getOFFICE());
// avoid declaring namespaces in each child
for (final Namespace ns : version.getALL())
root.addNamespaceDeclaration(ns);
 
final Document res = new Document(root);
return new Document(root, createDocType(version));
}
 
public final DocType createDocType(final XMLVersion version) {
// OpenDocument use relaxNG
if (version == XMLVersion.OOo)
res.setDocType(new DocType(getElementNSPrefix() + ":" + getElementName(), "-//OpenOffice.org//DTD OfficeDocument 1.0//EN", "office.dtd"));
return res;
return new DocType(getElementNSPrefix() + ":" + getElementName(), "-//OpenOffice.org//DTD OfficeDocument 1.0//EN", "office.dtd");
else
return null;
}
 
/**
162,17 → 182,69
* @return <code>true</code> if <code>name</code> is a standard file, eg <code>true</code>.
*/
public static final boolean isStandardFile(final String name) {
return name.equals("mimetype") || subdocNames.contains(name) || name.startsWith("Thumbnails") || name.startsWith("META-INF") || name.startsWith("Configurations");
return name.equals(MIMETYPE_ENTRY) || subdocNames.contains(name) || name.startsWith("Thumbnails") || name.startsWith("META-INF") || name.startsWith("Configurations");
}
 
/**
* Create a package from a collection of sub-documents.
*
* @param content the content.
* @param style the styles, can be <code>null</code>.
* @return a package containing the XML documents.
*/
public static ODPackage createFromDocuments(Document content, Document style) {
return createFromDocuments(null, content, style, null, null);
}
 
public static ODPackage createFromDocuments(final ContentTypeVersioned type, Document content, Document style, Document meta, Document settings) {
final ODPackage pkg = new ODPackage();
if (type != null)
pkg.setContentType(type);
pkg.putFile(RootElement.CONTENT.getZipEntry(), content);
pkg.putFile(RootElement.STYLES.getZipEntry(), style);
pkg.putFile(RootElement.META.getZipEntry(), meta);
pkg.putFile(RootElement.SETTINGS.getZipEntry(), settings);
return pkg;
}
 
static private XMLVersion getVersion(final XMLFormatVersion fv, final ContentTypeVersioned ct) {
final XMLVersion v;
if (ct == null && fv == null)
v = null;
else if (ct != null)
v = ct.getVersion();
else
v = fv.getXMLVersion();
assert fv == null || ct == null || fv.getXMLVersion() == ct.getVersion();
return v;
}
 
static private <T> void checkVersion(final Class<T> clazz, final String s, final T actual, final T required) {
if (actual != null && required != null) {
final boolean ok;
if (actual instanceof ContentTypeVersioned) {
// we can change our template status since it doesn't affect our content
ok = ((ContentTypeVersioned) actual).getNonTemplate().equals(((ContentTypeVersioned) required).getNonTemplate());
} else {
ok = actual.equals(required);
}
if (!ok)
throw new IllegalArgumentException("Cannot change " + s + " from " + required + " to " + actual);
}
}
 
private final Map<String, ODPackageEntry> files;
private ContentTypeVersioned type;
private XMLFormatVersion version;
private File file;
private ODDocument doc;
 
public ODPackage() {
this.files = new HashMap<String, ODPackageEntry>();
this.type = null;
this.version = null;
this.file = null;
this.doc = null;
}
 
public ODPackage(InputStream ins) throws IOException {
186,7 → 258,7
final Object res;
if (subdocNames.contains(name)) {
try {
res = new ODXMLDocument(OOUtils.getBuilder().build(in));
res = OOUtils.getBuilder().build(in);
} catch (JDOMException e) {
// always correct
throw new IllegalStateException("parse error", e);
244,7 → 316,9
this.putFile(name, myData, entry.getType(), entry.isCompressed());
}
this.type = o.type;
this.version = o.version;
this.file = o.file;
this.doc = null;
}
 
public final File getFile() {
269,16 → 343,11
* @return the version of this package, can be <code>null</code>.
*/
public final XMLVersion getVersion() {
final XMLFormatVersion res = getFormatVersion();
return res == null ? null : res.getXMLVersion();
return getVersion(this.version, this.type);
}
 
public final XMLFormatVersion getFormatVersion() {
final ODXMLDocument content = this.getContent();
if (content == null)
return null;
else
return content.getFormatVersion();
return this.version;
}
 
/**
287,25 → 356,90
* @return the type of this package, can be <code>null</code>.
*/
public final ContentTypeVersioned getContentType() {
if (this.type == null) {
if (this.files.containsKey("mimetype"))
this.type = ContentTypeVersioned.fromMime(new String(this.getBinaryFile("mimetype"), MIMETYPE_ENC));
else if (this.getVersion().equals(XMLVersion.OOo)) {
final Element contentRoot = this.getContent().getDocument().getRootElement();
final String docClass = contentRoot.getAttributeValue("class", contentRoot.getNamespace("office"));
this.type = ContentTypeVersioned.fromClass(docClass);
} else if (this.getVersion().equals(XMLVersion.OD)) {
final Element bodyChild = (Element) this.getContent().getChild("body").getChildren().get(0);
this.type = ContentTypeVersioned.fromBody(bodyChild.getName());
return this.type;
}
 
public final void setContentType(final ContentTypeVersioned newType) {
this.putFile(MIMETYPE_ENTRY, newType.getMimeType().getBytes(MIMETYPE_ENC));
}
return this.type;
 
private void updateTypeAndVersion(final String entry, ODXMLDocument xml) {
this.setTypeAndVersion(entry.equals(CONTENT.getZipEntry()) ? ContentTypeVersioned.fromContent(xml) : null, xml.getFormatVersion(), entry);
}
 
private void updateTypeAndVersion(byte[] mimetype) {
this.setTypeAndVersion(ContentTypeVersioned.fromMime(mimetype), null, MIMETYPE_ENTRY);
}
 
private final void setTypeAndVersion(final ContentTypeVersioned ct, final XMLFormatVersion fv, final String entry) {
final Tuple3<XMLVersion, ContentTypeVersioned, XMLFormatVersion> requiredByPkg = this.getRequired(entry);
if (requiredByPkg != null) {
checkVersion(XMLVersion.class, "version", getVersion(fv, ct), requiredByPkg.get0());
checkVersion(ContentTypeVersioned.class, "type", ct, requiredByPkg.get1());
checkVersion(XMLFormatVersion.class, "format version", fv, requiredByPkg.get2());
}
 
// since we're adding "entry" never set attributes to null
if (fv != null && !fv.equals(this.version))
this.version = fv;
// don't let non-template from content overwrite the correct one
if (ct != null && !ct.equals(this.type) && (this.type == null || entry.equals(MIMETYPE_ENTRY)))
this.type = ct;
}
 
// find the versions required by the package without the passed entry
private final Tuple3<XMLVersion, ContentTypeVersioned, XMLFormatVersion> getRequired(final String entryToIgnore) {
if (this.files.size() == 0 || (this.files.size() == 1 && this.files.containsKey(entryToIgnore)))
return null;
 
final byte[] mimetype;
if (this.files.containsKey(MIMETYPE_ENTRY) && !MIMETYPE_ENTRY.equals(entryToIgnore)) {
mimetype = this.getBinaryFile(MIMETYPE_ENTRY);
} else {
mimetype = null;
}
XMLFormatVersion fv = null;
final Map<String, Object> versionFiles = new HashMap<String, Object>();
for (final String e : subdocNames) {
if (this.files.containsKey(e) && !e.equals(entryToIgnore)) {
final ODXMLDocument xmlFile = this.getXMLFile(e);
versionFiles.put(e, xmlFile);
if (fv == null)
fv = xmlFile.getFormatVersion();
else
assert fv.equals(xmlFile.getFormatVersion()) : "Incoherence";
}
}
final ODXMLDocument content = (ODXMLDocument) versionFiles.get(CONTENT.getZipEntry());
 
final ContentTypeVersioned ct;
if (mimetype != null)
ct = ContentTypeVersioned.fromMime(mimetype);
else if (content != null)
ct = ContentTypeVersioned.fromContent(content);
else
ct = null;
 
return Tuple3.create(getVersion(fv, ct), ct, fv);
}
 
public final String getMimeType() {
return this.getContentType().getMimeType();
}
 
public final boolean isTemplate() {
return this.getContentType().isTemplate();
}
 
public final void setTemplate(boolean b) {
if (this.type == null)
throw new IllegalStateException("No type");
final ContentTypeVersioned newType = b ? this.type.getTemplate() : this.type.getNonTemplate();
if (newType == null)
throw new IllegalStateException("Missing " + (b ? "" : "non-") + "template for " + this.type);
this.setContentType(newType);
}
 
/**
* Call {@link Validator#isValid()} on each XML subdocuments.
*
313,13 → 447,24
* if validation couldn't occur.
*/
public final Map<String, String> validateSubDocuments() {
return this.validateSubDocuments(true);
}
 
public final Map<String, String> validateSubDocuments(final boolean allowChangeToValidate) {
final OOXML ooxml = this.getFormatVersion().getXML();
if (!ooxml.canValidate())
return null;
final Map<String, String> res = new HashMap<String, String>();
for (final String s : subdocNames) {
if (this.getEntries().contains(s)) {
final String valid = ooxml.getValidator(this.getDocument(s)).isValid();
final Document doc = this.getDocument(s);
if (doc != null) {
if (allowChangeToValidate) {
// OpenOffice do not generate DocType declaration
final DocType docType = RootElement.fromDocument(doc).createDocType(ooxml.getVersion());
if (docType != null && doc.getDocType() == null)
doc.setDocType(docType);
}
final String valid = ooxml.getValidator(doc).isValid();
if (valid != null)
res.put(s, valid);
}
327,6 → 472,31
return res;
}
 
public final ODDocument getODDocument() {
// cache ODDocument otherwise a second one can modify the XML (e.g. remove rows) without the
// first one knowing
if (this.doc == null) {
final ContentType ct = this.getContentType().getType();
if (ct.equals(ContentType.SPREADSHEET))
this.doc = SpreadSheet.get(this);
else if (ct.equals(ContentType.TEXT))
this.doc = TextDocument.get(this);
}
return this.doc;
}
 
public final boolean hasODDocument() {
return this.doc != null;
}
 
public final SpreadSheet getSpreadSheet() {
return (SpreadSheet) this.getODDocument();
}
 
public final TextDocument getTextDocument() {
return (TextDocument) this.getODDocument();
}
 
// *** getter on files
 
public final Set<String> getEntries() {
447,6 → 617,97
return getStyles().getDefaultStyle(desc);
}
 
/**
* Verify that styles referenced by this document are indeed defined. NOTE this method is not
* perfect : not all problems are detected.
*
* @return <code>null</code> if no problem has been found, else a String describing it.
*/
public final String checkStyles() {
final ODXMLDocument stylesDoc = this.getStyles();
final ODXMLDocument contentDoc = this.getContent();
final Element styles;
if (stylesDoc != null) {
styles = stylesDoc.getChild("styles");
// check styles.xml
final String res = checkStyles(stylesDoc, styles);
if (res != null)
return res;
} else {
styles = contentDoc.getChild("styles");
}
 
// check content.xml
return checkStyles(contentDoc, styles);
}
 
static private final String checkStyles(ODXMLDocument doc, Element styles) {
try {
final CollectionMap<String, String> stylesNames = getStylesNames(doc, styles, doc.getChild("automatic-styles"));
// text:style-name : text:p, text:span
// table:style-name : table:table, table:row, table:column, table:cell
// draw:style-name : draw:text-box
// style:data-style-name : <style:style style:family="table-cell">
// TODO check by family
final Set<String> names = new HashSet<String>(stylesNames.values());
final Iterator attrs = doc.getXPath(".//@text:style-name | .//@table:style-name | .//@draw:style-name | .//@style:data-style-name | .//@style:list-style-name")
.selectNodes(doc.getDocument()).iterator();
while (attrs.hasNext()) {
final Attribute attr = (Attribute) attrs.next();
if (!names.contains(attr.getValue()))
return "unknown style referenced by " + attr.getName() + " in " + JDOMUtils.output(attr.getParent());
}
// TODO check other references like page-*-name (§3 of #prefix())
} catch (IllegalStateException e) {
return ExceptionUtils.getStackTrace(e);
} catch (JDOMException e) {
return ExceptionUtils.getStackTrace(e);
}
return null;
}
 
static private final CollectionMap<String, String> getStylesNames(final ODXMLDocument doc, final Element styles, final Element autoStyles) throws IllegalStateException {
// section 14.1 § Style Name : style:family + style:name is unique
final CollectionMap<String, String> res = new CollectionMap<String, String>(HashSet.class);
 
final List<Element> nodes = new ArrayList<Element>();
if (styles != null)
nodes.add(styles);
if (autoStyles != null)
nodes.add(autoStyles);
 
try {
{
final Iterator iter = doc.getXPath("./style:style/@style:name").selectNodes(nodes).iterator();
while (iter.hasNext()) {
final Attribute attr = (Attribute) iter.next();
final String styleName = attr.getValue();
final String family = attr.getParent().getAttributeValue("family", attr.getNamespace());
if (res.getNonNull(family).contains(styleName))
throw new IllegalStateException("duplicate style in " + family + " : " + styleName);
res.put(family, styleName);
}
}
{
final List<String> dataStyles = Arrays.asList("number-style", "currency-style", "percentage-style", "date-style", "time-style", "boolean-style", "text-style");
final String xpDataStyles = org.openconcerto.utils.CollectionUtils.join(dataStyles, " | ", new ITransformer<String, String>() {
@Override
public String transformChecked(String input) {
return "./number:" + input;
}
});
final Iterator listIter = doc.getXPath("./text:list-style | " + xpDataStyles).selectNodes(nodes).iterator();
while (listIter.hasNext()) {
final Element elem = (Element) listIter.next();
res.put(elem.getQualifiedName(), elem.getAttributeValue("name", doc.getVersion().getSTYLE()));
}
}
} catch (JDOMException e) {
throw new IllegalStateException(e);
}
return res;
}
 
// *** setter
 
public void putFile(String entry, Object data) {
460,29 → 721,54
public void putFile(final String entry, final Object data, final String mediaType, final boolean compress) {
if (entry == null)
throw new NullPointerException("null name");
if (data == null) {
this.rmFile(entry);
return;
}
final Object myData;
if (subdocNames.contains(entry)) {
final ODXMLDocument oodoc;
if (data instanceof Document)
oodoc = new ODXMLDocument((Document) data);
oodoc = ODXMLDocument.create((Document) data);
else
oodoc = (ODXMLDocument) data;
// si le package est vide n'importe quelle version convient
if (this.getVersion() != null && !oodoc.getVersion().equals(this.getVersion()))
throw new IllegalArgumentException("version mismatch " + this.getVersion() + " != " + oodoc);
checkEntryForDocument(entry);
this.updateTypeAndVersion(entry, oodoc);
myData = oodoc;
} else if (data != null && !(data instanceof byte[]))
} else if (!(data instanceof byte[])) {
throw new IllegalArgumentException("should be byte[] for " + entry + ": " + data);
else
} else {
if (entry.equals(MIMETYPE_ENTRY))
this.updateTypeAndVersion((byte[]) data);
myData = data;
}
final String inferredType = mediaType != null ? mediaType : FileUtils.findMimeType(entry);
this.files.put(entry, new ODPackageEntry(entry, inferredType, myData, compress));
}
 
// Perhaps add a clearODDocument() method to set doc to null and in ODDocument set pkg to null
// (after having verified !hasDocument()). For now just copy the package.
private void checkEntryForDocument(final String entry) {
if (this.hasODDocument() && (entry.equals(RootElement.CONTENT.getZipEntry()) || entry.equals(RootElement.STYLES.getZipEntry())))
throw new IllegalArgumentException("Cannot change content or styles with existing ODDocument");
}
 
public void rmFile(String entry) {
this.checkEntryForDocument(entry);
this.files.remove(entry);
if (entry.equals(MIMETYPE_ENTRY) || subdocNames.contains(entry)) {
final Tuple3<XMLVersion, ContentTypeVersioned, XMLFormatVersion> required = this.getRequired(entry);
this.type = required == null ? null : required.get1();
this.version = required == null ? null : required.get2();
}
}
 
public void clear() {
this.files.clear();
this.type = null;
this.version = null;
}
 
/**
* Transform this to use a {@link ODSingleXMLDocument}. Ie after this method, only "content.xml"
* remains and it's an instance of ODSingleXMLDocument.
491,13 → 777,7
*/
public ODSingleXMLDocument toSingle() {
if (!this.isSingle()) {
// this removes xml files used by OOSingleXMLDocument
final Document content = removeAndGetDoc(CONTENT.getZipEntry());
final Document styles = removeAndGetDoc(STYLES.getZipEntry());
final Document settings = removeAndGetDoc(SETTINGS.getZipEntry());
final Document meta = removeAndGetDoc(META.getZipEntry());
 
return ODSingleXMLDocument.createFromDocument(content, styles, settings, meta, this);
return ODSingleXMLDocument.create(this);
} else
return (ODSingleXMLDocument) this.getContent();
}
506,13 → 786,6
return this.getContent() instanceof ODSingleXMLDocument;
}
 
private Document removeAndGetDoc(String name) {
if (!this.files.containsKey(name))
return null;
final ODXMLDocument xmlDoc = (ODXMLDocument) this.files.remove(name).getData();
return xmlDoc == null ? null : xmlDoc.getDocument();
}
 
/**
* Split the {@link RootElement#SINGLE_CONTENT}. If this was {@link #isSingle() single} the
* former {@link #getContent() content} won't be useable anymore, you can check it with
554,12 → 827,12
final Zip z = new Zip(out);
 
// magic number, see section 17.4
z.zipNonCompressed("mimetype", this.getMimeType().getBytes(MIMETYPE_ENC));
z.zipNonCompressed(MIMETYPE_ENTRY, this.getMimeType().getBytes(MIMETYPE_ENC));
 
final Manifest manifest = new Manifest(this.getVersion(), this.getMimeType());
for (final String name : this.files.keySet()) {
// added at the end
if (name.equals("mimetype") || name.equals(Manifest.ENTRY_NAME))
if (name.equals(MIMETYPE_ENTRY) || name.equals(Manifest.ENTRY_NAME))
continue;
 
final ODPackageEntry entry = this.files.get(name);
/trunk/OpenConcerto/src/org/openconcerto/openoffice/LengthUnit.java
25,7 → 25,7
* Units of length.
*
* @author Sylvain CUAZ
* @see http://www.w3.org/TR/xsl/#d0e5752
* @see <a href="http://www.w3.org/TR/xsl/#d0e5752">W3C Definitions</a>
*/
public enum LengthUnit {
/**
/trunk/OpenConcerto/src/org/openconcerto/openoffice/ODXMLDocument.java
16,6 → 16,7
*/
package org.openconcerto.openoffice;
 
import org.openconcerto.openoffice.ODPackage.RootElement;
import org.openconcerto.openoffice.spreadsheet.ColumnStyle;
import org.openconcerto.utils.cc.IFactory;
import org.openconcerto.xml.JDOMUtils;
90,6 → 91,13
return Collections.unmodifiableSet(namePrefixes.keySet());
}
 
public static final ODXMLDocument create(final Document doc) {
if (RootElement.fromDocument(doc) == RootElement.SINGLE_CONTENT)
return new ODSingleXMLDocument(doc);
else
return new ODXMLDocument(doc);
}
 
private final Document content;
private final XMLFormatVersion version;
private final ChildCreator childCreator;
/trunk/OpenConcerto/src/org/openconcerto/openoffice/ODSingleXMLDocument.java
15,13 → 15,9
 
import static org.openconcerto.openoffice.ODPackage.RootElement.CONTENT;
import org.openconcerto.openoffice.ODPackage.RootElement;
import org.openconcerto.openoffice.text.TextNode;
import org.openconcerto.utils.CollectionMap;
import org.openconcerto.utils.CopyUtils;
import org.openconcerto.utils.ExceptionUtils;
import org.openconcerto.utils.ProductInfo;
import org.openconcerto.utils.cc.IFactory;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.xml.JDOMUtils;
import org.openconcerto.xml.SimpleXMLPath;
import org.openconcerto.xml.Step;
33,7 → 29,6
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
56,7 → 51,7
*
* @author Sylvain CUAZ 24 nov. 2004
*/
public class ODSingleXMLDocument extends ODXMLDocument implements Cloneable, ODDocument {
public class ODSingleXMLDocument extends ODXMLDocument implements Cloneable {
 
final static Set<String> DONT_PREFIX;
static {
82,14 → 77,12
* @return the merged document.
*/
public static ODSingleXMLDocument createFromDocument(Document content, Document style) {
return createFromDocument(content, style, null);
return ODPackage.createFromDocuments(content, style).toSingle();
}
 
public static ODSingleXMLDocument createFromDocument(Document content, Document style, Document settings) {
return createFromDocument(content, style, settings, null, new ODPackage());
}
 
static ODSingleXMLDocument createFromDocument(Document content, Document style, Document settings, Document meta, ODPackage files) {
static ODSingleXMLDocument create(ODPackage files) {
final Document content = files.getContent().getDocument();
final Document style = files.getDocument(RootElement.STYLES.getZipEntry());
// signal that the xml is a complete document (was document-content)
final Document singleContent = RootElement.createSingle(content);
copyNS(content, singleContent);
97,8 → 90,8
final Element root = singleContent.getRootElement();
root.addContent(content.getRootElement().removeContent());
// see section 2.1.1 first meta, then settings, then the rest
prependToRoot(settings, root);
prependToRoot(meta, root);
prependToRoot(files.getDocument(RootElement.SETTINGS.getZipEntry()), root);
prependToRoot(files.getDocument(RootElement.META.getZipEntry()), root);
final ODSingleXMLDocument single = new ODSingleXMLDocument(singleContent, files);
if (single.getChild("body") == null)
throw new IllegalArgumentException("no body in " + single);
139,7 → 132,6
* @return the merged file.
* @throws JDOMException if the file is not a valid OpenDocument file.
* @throws IOException if the file can't be read.
* @see #createFromDocument(Document, Document)
*/
public static ODSingleXMLDocument createFromFile(File f) throws JDOMException, IOException {
// this loads all linked files
266,39 → 258,11
return this.numero;
}
 
@Override
public ODPackage getPackage() {
return this.pkg;
}
 
/**
* Append a paragraph or a heading.
*
* @param p paragraph to add.
*/
public synchronized void add(TextNode p) {
this.add(p, null, -1);
}
 
public synchronized void add(TextNode p, Element where, int index) {
// add it first to avoid infinite loop, since setDocument() can call this method
final Element addToElem = where == null ? this.getBody() : where;
if (index < 0)
addToElem.addContent(p.getElement());
else
addToElem.addContent(index, p.getElement());
 
try {
p.setDocument(this);
} catch (RuntimeException e) {
// the paragraph can throw an exception to notify that is not compatible with us (eg
// missing styles), in that case remove it
p.getElement().detach();
throw e;
}
}
 
/**
* Append a document.
*
* @param doc the document to add.
545,77 → 509,6
}
 
/**
* Verify that styles referenced by this document are indeed defined. NOTE this method is not
* perfect : not all problems are detected.
*
* @return <code>null</code> if no problem has been found, else a String describing it.
*/
public final String checkStyles() {
try {
final CollectionMap<String, String> stylesNames = this.getStylesNames();
// text:style-name : text:p, text:span
// table:style-name : table:table, table:row, table:column, table:cell
// draw:style-name : draw:text-box
// style:data-style-name : <style:style style:family="table-cell">
// TODO check by family
final Set<String> names = new HashSet<String>(stylesNames.values());
final Iterator attrs = this.getXPath(".//@text:style-name | .//@table:style-name | .//@draw:style-name | .//@style:data-style-name | .//@style:list-style-name")
.selectNodes(this.getDocument()).iterator();
while (attrs.hasNext()) {
final Attribute attr = (Attribute) attrs.next();
if (!names.contains(attr.getValue()))
return "unknown style referenced by " + attr.getName() + " in " + JDOMUtils.output(attr.getParent());
}
// TODO check other references like page-*-name (§3 of #prefix())
} catch (IllegalStateException e) {
return ExceptionUtils.getStackTrace(e);
} catch (JDOMException e) {
return ExceptionUtils.getStackTrace(e);
}
return null;
}
 
private final CollectionMap<String, String> getStylesNames() throws IllegalStateException {
// section 14.1 § Style Name : style:family + style:name is unique
final CollectionMap<String, String> res = new CollectionMap<String, String>(HashSet.class);
 
final List<Element> nodes = new ArrayList<Element>();
nodes.add(this.getChild("styles"));
nodes.add(this.getChild("automatic-styles"));
 
try {
{
final Iterator iter = this.getXPath("./style:style/@style:name").selectNodes(nodes).iterator();
while (iter.hasNext()) {
final Attribute attr = (Attribute) iter.next();
final String styleName = attr.getValue();
final String family = attr.getParent().getAttributeValue("family", attr.getNamespace());
if (res.getNonNull(family).contains(styleName))
throw new IllegalStateException("duplicate style in " + family + " : " + styleName);
res.put(family, styleName);
}
}
{
final List<String> dataStyles = Arrays.asList("number-style", "currency-style", "percentage-style", "date-style", "time-style", "boolean-style", "text-style");
final String xpDataStyles = org.openconcerto.utils.CollectionUtils.join(dataStyles, " | ", new ITransformer<String, String>() {
@Override
public String transformChecked(String input) {
return "./number:" + input;
}
});
final Iterator listIter = this.getXPath("./text:list-style | " + xpDataStyles).selectNodes(nodes).iterator();
while (listIter.hasNext()) {
final Element elem = (Element) listIter.next();
res.put(elem.getQualifiedName(), elem.getAttributeValue("name", getVersion().getSTYLE()));
}
}
} catch (JDOMException e) {
throw new IllegalStateException(e);
}
return res;
}
 
/**
* Préfixe les attributs en ayant besoin.
*
* @param elem l'élément à préfixer.
822,13 → 715,13
final Map<RootElement, Document> res = new HashMap<RootElement, Document>();
final XMLVersion version = getVersion();
final Element root = this.getDocument().getRootElement();
final String officeVersion = getFormatVersion().getOfficeVersion();
final XMLFormatVersion officeVersion = getFormatVersion();
 
// meta
{
final Element thisMeta = root.getChild("meta", version.getOFFICE());
if (thisMeta != null) {
final Document meta = createDocument(res, RootElement.META, version, officeVersion);
final Document meta = createDocument(res, RootElement.META, officeVersion);
meta.getRootElement().addContent(thisMeta.detach());
}
}
836,7 → 729,7
{
final Element thisSettings = root.getChild("settings", version.getOFFICE());
if (thisSettings != null) {
final Document settings = createDocument(res, RootElement.SETTINGS, version, officeVersion);
final Document settings = createDocument(res, RootElement.SETTINGS, officeVersion);
settings.getRootElement().addContent(thisSettings.detach());
}
}
843,7 → 736,7
// styles
// we must move office:styles, office:master-styles and referenced office:automatic-styles
{
final Document styles = createDocument(res, RootElement.STYLES, version, officeVersion);
final Document styles = createDocument(res, RootElement.STYLES, officeVersion);
// don't bother finding out which font is used where since there isn't that many of them
styles.getRootElement().addContent((Element) root.getChild(getFontDecls()[0], version.getOFFICE()).clone());
// extract common styles
874,7 → 767,7
// content
{
this.pkg = null;
final Document content = createDocument(res, RootElement.CONTENT, version, officeVersion);
final Document content = createDocument(res, RootElement.CONTENT, officeVersion);
getContentTypeVersioned().setType(content);
content.getRootElement().addContent(root.removeContent());
}
881,8 → 774,8
return res;
}
 
private Document createDocument(final Map<RootElement, Document> res, RootElement rootElement, final XMLVersion version, final String officeVersion) {
final Document doc = rootElement.createDocument(version, officeVersion);
private Document createDocument(final Map<RootElement, Document> res, RootElement rootElement, final XMLFormatVersion version) {
final Document doc = rootElement.createDocument(version);
copyNS(this.getDocument(), doc);
res.put(rootElement, doc);
return doc;
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/RepeatedBreaker.java
New file
0,0 → 1,97
/*
* 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.openoffice.spreadsheet;
 
import org.openconcerto.openoffice.ODDocument;
import org.openconcerto.openoffice.ODNode;
 
import java.util.List;
 
import org.jdom.Element;
 
abstract class RepeatedBreaker<P, C extends ODNode> {
 
@SuppressWarnings("rawtypes")
static private final RepeatedBreaker CELL_BREAKER = new RepeatedBreaker<Row<?>, Cell<?>>("number-columns-repeated") {
@Override
Cell<?> create(Element elem, Row<?> parent, int index, boolean single) {
return createD(elem, parent, index, single);
}
 
<D extends ODDocument> Cell<D> createD(Element elem, Row<D> parent, int index, boolean single) {
return single ? new MutableCell<D>(parent, elem, parent.getSheet().getCellStyleDesc()) : new Cell<D>(parent, elem, parent.getSheet().getCellStyleDesc());
}
};
 
@SuppressWarnings("rawtypes")
static private final RepeatedBreaker ROW_BREAKER = new RepeatedBreaker<Table<?>, Row<?>>(Axis.ROW.getRepeatedAttrName()) {
@Override
Row<?> create(Element elem, Table<?> parent, int index, boolean single) {
return createD(elem, parent, index, single);
}
 
<D extends ODDocument> Row<D> createD(Element elem, Table<D> parent, int index, boolean single) {
return new Row<D>(parent, elem, index, parent.getRowStyleDesc(), parent.getCellStyleDesc());
}
};
 
@SuppressWarnings("unchecked")
static final <D extends ODDocument> RepeatedBreaker<Row<D>, Cell<D>> getCellBreaker() {
return (RepeatedBreaker<Row<D>, Cell<D>>) CELL_BREAKER;
}
 
@SuppressWarnings("unchecked")
static final <D extends ODDocument> RepeatedBreaker<Table<D>, Row<D>> getRowBreaker() {
return (RepeatedBreaker<Table<D>, Row<D>>) ROW_BREAKER;
}
 
private final String attrName;
 
public RepeatedBreaker(final String attrName) {
this.attrName = attrName;
}
 
abstract C create(final Element elem, final P parent, final int index, final boolean single);
 
public final void breakRepeated(final P parent, final List<C> children, final int col) {
final C c = children.get(col);
final Element element = c.getElement();
final String repeatedS = element.getAttributeValue(this.attrName, element.getNamespace());
if (repeatedS != null) {
final int repeated = Integer.parseInt(repeatedS);
final int firstIndex = children.indexOf(c);
final int lastIndex = firstIndex + repeated - 1;
 
final int preRepeated = col - firstIndex;
final int postRepeated = lastIndex - col;
 
breakRepeated(parent, children, element, firstIndex, preRepeated, true);
element.removeAttribute(this.attrName, element.getNamespace());
breakRepeated(parent, children, element, col + 1, postRepeated, false);
}
children.set(col, this.create(element, parent, col, true));
}
 
private final void breakRepeated(final P parent, final List<C> children, Element element, int firstIndex, int repeat, boolean before) {
if (repeat > 0) {
final Element newElem = (Element) element.clone();
element.getParentElement().addContent(element.getParent().indexOf(element) + (before ? 0 : 1), newElem);
newElem.setAttribute(this.attrName, repeat + "", element.getNamespace());
final C preCell = this.create(newElem, parent, firstIndex, false);
for (int i = 0; i < repeat; i++) {
children.set(firstIndex + i, preCell);
}
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/Table.java
16,6 → 16,7
import org.openconcerto.openoffice.LengthUnit;
import org.openconcerto.openoffice.ODDocument;
import org.openconcerto.openoffice.Style;
import org.openconcerto.openoffice.StyleDesc;
import org.openconcerto.openoffice.StyleStyleDesc;
import org.openconcerto.openoffice.XMLVersion;
import org.openconcerto.openoffice.spreadsheet.SheetTableModel.MutableTableModel;
30,7 → 31,6
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
60,92 → 60,105
}
 
// ATTN Row have their index as attribute
private final List<Row<D>> rows;
private int headerRowCount;
private final List<Column<D>> cols;
private int headerColumnCount;
private final ArrayList<Row<D>> rows;
private TableGroup rowGroup;
private final ArrayList<Column<D>> cols;
private TableGroup columnGroup;
 
public Table(D parent, Element local) {
super(parent, local, TableStyle.class);
 
this.rows = new ArrayList<Row<D>>();
this.cols = new ArrayList<Column<D>>();
this.rows = new ArrayList<Row<D>>(64);
this.cols = new ArrayList<Column<D>>(32);
 
// read columns first since Row constructor needs it
this.readColumns();
this.readRows();
}
 
private void readColumns() {
this.read(true);
this.read(Axis.COLUMN);
}
 
private final void readRows() {
this.read(false);
this.read(Axis.ROW);
}
 
private final void read(final boolean col) {
final Tuple2<List<Element>, Integer> r = flatten(col);
(col ? this.cols : this.rows).clear();
for (final Element clone : r.get0()) {
if (col)
this.addCol(clone);
else
this.addRow(clone);
private final void read(final Axis axis) {
final boolean col = axis == Axis.COLUMN;
final Tuple2<TableGroup, List<Element>> r = TableGroup.createRoot(this, axis);
final ArrayList<?> l = col ? this.cols : this.rows;
final int oldSize = l.size();
l.clear();
final int newSize = r.get0().getSize();
l.ensureCapacity(newSize);
if (col) {
final StyleStyleDesc<ColumnStyle> colStyleDesc = getColumnStyleDesc();
for (final Element clone : r.get1())
this.addCol(clone, colStyleDesc);
this.columnGroup = r.get0();
} else {
final StyleStyleDesc<RowStyle> rowStyleDesc = getRowStyleDesc();
final StyleStyleDesc<CellStyle> cellStyleDesc = getCellStyleDesc();
for (final Element clone : r.get1())
this.addRow(clone, rowStyleDesc, cellStyleDesc);
this.rowGroup = r.get0();
}
if (col)
this.headerColumnCount = r.get1();
else
this.headerRowCount = r.get1();
// this always copy the array, so make sure we reclaim enough memory (~ 64k)
if (oldSize - newSize > 8192) {
l.trimToSize();
}
assert newSize == (col ? this.getColumnCount() : this.getRowCount());
}
 
private final void addCol(Element clone) {
this.cols.add(new Column<D>(this, clone));
private final void addCol(Element clone, StyleStyleDesc<ColumnStyle> colStyleDesc) {
this.cols.add(new Column<D>(this, clone, colStyleDesc));
}
 
private Tuple2<List<Element>, Integer> flatten(boolean col) {
final List<Element> res = new ArrayList<Element>();
final Element header = this.getElement().getChild("table-header-" + getName(col) + "s", getTABLE());
if (header != null)
res.addAll(flatten(header, col));
final int headerCount = res.size();
static final int flattenChildren(final List<Element> res, final Element elem, final Axis axis) {
int count = 0;
// array so that flatten1() can modify an int
int[] index = new int[] { 0 };
// copy since we will change our children (don't use List.listIterator(int) since it
// re-filters all content)
@SuppressWarnings("unchecked")
final List<Element> children = new ArrayList<Element>(elem.getChildren(axis.getElemName(), elem.getNamespace()));
final int stop = children.size();
for (int i = 0; i < stop; i++) {
final Element row = children.get(i);
count += flatten1(res, row, axis, index);
}
return count;
}
 
res.addAll(flatten(getElement(), col));
 
return Tuple2.create(res, headerCount);
static int flatten1(final List<Element> res, final Element row, final Axis axis) {
return flatten1(res, row, axis, null);
}
 
@SuppressWarnings("unchecked")
private List<Element> flatten(final Element elem, boolean col) {
final String childName = getName(col);
final List<Element> children = elem.getChildren("table-" + childName, getTABLE());
// not final, since iter.add() does not work consistently, and
// thus we must recreate an iterator each time
ListIterator<Element> iter = children.listIterator();
while (iter.hasNext()) {
final Element row = iter.next();
final Attribute repeatedAttr = row.getAttribute("number-" + childName + "s-repeated", getTABLE());
if (repeatedAttr != null) {
// add XML elements to res and return the logical count
private static int flatten1(final List<Element> res, final Element row, final Axis axis, final int[] parentIndex) {
final int resSize = res.size();
final Attribute repeatedAttr = axis.getRepeatedAttr(row);
final int repeated = repeatedAttr == null ? 1 : Integer.parseInt(repeatedAttr.getValue());
if (axis == Axis.COLUMN && repeated > 1) {
row.removeAttribute(repeatedAttr);
final int index = iter.previousIndex();
int repeated = Integer.parseInt(repeatedAttr.getValue());
if (repeated > 60000) {
repeated = 10;
}
final Element parent = row.getParentElement();
final int index = (parentIndex == null ? parent.indexOf(row) : parentIndex[0]) + 1;
res.add(row);
// -1 : we keep the original row
for (int i = 0; i < repeated - 1; i++) {
final Element clone = (Element) row.clone();
// cannot use iter.add() since on JDOM 1.1 if row is the last table-column
// before table-row the clone is added at the very end
children.add(index, clone);
res.add(clone);
parent.addContent(index + i, clone);
}
// restart after the added rows
iter = children.listIterator(index + repeated);
} else {
res.add(row);
}
if (parentIndex != null)
parentIndex[0] += res.size() - resSize;
return repeated;
}
 
return children;
}
 
public final String getName() {
return getName(this.getElement());
}
158,10 → 171,6
this.getElement().detach();
}
 
private final String getName(boolean col) {
return col ? "column" : "row";
}
 
public final Object getPrintRanges() {
return this.getElement().getAttributeValue("print-ranges", this.getTABLE());
}
235,14 → 244,38
 
// clone xml elements and add them to our tree
final List<Element> clones = new ArrayList<Element>(count * copies);
for (int i = 0; i < copies; i++) {
for (int l = start; l < stop; l++) {
final Element r = this.rows.get(l).getElement();
clones.add((Element) r.clone());
for (int l = start; l < stop;) {
final Row<D> immutableRow = this.getRow(l);
final Row<D> toClone;
// MAYBE use something else than getMutableRow() since we don't need a single row.
// the repeated row starts before the copied range, split it at the beginning
if (immutableRow.getY() < l) {
toClone = this.getMutableRow(l);
} else {
assert immutableRow.getY() == l;
if (immutableRow.getLastY() >= stop) {
// the repeated row goes beyond the copied range, split it at the end
assert this.getRow(stop) == immutableRow;
this.getMutableRow(stop);
toClone = this.getRow(l);
} else {
toClone = immutableRow;
}
}
assert toClone.getY() == l;
assert toClone.getLastY() < stop : "Row goes to far";
l += toClone.getRepeated();
clones.add((Element) toClone.getElement().clone());
}
final int clonesSize = clones.size();
for (int i = 1; i < copies; i++) {
for (int j = 0; j < clonesSize; j++) {
clones.add((Element) clones.get(j).clone());
}
}
// works anywhere its XML element is
JDOMUtils.insertAfter(this.rows.get(stop - 1).getElement(), clones);
assert this.getRow(stop - 1).getLastY() == stop - 1 : "Adding XML element too far";
JDOMUtils.insertAfter(this.getRow(stop - 1).getElement(), clones);
 
for (final Point coverOrigin : coverOriginsToUpdate) {
final MutableCell<D> coveringCell = getCellAt(coverOrigin);
249,7 → 282,7
coveringCell.setRowsSpanned(coveringCell.getRowsSpanned() + count * copies);
}
 
// synchronize our rows with our new tree
// synchronize our rows with our new tree (rows' index have changed)
this.readRows();
 
// 19.627 in OpenDocument-v1.2-cs01-part1 : The table:end-cell-address attribute specifies
323,9 → 356,13
}
}
 
private synchronized void addRow(Element child) {
this.rows.add(new Row<D>(this, child, this.rows.size()));
private synchronized void addRow(Element child, StyleDesc<RowStyle> styleDesc, StyleDesc<CellStyle> cellStyleDesc) {
final Row<D> row = new Row<D>(this, child, this.rows.size(), styleDesc, cellStyleDesc);
final int toRepeat = row.getRepeated();
for (int i = 0; i < toRepeat; i++) {
this.rows.add(row);
}
}
 
public final Point resolveHint(String ref) {
final Point res = resolve(ref);
346,8 → 383,18
return this.getImmutableCellAt(x, y).isValid();
}
 
/**
* Return a modifiable cell at the passed coordinates. This is slower than
* {@link #getImmutableCellAt(int, int)} since this method may modify the underlying XML (e.g.
* break up repeated cells to allow for modification of only the returned cell).
*
* @param x the column.
* @param y the row.
* @return the cell.
* @see #getImmutableCellAt(int, int)
*/
public final MutableCell<D> getCellAt(int x, int y) {
return this.getRow(y).getMutableCellAt(x);
return this.getMutableRow(y).getMutableCellAt(x);
}
 
public final MutableCell<D> getCellAt(String ref) {
375,11 → 422,20
 
// *** get cell
 
protected final Cell<D> getImmutableCellAt(int x, int y) {
/**
* Return a non modifiable cell at the passed coordinates. This is faster than
* {@link #getCellAt(int, int)} since this method never modifies the underlying XML.
*
* @param x the column.
* @param y the row.
* @return the cell.
* @see #getCellAt(int, int)
*/
public final Cell<D> getImmutableCellAt(int x, int y) {
return this.getRow(y).getCellAt(x);
}
 
protected final Cell<D> getImmutableCellAt(String ref) {
public final Cell<D> getImmutableCellAt(String ref) {
final Point p = resolveHint(ref);
return this.getImmutableCellAt(p.x, p.y);
}
457,7 → 513,7
}
 
public final CellStyle getStyleAt(int column, int row) {
return getCellStyleDesc().findStyle(this.getODDocument().getPackage(), this.getElement().getDocument(), this.getStyleNameAt(column, row));
return getCellStyleDesc().findStyleForNode(this.getImmutableCellAt(column, row), this.getStyleNameAt(column, row));
}
 
protected StyleStyleDesc<CellStyle> getCellStyleDesc() {
506,6 → 562,14
return res;
}
 
protected final StyleStyleDesc<ColumnStyle> getColumnStyleDesc() {
return Style.getStyleStyleDesc(ColumnStyle.class, XMLVersion.getVersion(getElement()));
}
 
protected final StyleStyleDesc<RowStyle> getRowStyleDesc() {
return Style.getStyleStyleDesc(RowStyle.class, XMLVersion.getVersion(getElement()));
}
 
/**
* Retourne la valeur de la cellule spécifiée.
*
518,10 → 582,20
 
// *** get count
 
private Row<D> getRow(int index) {
final Row<D> getRow(int index) {
return this.rows.get(index);
}
 
final Row<D> getMutableRow(int y) {
final Row<D> c = this.getRow(y);
if (c.getRepeated() > 1) {
RepeatedBreaker.<D> getRowBreaker().breakRepeated(this, this.rows, y);
return this.getRow(y);
} else {
return c;
}
}
 
public final Column<D> getColumn(int i) {
return this.cols.get(i);
}
530,8 → 604,22
return this.rows.size();
}
 
public final TableGroup getRowGroup() {
return this.rowGroup;
}
 
/**
* Return the deepest group at the passed row.
*
* @param y a row index.
* @return the group at the index, never <code>null</code>.
*/
public final TableGroup getRowGroupAt(final int y) {
return this.getRowGroup().getDescendentOrSelfContaining(y);
}
 
public final int getHeaderRowCount() {
return this.headerRowCount;
return this.getRowGroup().getFollowingHeaderCount();
}
 
public final int getColumnCount() {
538,8 → 626,22
return this.cols.size();
}
 
public final TableGroup getColumnGroup() {
return this.columnGroup;
}
 
/**
* Return the deepest group at the passed column.
*
* @param x a column index.
* @return the group at the index, never <code>null</code>.
*/
public final TableGroup getColumnGroupAt(final int x) {
return this.getColumnGroup().getDescendentOrSelfContaining(x);
}
 
public final int getHeaderColumnCount() {
return this.headerColumnCount;
return this.getColumnGroup().getFollowingHeaderCount();
}
 
// *** set count
595,17 → 697,22
} else {
elemToClone = getColumn(colIndex).getElement();
}
final StyleStyleDesc<ColumnStyle> columnStyleDesc = getColumnStyleDesc();
for (int i = 0; i < toGrow; i++) {
final Element newElem = (Element) elemToClone.clone();
this.getElement().addContent(indexOfLastCol + 1 + i, newElem);
this.cols.add(new Column<D>(this, newElem));
this.cols.add(new Column<D>(this, newElem, columnStyleDesc));
}
// now update widths
updateWidth(keepTableWidth);
 
// add needed cells
for (final Row r : this.rows) {
r.columnCountChanged();
final StyleStyleDesc<CellStyle> cellStyleDesc = this.getCellStyleDesc();
final int rowCount = this.getRowCount();
for (int i = 0; i < rowCount;) {
final Row<D> r = this.getRow(i);
r.columnCountChanged(cellStyleDesc);
i += r.getRepeated();
}
}
}
629,16 → 736,21
*/
public final void removeColumn(int firstIndex, int lastIndex, final boolean keepTableWidth) {
// first check that removeCells() will succeed, so that we avoid an incoherent XML state
for (final Row r : this.rows) {
final int rowCount = this.getRowCount();
for (int i = 0; i < rowCount;) {
final Row<D> r = this.getRow(i);
r.checkRemove(firstIndex, lastIndex);
i += r.getRepeated();
}
// rm column element
remove(true, firstIndex, lastIndex - 1);
remove(Axis.COLUMN, firstIndex, lastIndex - 1);
// update widths
updateWidth(keepTableWidth);
// rm cells
for (final Row r : this.rows) {
for (int i = 0; i < rowCount;) {
final Row<D> r = this.getRow(i);
r.removeCells(firstIndex, lastIndex);
i += r.getRepeated();
}
}
 
709,20 → 821,41
return colStyle;
}
 
private final void setCount(final boolean col, final int newSize) {
private final void setCount(final Axis col, final int newSize) {
this.remove(col, newSize, -1);
}
 
// both inclusive
private final void remove(final boolean col, final int fromIndex, final int toIndexIncl) {
// ok since rows and cols are flattened in ctor
final List<? extends TableCalcNode> l = col ? this.cols : this.rows;
private final void remove(final Axis col, final int fromIndex, final int toIndexIncl) {
assert col == Axis.COLUMN || toIndexIncl < 0 : "Row index will be wrong";
final List<? extends TableCalcNode<?, ?>> l = col == Axis.COLUMN ? this.cols : this.rows;
final int toIndexValid = CollectionUtils.getValidIndex(l, toIndexIncl);
for (int i = toIndexValid; i >= fromIndex; i--) {
// works anywhere its XML element is
l.remove(i).getElement().detach();
final int toRemoveCount = toIndexValid - fromIndex + 1;
int removedCount = 0;
while (removedCount < toRemoveCount) {
// works backwards to keep y OK
final int i = toIndexValid - removedCount;
final TableCalcNode<?, ?> removed = l.get(i);
if (removed instanceof Row) {
final Row<?> r = (Row<?>) removed;
final int removeFromRepeated = i - Math.max(fromIndex, r.getY()) + 1;
// removedCount grows each iteration
assert removeFromRepeated > 0;
final int newRepeated = r.getRepeated() - removeFromRepeated;
if (newRepeated == 0)
removed.getElement().detach();
else
r.setRepeated(newRepeated);
removedCount += removeFromRepeated;
} else {
// Columns are always flattened
removed.getElement().detach();
removedCount++;
}
}
// one remove to be efficient
l.subList(fromIndex, toIndexValid + 1).clear();
}
 
public final void ensureRowCount(int newSize) {
if (newSize > this.getRowCount())
741,25 → 874,24
* @param rowIndex the index of the row to be copied, -1 for empty row (i.e. default style).
*/
public final void setRowCount(int newSize, int rowIndex) {
final int toGrow = newSize - this.getRowCount();
if (toGrow < 0) {
setCount(Axis.ROW, newSize);
} else if (toGrow > 0) {
final Element elemToClone;
if (rowIndex < 0) {
elemToClone = Row.createEmpty(this.getODDocument().getVersion());
// each row MUST have the same number of columns
elemToClone.addContent(Cell.createEmpty(this.getODDocument().getVersion(), this.getColumnCount()));
} else
elemToClone = getRow(rowIndex).getElement();
final int toGrow = newSize - this.getRowCount();
if (toGrow < 0) {
setCount(false, newSize);
} else {
for (int i = 0; i < toGrow; i++) {
final Element newElem = (Element) elemToClone.clone();
elemToClone = (Element) getRow(rowIndex).getElement().clone();
}
Axis.ROW.setRepeated(elemToClone, toGrow);
// as per section 8.1.1 rows are the last elements inside a table
this.getElement().addContent(newElem);
addRow(newElem);
this.getElement().addContent(elemToClone);
addRow(elemToClone, getRowStyleDesc(), getCellStyleDesc());
}
}
}
 
// *** table models
 
948,7 → 1080,7
public final Range getCurrentRegion() {
while (this.checkFrame())
;// bounded by table size
return new Range(getName(), new Point(minX, minY), new Point(maxX, maxY));
return new Range(getName(), new Point(this.minX, this.minY), new Point(this.maxX, this.maxY));
}
}
 
968,7 → 1100,8
* @param startX x coordinate.
* @param startY y coordinate.
* @return the smallest range containing the passed cell.
* @see http://msdn.microsoft.com/library/aa214248(v=office.11).aspx
* @see <a href="http://msdn.microsoft.com/library/aa214248(v=office.11).aspx">CurrentRegion
* Property</a>
*/
public final Range getCurrentRegion(final int startX, final int startY) {
return this.getCurrentRegion(startX, startY, false);
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/MutableCell.java
18,11 → 18,13
import org.openconcerto.openoffice.ODFrame;
import org.openconcerto.openoffice.ODValueType;
import org.openconcerto.openoffice.OOXML;
import org.openconcerto.openoffice.StyleDesc;
import org.openconcerto.openoffice.spreadsheet.BytesProducer.ByteArrayProducer;
import org.openconcerto.openoffice.spreadsheet.BytesProducer.ImageProducer;
import org.openconcerto.openoffice.style.data.DataStyle;
import org.openconcerto.utils.ExceptionUtils;
import org.openconcerto.utils.FileUtils;
import org.openconcerto.utils.Tuple3;
 
import java.awt.Color;
import java.awt.Image;
36,6 → 38,9
import java.util.Date;
import java.util.List;
 
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.Duration;
 
import org.jdom.Attribute;
import org.jdom.Element;
import org.jdom.Namespace;
51,6 → 56,7
 
static private final DateFormat TextPDateFormat = DateFormat.getDateInstance();
static private final DateFormat TextPTimeFormat = DateFormat.getTimeInstance();
static private final NumberFormat TextPMinuteSecondFormat = new DecimalFormat("00.###");
static private final NumberFormat TextPFloatFormat = DecimalFormat.getNumberInstance();
static private final NumberFormat TextPPercentFormat = DecimalFormat.getPercentInstance();
static private final NumberFormat TextPCurrencyFormat = DecimalFormat.getCurrencyInstance();
76,8 → 82,8
}
}
 
MutableCell(Row<D> parent, Element elem) {
super(parent, elem);
MutableCell(Row<D> parent, Element elem, StyleDesc<CellStyle> styleDesc) {
super(parent, elem, styleDesc);
}
 
// ask our column to our row so we don't have to update anything when columns are removed/added
142,6 → 148,10
}
 
public void setValue(Object obj) {
this.setValue(obj, true);
}
 
public void setValue(Object obj, final boolean allowTypeChange) throws UnsupportedOperationException {
final ODValueType type;
final ODValueType currentType = getValueType();
// try to keep current type, since for example a Number can work with FLOAT, PERCENTAGE
148,18 → 158,13
// and CURRENCY
if (currentType != null && currentType.canFormat(obj.getClass())) {
type = currentType;
} else if (obj instanceof Number) {
type = ODValueType.FLOAT;
} else if (obj instanceof Date || obj instanceof Calendar) {
type = ODValueType.DATE;
} else if (obj instanceof Boolean) {
type = ODValueType.BOOLEAN;
} else if (obj instanceof String) {
type = ODValueType.STRING;
} else {
type = ODValueType.forObject(obj);
}
if (type == null) {
throw new IllegalArgumentException("Couldn't infer type of " + obj);
}
this.setValue(obj, type, true);
this.setValue(obj, type, allowTypeChange, true);
}
 
/**
167,16 → 172,20
*
* @param obj the new cell value.
* @param vt the value type.
* @param allowTypeChange if <code>true</code> <code>obj</code> and <code>vt</code> might be
* changed to allow the data style to format, e.g. from Boolean.FALSE to 0.
* @param lenient <code>false</code> to throw an exception if we can't format according to the
* ODF, <code>true</code> to try best-effort.
* @throws UnsupportedOperationException if <code>obj</code> couldn't be formatted.
*/
public void setValue(final Object obj, final ODValueType vt, final boolean lenient) throws UnsupportedOperationException {
public void setValue(Object obj, ODValueType vt, final boolean allowTypeChange, final boolean lenient) throws UnsupportedOperationException {
final String text;
final String formatted = format(obj, lenient);
final Tuple3<String, ODValueType, Object> formatted = format(obj, vt, !allowTypeChange, lenient);
vt = formatted.get1();
obj = formatted.get2();
 
if (formatted != null) {
text = formatted;
if (formatted.get0() != null) {
text = formatted.get0();
} else {
// either there were no format or formatting failed
if (vt == ODValueType.FLOAT) {
186,9 → 195,21
} else if (vt == ODValueType.CURRENCY) {
text = formatCurrency((Number) obj, getDefaultStyle());
} else if (vt == ODValueType.DATE) {
text = TextPDateFormat.format(obj);
final Date d;
if (obj instanceof Calendar) {
d = ((Calendar) obj).getTime();
} else {
d = (Date) obj;
}
text = TextPDateFormat.format(d);
} else if (vt == ODValueType.TIME) {
text = TextPTimeFormat.format(obj);
if (obj instanceof Duration) {
final Duration normalized = getODDocument().getEpoch().normalizeToHours((Duration) obj);
text = "" + normalized.getHours() + ':' + TextPMinuteSecondFormat.format(normalized.getMinutes()) + ':'
+ TextPMinuteSecondFormat.format(normalized.getField(DatatypeConstants.SECONDS));
} else {
text = TextPTimeFormat.format(((Calendar) obj).getTime());
}
} else if (vt == ODValueType.BOOLEAN) {
if (lenient)
text = obj.toString();
203,14 → 224,19
this.setValue(vt, obj, text);
}
 
// return null if no data style exists, or if one exists but we couldn't use it
private String format(Object obj, boolean lenient) {
// return null String if no data style exists, or if one exists but we couldn't use it
private Tuple3<String, ODValueType, Object> format(Object obj, ODValueType valueType, boolean onlyCast, boolean lenient) {
String res = null;
try {
final DataStyle ds = getDataStyle();
final Tuple3<DataStyle, ODValueType, Object> ds = getDataStyleAndValue(obj, valueType, onlyCast);
if (ds != null) {
obj = ds.get2();
valueType = ds.get1();
// act like OO, that is if we set a String to a Date cell, change the value and
// value-type but leave the data-style untouched
if (ds != null && ds.canFormat(obj.getClass()))
return ds.format(obj, getDefaultStyle(), lenient);
if (ds.get0().canFormat(obj.getClass()))
res = ds.get0().format(obj, getDefaultStyle(), lenient);
}
} catch (UnsupportedOperationException e) {
if (lenient)
Log.get().warning(ExceptionUtils.getStackTrace(e));
217,12 → 243,17
else
throw e;
}
return null;
return Tuple3.create(res, valueType, obj);
}
 
public final DataStyle getDataStyle() {
final Tuple3<DataStyle, ODValueType, Object> s = this.getDataStyleAndValue(this.getValue(), this.getValueType(), true);
return s != null ? s.get0() : null;
}
 
private final Tuple3<DataStyle, ODValueType, Object> getDataStyleAndValue(Object obj, ODValueType valueType, boolean onlyCast) {
final CellStyle s = this.getStyle();
return s != null ? getStyle().getDataStyle() : null;
return s != null ? getStyle().getDataStyle(obj, valueType, onlyCast) : null;
}
 
protected final CellStyle getDefaultStyle() {
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/Column.java
14,6 → 14,7
package org.openconcerto.openoffice.spreadsheet;
 
import org.openconcerto.openoffice.ODDocument;
import org.openconcerto.openoffice.StyleStyleDesc;
import org.openconcerto.openoffice.XMLVersion;
 
import org.jdom.Element;
30,8 → 31,8
return res;
}
 
public Column(final Table<D> parent, Element tableColElem) {
super(parent.getODDocument(), tableColElem, ColumnStyle.class);
public Column(final Table<D> parent, Element tableColElem, StyleStyleDesc<ColumnStyle> colStyleDesc) {
super(parent.getODDocument(), tableColElem, colStyleDesc);
}
 
public final Float getWidth() {
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/CellStyle.java
13,23 → 13,44
package org.openconcerto.openoffice.spreadsheet;
 
import org.openconcerto.openoffice.Log;
import org.openconcerto.openoffice.ODPackage;
import org.openconcerto.openoffice.ODValueType;
import org.openconcerto.openoffice.Style;
import org.openconcerto.openoffice.StyleStyle;
import org.openconcerto.openoffice.StyleStyleDesc;
import org.openconcerto.openoffice.StyledNode;
import org.openconcerto.openoffice.XMLVersion;
import org.openconcerto.openoffice.style.RelationalOperator;
import org.openconcerto.openoffice.style.SideStyleProperties;
import org.openconcerto.openoffice.style.data.BooleanStyle;
import org.openconcerto.openoffice.style.data.DataStyle;
import org.openconcerto.openoffice.style.data.NumberStyle;
import org.openconcerto.openoffice.text.ParagraphStyle.StyleParagraphProperties;
import org.openconcerto.openoffice.text.TextStyle.StyleTextProperties;
import org.openconcerto.utils.CompareUtils;
import org.openconcerto.utils.Tuple3;
import org.openconcerto.xml.JDOMUtils;
 
import java.awt.Color;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import org.jdom.Attribute;
import org.jdom.Element;
 
public class CellStyle extends StyleStyle {
 
private static final Pattern numberPatrn = Pattern.compile("-?\\d+(?:\\.\\d+)?");
private static final Pattern escapedQuotePatrn = Pattern.compile("\"\"", Pattern.LITERAL);
private static final Pattern stringPatrn = Pattern.compile("\"(?:[^\\p{Cntrl}\"]|\\p{Space}|" + escapedQuotePatrn.pattern() + ")*\"");
private static final String valuePatrn = "(" + numberPatrn.pattern() + "|" + stringPatrn.pattern() + ")";
private static final Pattern cellContentPatrn = Pattern.compile("cell-content\\(\\) *(" + RelationalOperator.OR_PATTERN + ") *" + valuePatrn + "");
private static final Pattern cellContentBetweenPatrn = Pattern.compile("cell-content-is(?:-not)?-between\\(" + valuePatrn + ", *" + valuePatrn + "\\)");
 
// from section 18.728 in v1.2-part1
public static final StyleStyleDesc<CellStyle> DESC = new StyleStyleDesc<CellStyle>(CellStyle.class, XMLVersion.OD, "table-cell", "ce", "table", Arrays.asList("table:body",
"table:covered-table-cell", "table:even-rows", "table:first-column", "table:first-row", "table:last-column", "table:last-row", "table:odd-columns", "table:odd-rows", "table:table-cell")) {
42,8 → 63,54
public CellStyle create(ODPackage pkg, Element e) {
return new CellStyle(pkg, e);
}
 
@Override
protected boolean supportConditions() {
return true;
}
 
@Override
protected Element evaluateConditions(final StyledNode<CellStyle, ?> styledNode, final List<Element> styleMaps) {
final Cell<?> cell = (Cell<?>) styledNode;
final Object cellValue = cell.getValue();
for (final Element styleMap : styleMaps) {
final String condition = styleMap.getAttributeValue("condition", getVersion().getSTYLE()).trim();
Matcher matcher = cellContentPatrn.matcher(condition);
if (matcher.matches()) {
if (RelationalOperator.getInstance(matcher.group(1)).compare(cellValue, parse(matcher.group(2))))
return styleMap;
} else if ((matcher = cellContentBetweenPatrn.matcher(condition)).matches()) {
final boolean wantBetween = condition.startsWith("cell-content-is-between");
assert wantBetween ^ condition.startsWith("cell-content-is-not-between");
final Object o1 = parse(matcher.group(1));
final Object o2 = parse(matcher.group(2));
final boolean isBetween = CompareUtils.compare(cellValue, o1) >= 0 && CompareUtils.compare(cellValue, o2) <= 0;
if (isBetween == wantBetween)
return styleMap;
} else {
// If a consumer does not recognize a condition, it shall ignore the <style:map>
// element containing the condition.
Log.get().fine("Ignoring " + JDOMUtils.output(styleMap));
}
}
return null;
}
};
 
private static final Pattern conditionPatrn = Pattern.compile("value\\(\\) *(" + RelationalOperator.OR_PATTERN + ") *(true|false|" + numberPatrn.pattern() + ")");
 
// from style:condition :
// "n is a number for non-Boolean data styles and true or false for Boolean data styles"
private static final Object convertForCondition(final Object value, final DataStyle style) {
final Object castedValue;
if (style instanceof BooleanStyle) {
castedValue = BooleanStyle.toBoolean(value);
} else {
castedValue = NumberStyle.toNumber(value, style.getEpoch());
}
return castedValue;
}
 
private StyleTableCellProperties cellProps;
private StyleTextProperties textProps;
private StyleParagraphProperties pProps;
52,10 → 119,67
super(pkg, tableColElem);
}
 
public final DataStyle getDataStyle() {
return (DataStyle) Style.getReferencedStyle(getPackage(), getElement().getAttribute("data-style-name", getSTYLE()));
private final DataStyle getDataStyle(final Attribute name) {
return (DataStyle) Style.getReferencedStyle(getPackage(), name);
}
 
// return value since it can be changed depending on the data style.
// e.g. in OO if we input 12:30 in an empty cell, it will have value-type="time"
// but if we had previously set a number style (like 0,00) it would have been converted to 0,52
// value-type="float"
final Tuple3<DataStyle, ODValueType, Object> getDataStyle(final Object cellValue, final ODValueType valueType, final boolean onlyCast) {
DataStyle res = getDataStyle(this.getElement().getAttribute("data-style-name", this.getSTYLE()));
ODValueType returnValueType = valueType;
Object returnCellValue = cellValue;
// if the type is null, then the cell is empty so don't try to convert the cell value or
// evaluate conditions
if (res != null && valueType != null) {
if (!onlyCast) {
final Object convertedForStyle = res.convert(cellValue);
// if conversion is successful
if (convertedForStyle != null) {
returnCellValue = convertedForStyle;
returnValueType = res.getDataType();
}
}
 
final List<?> styleMaps = res.getElement().getChildren("map", getSTYLE());
if (styleMaps.size() > 0) {
final Object converted = convertForCondition(returnCellValue, res);
// we can't compare() so don't try
if (converted != null) {
for (Object child : styleMaps) {
final Element styleMap = (Element) child;
final Matcher matcher = conditionPatrn.matcher(styleMap.getAttributeValue("condition", getSTYLE()).trim());
if (!matcher.matches())
throw new IllegalStateException("Cannot parse " + JDOMUtils.output(styleMap));
if (RelationalOperator.getInstance(matcher.group(1)).compare(converted, parse(matcher.group(2)))) {
res = getDataStyle(styleMap.getAttribute("apply-style-name", getSTYLE()));
break;
}
}
}
}
}
// if the type is null, then the cell is empty, we cannot make up some value, otherwise
// don't change it to null
assert (valueType == null) == (returnValueType == null) : "don't change type to null";
assert !onlyCast || (returnValueType == valueType && returnCellValue == cellValue) : "Requested to only cast, but different object";
// if res is null, the document is incoherent (non existing style name)
return res == null ? null : Tuple3.create(res, returnValueType, returnCellValue);
}
 
static private Object parse(String val) {
if (val.equalsIgnoreCase("true"))
return Boolean.TRUE;
else if (val.equalsIgnoreCase("false"))
return Boolean.FALSE;
else if (val.charAt(0) == '"')
return escapedQuotePatrn.matcher(val.substring(1, val.length() - 1)).replaceAll("\"");
else
return new BigDecimal(val);
}
 
public final Color getBackgroundColor() {
return getTableCellProperties().getBackgroundColor();
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/SheetTableModel.java
14,6 → 14,7
package org.openconcerto.openoffice.spreadsheet;
 
import org.openconcerto.openoffice.ODDocument;
import org.openconcerto.utils.CompareUtils;
 
import javax.swing.table.AbstractTableModel;
 
76,6 → 77,50
throw new IndexOutOfBoundsException("column: " + columnIndex + " not between 0 and " + (this.getColumnCount() - 1));
}
 
@Override
public int hashCode() {
final int rowCount = getRowCount();
final int columnCount = getColumnCount();
final int prime = 17;
int result = 1;
result = prime * result + rowCount;
result = prime * result + columnCount;
// use some of the values
final int maxX = Math.min(4, columnCount);
final int maxY = Math.min(8, rowCount);
for (int y = 0; y < maxY; y++) {
for (int x = 0; x < maxX; x++) {
final Object v = this.getValueAt(x, y);
result = prime * result + (v == null ? 0 : v.hashCode());
}
}
return result;
}
 
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof SheetTableModel))
return false;
final SheetTableModel<?> other = (SheetTableModel<?>) obj;
 
final int rowCount = this.getRowCount();
final int columnCount = this.getColumnCount();
if (other.getRowCount() != rowCount || other.getColumnCount() != columnCount)
return false;
 
for (int y = 0; y < rowCount; y++) {
for (int x = 0; x < columnCount; x++) {
if (!CompareUtils.equals(this.getValueAt(x, y), other.getValueAt(x, y)))
return false;
}
}
return true;
}
 
static public final class MutableTableModel<D extends ODDocument> extends SheetTableModel<D> {
 
MutableTableModel(final Table<D> table, final int row, final int column) {
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/TableGroup.java
New file
0,0 → 1,174
/*
* 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.openoffice.spreadsheet;
 
import org.openconcerto.openoffice.ODNode;
import org.openconcerto.openoffice.StyleProperties;
import org.openconcerto.utils.Tuple2;
 
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
 
import org.jdom.Element;
 
/**
* A group of columns/rows that can be hidden.
*
* @author Sylvain CUAZ
*/
public class TableGroup extends ODNode {
 
static Tuple2<TableGroup, List<Element>> createRoot(final Table<?> table, final Axis col) {
final TableGroup group = new TableGroup(table, col);
return Tuple2.create(group, group.flatten());
}
 
private final Table<?> table;
private final Axis axis;
private final TableGroup parent;
private final List<TableGroup> children;
 
private int headerCount;
private int first;
private int size;
 
// root group
private TableGroup(final Table<?> table, final Axis col) {
this(table, col, null, table.getElement(), 0);
}
 
private TableGroup(Table<?> table, TableGroup parent, Element elem, final int first) {
this(table, parent.axis, parent, elem, first);
}
 
private TableGroup(Table<?> table, final Axis col, TableGroup parent, Element elem, final int first) {
super(elem);
if (table == null)
throw new NullPointerException("null table");
this.table = table;
this.axis = col;
this.parent = parent;
this.first = first;
this.children = new ArrayList<TableGroup>();
}
 
private List<Element> flatten() {
// max() since a group can have only group children
final List<Element> res = new ArrayList<Element>(Math.max(128, getElement().getContentSize()));
final String fullName = this.axis.getElemName();
final String groupName = this.axis.getGroupName();
final String pluralName = this.axis.getPluralName();
 
// A table shall not contain more than one <table:table-header-rows> element.
final Element header = this.getElement().getChild(this.axis.getHeaderName(), getElement().getNamespace());
if (header != null)
this.headerCount = Table.flattenChildren(res, header, this.axis);
else
this.headerCount = 0;
int size = this.headerCount;
 
this.children.clear();
@SuppressWarnings("unchecked")
final List<Element> content = new ArrayList<Element>(getElement().getChildren());
for (final Element child : content) {
if (child.getName().equals(fullName)) {
size += Table.flatten1(res, child, this.axis);
} else if (child.getName().equals(pluralName)) {
// ignore table-rows element (but add its children)
size += Table.flattenChildren(res, child, this.axis);
} else if (child.getName().equals(groupName)) {
final TableGroup g = new TableGroup(getTable(), this, child, this.first + size);
this.children.add(g);
res.addAll(g.flatten());
size += g.getSize();
}
// else nothing to do (header or soft-page-break)
}
 
this.size = size;
 
return res;
}
 
public final Table<?> getTable() {
return this.table;
}
 
/**
* The parent of this group.
*
* @return the parent, <code>null</code> if this is the root group.
*/
public final TableGroup getParent() {
return this.parent;
}
 
public final List<TableGroup> getChildren() {
return Collections.unmodifiableList(this.children);
}
 
final TableGroup getDescendentOrSelfContaining(final int y) {
if (!this.contains(y))
return null;
for (final TableGroup g : this.getChildren()) {
final TableGroup res = g.getDescendentOrSelfContaining(y);
if (res != null)
return res;
}
return this;
}
 
public final boolean isDisplayed() {
if (this.getParent() == null)
return true;
else
// from table:display : the default value for this attribute is true
return StyleProperties.parseBoolean(getElement().getAttributeValue("display", getElement().getNamespace()), true);
}
 
/**
* The index of the first row/column in this group.
*
* @return index of the first element.
*/
public final int getFirst() {
return this.first;
}
 
public final int getHeaderCount() {
return this.headerCount;
}
 
public final int getSize() {
return this.size;
}
 
public final boolean contains(final int i) {
return i >= this.getFirst() && i < this.getFirst() + this.getSize();
}
 
final int getFollowingHeaderCount() {
int res = this.getHeaderCount();
// the table and each distinct group may contain one <table:table-header-rows> element, if
// and only if the table rows contained in the <table:table-header-rows> elements are
// adjacent.
for (final TableGroup g : this.getChildren()) {
if (g.getFirst() != this.getFirst() + res)
break;
res += g.getFollowingHeaderCount();
}
return res;
}
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/Axis.java
New file
0,0 → 1,79
/*
* 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.openoffice.spreadsheet;
 
import org.openconcerto.openoffice.StyleProperties;
 
import org.jdom.Attribute;
import org.jdom.Element;
 
public enum Axis {
ROW("row"), COLUMN("column");
 
private final String shortName, elemName, headerName, groupName, pluralName, repeatedAttrName;
 
private Axis(final String shortName) {
this.shortName = shortName;
 
this.elemName = "table-" + shortName;
this.headerName = "table-header-" + shortName + "s";
this.groupName = this.elemName + "-group";
this.pluralName = this.elemName + "s";
this.repeatedAttrName = "number-" + shortName + "s-repeated";
}
 
public final String getShortName() {
return this.shortName;
}
 
public final String getElemName() {
return this.elemName;
}
 
public final String getHeaderName() {
return this.headerName;
}
 
public final String getGroupName() {
return this.groupName;
}
 
public final String getPluralName() {
return this.pluralName;
}
 
public final String getRepeatedAttrName() {
return this.repeatedAttrName;
}
 
final Attribute getRepeatedAttr(final Element elem) {
assert elem.getName().equals(this.getElemName());
return elem.getAttribute(getRepeatedAttrName(), elem.getNamespace());
}
 
final int getRepeated(final Element elem) {
assert elem.getName().equals(this.getElemName());
return StyleProperties.parseInt(elem.getAttributeValue(getRepeatedAttrName(), elem.getNamespace()), 1);
}
 
final void setRepeated(final Element elem, final int i) {
assert elem.getName().equals(this.getElemName());
if (i < 1)
throw new IllegalArgumentException("repeated <1 : " + i);
if (i == 1)
elem.removeAttribute(getRepeatedAttrName(), elem.getNamespace());
else
elem.setAttribute(getRepeatedAttrName(), String.valueOf(i), elem.getNamespace());
}
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/Row.java
16,10 → 16,13
*/
package org.openconcerto.openoffice.spreadsheet;
 
import org.openconcerto.openoffice.ODDocument;
import org.openconcerto.openoffice.StyleDesc;
import org.openconcerto.openoffice.StyleProperties;
import org.openconcerto.openoffice.StyleStyleDesc;
import org.openconcerto.openoffice.XMLVersion;
import org.openconcerto.openoffice.ODDocument;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
 
import org.jdom.Element;
39,46 → 42,79
 
private final Table<D> parent;
private final int index;
private int repeated;
// the same immutable cell instance is repeated, but each MutableCell is only once
// ATTN MutableCell have their index as attribute
private final List<Cell<D>> cells;
// array is faster than List
private Cell<D>[] cells;
private int cellCount;
 
Row(Table<D> parent, Element tableRowElem, int index) {
super(parent.getODDocument(), tableRowElem, RowStyle.class);
Row(Table<D> parent, Element tableRowElem, int index, StyleDesc<RowStyle> styleDesc, StyleDesc<CellStyle> cellStyleDesc) {
super(parent.getODDocument(), tableRowElem, styleDesc);
this.parent = parent;
this.index = index;
this.cells = new ArrayList<Cell<D>>();
this.repeated = Axis.ROW.getRepeated(getElement());
@SuppressWarnings("unchecked")
final Cell<D>[] unsafe = new Cell[parent.getColumnCount()];
this.cells = unsafe;
this.cellCount = 0;
for (final Element cellElem : this.getCellElements()) {
addCellElem(cellElem);
addCellElem(cellElem, cellStyleDesc);
}
}
 
private final void ensureRoom(int additionalItems) {
final int requiredSize = this.getCellCount() + additionalItems;
if (requiredSize > this.cells.length) {
this.cells = Arrays.copyOf(this.cells, requiredSize);
}
}
 
protected final Table<D> getSheet() {
return this.parent;
}
 
// ATTN index of the first row
final int getY() {
return this.index;
}
 
// plain Cell instances have multiple indexes (if repeated) but MutableCell are unique
final int getX(MutableCell<D> c) {
return this.cells.indexOf(c);
// inclusive
final int getLastY() {
return this.getY() + this.getRepeated() - 1;
}
 
private void addCellElem(final Element cellElem) {
final Cell<D> cell = new Cell<D>(this, cellElem);
this.cells.add(cell);
final int getRepeated() {
return this.repeated;
}
 
final String repeatedS = cellElem.getAttributeValue("number-columns-repeated", this.getSheet().getTABLE());
if (repeatedS != null) {
final int toRepeat = Integer.parseInt(repeatedS) - 1;
for (int i = 0; i < toRepeat; i++) {
this.cells.add(cell);
final void setRepeated(int newRepeated) {
Axis.ROW.setRepeated(getElement(), newRepeated);
this.repeated = newRepeated;
}
 
// plain Cell instances have multiple indexes (if repeated) but MutableCell are unique
final int getX(MutableCell<D> c) {
final int stop = this.getCellCount();
for (int i = 0; i < stop; i++) {
final Cell<D> item = this.cells[i];
if (c.equals(item))
return i;
}
return -1;
}
 
private void addCellElem(final Element cellElem, StyleDesc<CellStyle> cellStyleDesc) {
final Cell<D> cell = new Cell<D>(this, cellElem, cellStyleDesc);
final String repeatedS = cellElem.getAttributeValue("number-columns-repeated", this.getTABLE());
final int toRepeat = StyleProperties.parseInt(repeatedS, 1);
final int stop = this.cellCount + toRepeat;
for (int i = this.cellCount; i < stop; i++) {
this.cells[i] = cell;
}
this.cellCount = stop;
}
 
/**
* All cells of this row.
*
90,8 → 126,16
return this.getElement().getChildren();
}
 
protected final int getCellCount() {
return this.cellCount;
}
 
private final List<Cell<D>> getCellsAsList() {
return Arrays.asList(this.cells);
}
 
protected final Cell<D> getCellAt(int col) {
return this.cells.get(col);
return this.cells[col];
}
 
protected final Cell<D> getValidCellAt(int col) {
102,68 → 146,41
}
 
public final MutableCell<D> getMutableCellAt(final int col) {
final Cell c = this.getValidCellAt(col);
final Cell<D> c = this.getValidCellAt(col);
if (!(c instanceof MutableCell)) {
final Element element = c.getElement();
final String repeatedS = element.getAttributeValue("number-columns-repeated", this.getSheet().getTABLE());
if (repeatedS != null) {
final int repeated = Integer.parseInt(repeatedS);
final int firstIndex = this.cells.indexOf(c);
final int lastIndex = firstIndex + repeated - 1;
 
final int preRepeated = col - firstIndex;
final int postRepeated = lastIndex - col;
 
casse(element, firstIndex, preRepeated, true);
element.removeAttribute("number-columns-repeated", this.getSheet().getTABLE());
casse(element, col + 1, postRepeated, false);
RepeatedBreaker.<D> getCellBreaker().breakRepeated(this, getCellsAsList(), col);
}
this.cells.set(col, new MutableCell<D>(this, element));
}
return (MutableCell<D>) this.getValidCellAt(col);
}
 
private final void casse(Element element, int firstIndex, int repeat, boolean before) {
if (repeat > 0) {
final Element newElem = (Element) element.clone();
element.getParentElement().addContent(element.getParent().indexOf(element) + (before ? 0 : 1), newElem);
newElem.setAttribute("number-columns-repeated", repeat + "", this.getSheet().getTABLE());
final Cell<D> preCell = new Cell<D>(this, newElem);
for (int i = 0; i < repeat; i++) {
this.cells.set(firstIndex + i, preCell);
}
}
}
 
// rempli cette ligne avec autant de cellules vides qu'il faut
void columnCountChanged() {
final int diff = this.getSheet().getColumnCount() - this.cells.size();
void columnCountChanged(StyleStyleDesc<CellStyle> cellStyleDesc) {
final int diff = this.getSheet().getColumnCount() - getCellCount();
if (diff < 0) {
throw new IllegalStateException("should have used Table.removeColumn()");
} else if (diff > 0) {
final Element e = Cell.createEmpty(this.getSheet().getODDocument().getVersion(), diff);
this.getElement().addContent(e);
addCellElem(e);
this.ensureRoom(diff);
addCellElem(e, cellStyleDesc);
}
if (this.cells.size() != this.getSheet().getColumnCount())
throw new IllegalStateException();
assert this.getCellCount() == this.getSheet().getColumnCount();
}
 
void checkRemove(int firstIndex, int lastIndexExcl) {
if (lastIndexExcl > this.cells.size()) {
throw new IndexOutOfBoundsException(lastIndexExcl + " > " + this.cells.size());
if (lastIndexExcl > getCellCount()) {
throw new IndexOutOfBoundsException(lastIndexExcl + " > " + getCellCount());
}
if (!this.getCellAt(firstIndex).isValid())
throw new IllegalArgumentException("unable to remove covered cell at " + firstIndex);
}
 
// ATTN unsafe, must call checkRemove() first
void removeCells(int firstIndex, int lastIndexExcl) {
checkRemove(firstIndex, lastIndexExcl);
 
this.getMutableCellAt(firstIndex).unmerge();
 
// if lastIndex == size, nothing to do
if (lastIndexExcl < this.cells.size()) {
if (lastIndexExcl < getCellCount()) {
if (!this.getCellAt(lastIndexExcl - 1).isValid()) {
int currentCol = lastIndexExcl - 2;
// the covering cell is on this row since last cells of previous rows have been
182,8 → 199,13
for (int i = firstIndex; i < lastIndexExcl; i++) {
// ok to detach multiple times the same element (since repeated cells share the same XML
// element)
this.cells.remove(firstIndex).getElement().detach();
this.cells[firstIndex].getElement().detach();
}
final int movedCount = getCellCount() - lastIndexExcl;
System.arraycopy(this.cells, lastIndexExcl, this.cells, firstIndex, movedCount);
this.cells = Arrays.copyOfRange(this.cells, 0, firstIndex + movedCount);
this.cellCount = this.cells.length;
assert this.getCellCount() == this.getSheet().getColumnCount();
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/SpreadSheet.java
13,8 → 13,6
package org.openconcerto.openoffice.spreadsheet;
 
import static org.openconcerto.openoffice.ODPackage.RootElement.CONTENT;
import static org.openconcerto.openoffice.ODPackage.RootElement.STYLES;
import org.openconcerto.openoffice.ContentType;
import org.openconcerto.openoffice.ContentTypeVersioned;
import org.openconcerto.openoffice.ODDocument;
21,13 → 19,11
import org.openconcerto.openoffice.ODPackage;
import org.openconcerto.openoffice.OOUtils;
import org.openconcerto.openoffice.XMLFormatVersion;
import org.openconcerto.openoffice.XMLVersion;
import org.openconcerto.openoffice.spreadsheet.SheetTableModel.MutableTableModel;
import org.openconcerto.utils.Tuple2;
 
import java.awt.Point;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
39,7 → 35,6
 
import javax.swing.table.TableModel;
 
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.xpath.XPath;
49,14 → 44,20
*
* @author Sylvain
*/
public class SpreadSheet implements ODDocument {
public class SpreadSheet extends ODDocument {
 
public static SpreadSheet createFromFile(File f) throws IOException {
return create(new ODPackage(f));
return new ODPackage(f).getSpreadSheet();
}
 
public static SpreadSheet create(final ODPackage fd) {
return new SpreadSheet(fd.getDocument(CONTENT.getZipEntry()), fd.getDocument(STYLES.getZipEntry()), fd);
/**
* This method should be avoided, use {@link ODPackage#getSpreadSheet()}.
*
* @param fd a package.
* @return the spreadsheet.
*/
public static SpreadSheet get(final ODPackage fd) {
return fd.hasODDocument() ? fd.getSpreadSheet() : new SpreadSheet(fd);
}
 
public static SpreadSheet createEmpty(TableModel t) throws IOException {
65,7 → 66,7
 
public static SpreadSheet createEmpty(TableModel t, XMLFormatVersion ns) throws IOException {
final ContentTypeVersioned ct = ContentType.SPREADSHEET.getVersioned(ns.getXMLVersion());
final SpreadSheet spreadSheet = create(ct.createPackage(ns));
final SpreadSheet spreadSheet = ct.createPackage(ns).getSpreadSheet();
spreadSheet.getBody().addContent(Sheet.createEmpty(ns.getXMLVersion()));
spreadSheet.getSheet(0).merge(t, 0, 0, true);
return spreadSheet;
85,46 → 86,14
return SpreadSheet.createEmpty(t, ns).saveAs(f);
}
 
private final ODPackage originalFile;
private final Map<Element, Sheet> sheets;
 
public SpreadSheet(Document doc, Document styles) {
this(doc, styles, null);
}
 
private SpreadSheet(final Document doc, final Document styles, final ODPackage orig) {
if (orig != null) {
// ATTN OK because this is our private instance (see createFromFile())
this.originalFile = orig;
} else {
this.originalFile = new ODPackage();
}
this.originalFile.putFile("content.xml", doc);
if (styles != null)
this.originalFile.putFile("styles.xml", styles);
 
private SpreadSheet(final ODPackage orig) {
super(orig);
// map Sheet by XML elements so has not to depend on ordering or name
this.sheets = new HashMap<Element, Sheet>();
}
 
final Document getContent() {
return this.getPackage().getContent().getDocument();
}
 
@Override
public final XMLVersion getVersion() {
return this.getPackage().getVersion();
}
 
@Override
public XMLFormatVersion getFormatVersion() {
return this.getPackage().getFormatVersion();
}
 
private Element getBody() {
return ContentType.SPREADSHEET.getVersioned(getVersion()).getBody(getContent());
}
 
// ** from 8.3.1 Referencing Table Cells (just double the backslash for . and escape the $)
private static final String minCell = "\\$?([A-Z]+)\\$?([0-9]+)";
// added parens to capture cell address
334,17 → 303,4
parentElement.addContent(getContentIndex(toIndex), sheet.getElement());
// no need to update this.sheets since it doesn't depend on order
}
 
// *** Files
 
public File saveAs(File file) throws FileNotFoundException, IOException {
this.getPackage().setFile(file);
return this.getPackage().save();
}
 
@Override
public final ODPackage getPackage() {
return this.originalFile;
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/ColumnStyle.java
14,7 → 14,6
package org.openconcerto.openoffice.spreadsheet;
 
import org.openconcerto.openoffice.LengthUnit;
import org.openconcerto.openoffice.ODFrame;
import org.openconcerto.openoffice.ODPackage;
import org.openconcerto.openoffice.StyleProperties;
import org.openconcerto.openoffice.StyleStyle;
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/Cell.java
19,6 → 19,7
import org.openconcerto.openoffice.ODDocument;
import org.openconcerto.openoffice.ODValueType;
import org.openconcerto.openoffice.OOXML;
import org.openconcerto.openoffice.StyleDesc;
import org.openconcerto.openoffice.XMLFormatVersion;
import org.openconcerto.openoffice.XMLVersion;
import org.openconcerto.utils.CollectionUtils;
80,8 → 81,8
 
private final Row<D> row;
 
Cell(Row<D> parent, Element elem) {
super(parent.getODDocument(), elem, CellStyle.class);
Cell(Row<D> parent, Element elem, StyleDesc<CellStyle> styleDesc) {
super(parent.getODDocument(), elem, styleDesc);
this.row = parent;
}
 
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/TableCalcNode.java
15,6 → 15,7
 
import org.openconcerto.openoffice.ImmutableDocStyledNode;
import org.openconcerto.openoffice.ODDocument;
import org.openconcerto.openoffice.StyleDesc;
import org.openconcerto.openoffice.StyleStyle;
 
import org.jdom.Element;
34,7 → 35,12
super(parent, local, styleClass);
}
 
protected TableCalcNode(D parent, Element local, StyleDesc<S> styleDesc) {
super(parent, local, styleDesc);
}
 
protected final Namespace getTABLE() {
return this.getODDocument().getVersion().getTABLE();
// a lot faster than asking to the version of our document
return this.getElement().getNamespace();
}
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/style/RelationalOperator.java
New file
0,0 → 1,100
/*
* 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.openoffice.style;
 
import org.openconcerto.utils.CompareUtils;
 
import java.util.HashMap;
import java.util.Map;
 
public enum RelationalOperator {
LT("<") {
@Override
protected boolean evaluate(int i) {
return i < 0;
}
},
GT(">") {
@Override
protected boolean evaluate(int i) {
return i > 0;
}
},
LE("<=") {
@Override
protected boolean evaluate(int i) {
return i <= 0;
}
},
GE(">=") {
@Override
protected boolean evaluate(int i) {
return i >= 0;
}
},
EQ("=") {
@Override
protected boolean evaluate(int i) {
return i == 0;
}
},
NE("!=") {
@Override
protected boolean evaluate(int i) {
return !EQ.evaluate(i);
}
};
 
private final String s;
 
private RelationalOperator(final String s) {
this.s = s;
}
 
public final String asString() {
return this.s;
}
 
public final boolean compare(final Object o1, final Object o2) {
return this.evaluate(CompareUtils.compare(o1, o2));
}
 
protected abstract boolean evaluate(int i);
 
/**
* Regular expression with all operators.
*
* <pre>
* &lt;|&gt;|&lt;=|&gt;=|=|!=
* </pre>
*/
public static final String OR_PATTERN;
private static final Map<String, RelationalOperator> instances;
static {
instances = new HashMap<String, RelationalOperator>();
final StringBuilder sb = new StringBuilder(32);
for (final RelationalOperator op : values()) {
instances.put(op.s, op);
sb.append(op.asString());
sb.append('|');
}
// remove last |
sb.setLength(sb.length() - 1);
OR_PATTERN = sb.toString();
}
 
public static RelationalOperator getInstance(String op) {
return instances.get(op);
}
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/style/data/TextStyle.java
14,6 → 14,7
package org.openconcerto.openoffice.style.data;
 
import org.openconcerto.openoffice.ODPackage;
import org.openconcerto.openoffice.ODValueType;
import org.openconcerto.openoffice.XMLVersion;
import org.openconcerto.openoffice.spreadsheet.CellStyle;
 
33,10 → 34,15
};
 
public TextStyle(final ODPackage pkg, Element elem) {
super(pkg, elem, Object.class);
super(pkg, elem, ODValueType.STRING);
}
 
@Override
protected String convertNonNull(Object o) {
return o.toString();
}
 
@Override
public String format(Object o, CellStyle defaultStyle, boolean lenient) {
final Namespace numberNS = this.getElement().getNamespace();
final StringBuilder sb = new StringBuilder();
/trunk/OpenConcerto/src/org/openconcerto/openoffice/style/data/NumberStyle.java
13,13 → 13,19
package org.openconcerto.openoffice.style.data;
 
import org.openconcerto.openoffice.ODEpoch;
import org.openconcerto.openoffice.ODPackage;
import org.openconcerto.openoffice.ODValueType;
import org.openconcerto.openoffice.XMLVersion;
import org.openconcerto.openoffice.spreadsheet.CellStyle;
import org.openconcerto.openoffice.spreadsheet.MutableCell;
 
import java.util.Calendar;
import java.util.Date;
import java.util.List;
 
import javax.xml.datatype.Duration;
 
import org.jdom.Element;
import org.jdom.Namespace;
 
33,11 → 39,41
}
};
 
public static final Number toNumber(Object value, ODEpoch epoch) {
final Number res;
if (value instanceof Number) {
res = (Number) value;
} else if (value instanceof Boolean) {
res = ((Boolean) value).booleanValue() ? 1 : 0;
} else if ((value instanceof Duration || value instanceof Date || value instanceof Calendar)) {
if (value instanceof Duration) {
res = epoch.getDays((Duration) value);
} else {
final Calendar cal;
if (value instanceof Calendar) {
cal = (Calendar) value;
} else {
cal = Calendar.getInstance();
cal.setTime((Date) value);
}
res = epoch.getDays(cal);
}
} else {
res = null;
}
return res;
}
 
public NumberStyle(final ODPackage pkg, Element elem) {
super(pkg, elem, Number.class);
super(pkg, elem, ODValueType.FLOAT);
}
 
@Override
protected Number convertNonNull(Object value) {
return toNumber(value, getEpoch());
}
 
@Override
public String format(Object o, CellStyle defaultStyle, boolean lenient) {
final Number n = (Number) o;
final Namespace numberNS = this.getElement().getNamespace();
/trunk/OpenConcerto/src/org/openconcerto/openoffice/style/data/DataStyle.java
14,7 → 14,9
package org.openconcerto.openoffice.style.data;
 
import org.openconcerto.openoffice.Log;
import org.openconcerto.openoffice.ODEpoch;
import org.openconcerto.openoffice.ODPackage;
import org.openconcerto.openoffice.ODValueType;
import org.openconcerto.openoffice.Style;
import org.openconcerto.openoffice.StyleDesc;
import org.openconcerto.openoffice.StyleProperties;
74,24 → 76,62
Arrays.asList("presentation:date-time-decl", "style:style", "text:creation-date", "text:creation-time", "text:database-display", "text:date", "text:editing-duration",
"text:expression", "text:meta-field", "text:modification-date", "text:modification-time", "text:print-date", "text:print-time", "text:table-formula", "text:time",
"text:user-defined", "text:user-field-get", "text:user-field-input", "text:variable-get", "text:variable-input", "text:variable-set"));
this.getRefElementsMap().put("style:apply-style-name", "style:map");
}
}
 
// type accepted by #format()
private final Class<?> type;
private final ODValueType type;
private StyleTextProperties textProps;
 
protected DataStyle(final ODPackage pkg, Element elem, final Class<?> type) {
protected DataStyle(final ODPackage pkg, Element elem, final ODValueType type) {
super(pkg, elem);
this.type = type;
}
 
protected final Class<?> getDataType() {
public final ODValueType getDataType() {
return this.type;
}
 
public final ODEpoch getEpoch() {
return this.getPackage().getODDocument().getEpoch();
}
 
/**
* Convert the passed object to something that {@link #format(Object, CellStyle, boolean)} can
* accept.
*
* @param o the object to convert.
* @return an object that can be formatted, <code>null</code> if <code>o</code> cannot be
* converted.
* @throws NullPointerException if <code>o</code> is <code>null</code>.
* @see #canFormat(Class)
*/
public final Object convert(final Object o) throws NullPointerException {
if (o == null)
throw new NullPointerException();
 
final Object res;
if (this.canFormat(o.getClass()))
res = o;
else
res = this.convertNonNull(o);
assert res == null || this.canFormat(res.getClass());
return res;
}
 
// o is not null and canFormat(o.getClass()) is false
// return null if o cannot be converted
protected abstract Object convertNonNull(Object o);
 
/**
* Whether instances of the passed class can be {@link #format(Object, CellStyle, boolean)
* formatted}.
*
* @param toFormat the class.
* @return <code>true</code> if instances of <code>toFormat</code> can be formatted.
*/
public final boolean canFormat(Class<?> toFormat) {
return this.getDataType().isAssignableFrom(toFormat);
return this.getDataType().canFormat(toFormat);
}
 
public final String getTitle() {
/trunk/OpenConcerto/src/org/openconcerto/openoffice/style/data/CurrencyStyle.java
14,6 → 14,7
package org.openconcerto.openoffice.style.data;
 
import org.openconcerto.openoffice.ODPackage;
import org.openconcerto.openoffice.ODValueType;
import org.openconcerto.openoffice.XMLVersion;
import org.openconcerto.openoffice.spreadsheet.CellStyle;
 
34,10 → 35,15
};
 
public CurrencyStyle(final ODPackage pkg, Element elem) {
super(pkg, elem, Number.class);
super(pkg, elem, ODValueType.CURRENCY);
}
 
@Override
protected Object convertNonNull(Object o) {
return NumberStyle.toNumber(o, getEpoch());
}
 
@Override
public String format(Object o, CellStyle defaultStyle, boolean lenient) {
final Number n = (Number) o;
final Namespace numberNS = this.getElement().getNamespace();
/trunk/OpenConcerto/src/org/openconcerto/openoffice/style/data/DateStyle.java
14,9 → 14,11
package org.openconcerto.openoffice.style.data;
 
import org.openconcerto.openoffice.ODPackage;
import org.openconcerto.openoffice.ODValueType;
import org.openconcerto.openoffice.StyleProperties;
import org.openconcerto.openoffice.XMLVersion;
import org.openconcerto.openoffice.spreadsheet.CellStyle;
import org.openconcerto.utils.convertor.NumberConvertor;
 
import java.math.BigDecimal;
import java.text.DecimalFormat;
24,6 → 26,7
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
 
35,9 → 38,9
public class DateStyle extends DataStyle {
 
// see http://download.oracle.com/javase/6/docs/technotes/guides/intl/calendar.doc.html
private static final Locale BUDDHIST_LOCALE = new Locale("th", "TH");
private static final Locale JAPANESE_LOCALE = new Locale("ja", "JP", "JP");
private static final Locale GREGORIAN_LOCALE = new Locale("fr", "FR");
private static final Calendar BUDDHIST_CAL = Calendar.getInstance(new Locale("th", "TH"));
private static final Calendar JAPANESE_CAL = Calendar.getInstance(new Locale("ja", "JP", "JP"));
private static final Calendar GREGORIAN_CAL = new GregorianCalendar();
 
public static final DataStyleDesc<DateStyle> DESC = new DataStyleDesc<DateStyle>(DateStyle.class, XMLVersion.OD, "date-style", "N") {
@Override
63,17 → 66,17
return res;
}
 
private static final Locale getCalendarLocale(final Element elem, Locale defaultLocale) {
final Locale res;
private static final Calendar getCalendar(final Element elem, Calendar defaultCal) {
final Calendar res;
final String cal = elem.getAttributeValue("calendar", elem.getNamespace());
if (cal == null) {
res = defaultLocale;
res = defaultCal;
} else if ("buddhist".equals(cal)) {
res = BUDDHIST_LOCALE;
res = BUDDHIST_CAL;
} else if ("gengou".equals(cal)) {
res = JAPANESE_LOCALE;
res = JAPANESE_CAL;
} else if ("gregorian".equals(cal)) {
res = GREGORIAN_LOCALE;
res = GREGORIAN_CAL;
} else {
throw new IllegalArgumentException("Unsupported calendar : " + cal);
}
80,16 → 83,6
return res;
}
 
private static final Locale getCalendarLocale(final Locale locale) {
final Locale res;
if (locale.equals(BUDDHIST_LOCALE) || locale.equals(JAPANESE_LOCALE)) {
res = locale;
} else {
res = GREGORIAN_LOCALE;
}
return res;
}
 
static String formatSecondFraction(final Locale styleLocale, final BigDecimal seconds, final int decPlaces) {
if (decPlaces > 0) {
final DecimalFormat decFormat = new DecimalFormat();
106,18 → 99,35
}
 
public DateStyle(final ODPackage pkg, Element elem) {
super(pkg, elem, Date.class);
super(pkg, elem, ODValueType.DATE);
}
 
@Override
protected Object convertNonNull(Object o) {
if (o instanceof Number)
return getEpoch().getDate(NumberConvertor.toBigDecimal((Number) o));
else
return null;
}
 
private final void format(final StringBuilder res, final StringBuilder pattern, final Locale styleLocale, final Calendar currentCalendar, final Date d) {
if (pattern.length() > 0) {
final SimpleDateFormat fmt = new SimpleDateFormat(pattern.toString(), styleLocale);
pattern.setLength(0);
fmt.setCalendar((Calendar) currentCalendar.clone());
res.append(fmt.format(d));
}
}
 
@Override
public String format(Object o, CellStyle defaultStyle, boolean lenient) {
final Date d = o instanceof Calendar ? ((Calendar) o).getTime() : (Date) o;
final Namespace numberNS = this.getElement().getNamespace();
final Locale styleLocale = getLocale(getElement());
final Locale styleCalendarLocale = getCalendarLocale(styleLocale);
final Calendar styleCalendar = Calendar.getInstance(styleLocale);
final StringBuilder res = new StringBuilder();
 
Locale currentCalendarLocale = styleCalendarLocale;
Calendar currentCalendar = styleCalendar;
final StringBuilder sb = new StringBuilder();
 
@SuppressWarnings("unchecked")
124,14 → 134,11
final List<Element> children = this.getElement().getChildren();
for (final Element elem : children) {
if (elem.getNamespace().equals(numberNS)) {
final Locale calendarLocaleElem = getCalendarLocale(elem, styleCalendarLocale);
if (!calendarLocaleElem.equals(currentCalendarLocale)) {
if (sb.length() > 0) {
res.append(new SimpleDateFormat(sb.toString(), currentCalendarLocale).format(d));
sb.setLength(0);
final Calendar calendarLocaleElem = getCalendar(elem, styleCalendar);
if (!calendarLocaleElem.equals(currentCalendar)) {
format(res, sb, styleLocale, currentCalendar, d);
currentCalendar = calendarLocaleElem;
}
currentCalendarLocale = calendarLocaleElem;
}
 
if (elem.getName().equals("text")) {
DataStyle.addStringLiteral(sb, elem.getText());
140,7 → 147,11
} else if (elem.getName().equals("year")) {
sb.append(isShort(elem) ? "yy" : "yyyy");
} else if (elem.getName().equals("quarter")) {
final int quarter = Calendar.getInstance(GREGORIAN_LOCALE).get(Calendar.MONTH) / 3 + 1;
final Calendar cal = (Calendar) currentCalendar.clone();
cal.setTime(d);
final double quarterLength = cal.getActualMaximum(Calendar.MONTH) / 4.0;
final int quarter = (int) (cal.get(Calendar.MONTH) / quarterLength + 1);
assert quarter >= 1 && quarter <= 4;
// TODO localize and honor short/long style
reportError("Quarters are not localized", lenient);
DataStyle.addStringLiteral(sb, isShort(elem) ? "Q" + quarter : "Q" + quarter);
184,6 → 195,7
}
}
}
return new SimpleDateFormat(sb.toString(), currentCalendarLocale).format(d);
format(res, sb, styleLocale, currentCalendar, d);
return res.toString();
}
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/style/data/TimeStyle.java
14,12 → 14,16
package org.openconcerto.openoffice.style.data;
 
import org.openconcerto.openoffice.ODPackage;
import org.openconcerto.openoffice.ODValueType;
import org.openconcerto.openoffice.StyleProperties;
import org.openconcerto.openoffice.XMLVersion;
import org.openconcerto.openoffice.spreadsheet.CellStyle;
import org.openconcerto.utils.TimeUtils;
import org.openconcerto.utils.convertor.NumberConvertor;
 
import java.math.BigDecimal;
import java.text.DateFormatSymbols;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
 
48,12 → 52,21
}
 
public TimeStyle(final ODPackage pkg, Element elem) {
super(pkg, elem, Duration.class);
super(pkg, elem, ODValueType.TIME);
}
 
@Override
protected Duration convertNonNull(Object o) {
if (o instanceof Number) {
return TimeUtils.timePartToDuration(getEpoch().getDate(NumberConvertor.toBigDecimal((Number) o)));
} else {
return null;
}
}
 
@Override
public String format(Object o, CellStyle defaultStyle, boolean lenient) {
final Duration d = (Duration) o;
final Duration d = o instanceof Calendar ? TimeUtils.timePartToDuration((Calendar) o) : (Duration) o;
final Namespace numberNS = this.getElement().getNamespace();
final StringBuilder sb = new StringBuilder();
 
/trunk/OpenConcerto/src/org/openconcerto/openoffice/style/data/BooleanStyle.java
14,10 → 14,15
package org.openconcerto.openoffice.style.data;
 
import org.openconcerto.openoffice.ODPackage;
import org.openconcerto.openoffice.ODValueType;
import org.openconcerto.openoffice.XMLVersion;
import org.openconcerto.openoffice.spreadsheet.CellStyle;
import org.openconcerto.utils.NumberUtils;
 
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
 
import org.jdom.Element;
import org.jdom.Namespace;
32,13 → 37,47
}
};
 
public static final Boolean toBoolean(Object o) {
if (o instanceof Boolean)
return (Boolean) o;
else if (o instanceof Number)
return Boolean.valueOf(!NumberUtils.areNumericallyEqual(0, (Number) o));
else
return null;
}
 
private static final Map<String, String> trues = new HashMap<String, String>(), falses = new HashMap<String, String>();
 
private static final void add(final String iso3, final String trueS, final String falseS) {
if (trueS == null || falseS == null)
throw new NullPointerException();
trues.put(iso3, trueS);
falses.put(iso3, falseS);
}
 
static {
add(Locale.FRENCH.getISO3Language(), "VRAI", "FAUX");
add(Locale.ENGLISH.getISO3Language(), "TRUE", "FALSE");
add(Locale.GERMAN.getISO3Language(), "WAHR", "FALSCH");
add(Locale.ITALY.getISO3Language(), "VERO", "FALSO");
add("spa", "VERDADERO", "FALSO");
add("por", "VERDADEIRO", "FALSO");
}
 
public BooleanStyle(final ODPackage pkg, Element elem) {
super(pkg, elem, Boolean.class);
super(pkg, elem, ODValueType.BOOLEAN);
}
 
@Override
protected Boolean convertNonNull(Object o) {
return toBoolean(o);
}
 
@Override
public String format(Object o, CellStyle defaultStyle, boolean lenient) {
final Boolean b = (Boolean) o;
final Namespace numberNS = this.getElement().getNamespace();
final Locale styleLocale = DateStyle.getLocale(getElement());
final StringBuilder sb = new StringBuilder();
@SuppressWarnings("unchecked")
final List<Element> children = this.getElement().getChildren();
47,12 → 86,20
if (elem.getName().equals("text")) {
sb.append(elem.getText());
} else if (elem.getName().equals("boolean")) {
// TODO localize
// TODO localize more
final String s;
final String iso3Lang = styleLocale.getISO3Language();
final String localized = b.booleanValue() ? trues.get(iso3Lang) : falses.get(iso3Lang);
if (localized != null) {
s = localized;
} else {
reportError("Boolean not localized", lenient);
sb.append(o.toString());
s = b.toString();
}
sb.append(s);
}
}
}
return sb.toString();
}
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/style/data/PercentStyle.java
14,6 → 14,7
package org.openconcerto.openoffice.style.data;
 
import org.openconcerto.openoffice.ODPackage;
import org.openconcerto.openoffice.ODValueType;
import org.openconcerto.openoffice.XMLVersion;
import org.openconcerto.openoffice.spreadsheet.CellStyle;
 
33,10 → 34,15
};
 
public PercentStyle(final ODPackage pkg, Element elem) {
super(pkg, elem, Number.class);
super(pkg, elem, ODValueType.PERCENTAGE);
}
 
@Override
protected Object convertNonNull(Object o) {
return NumberStyle.toNumber(o, getEpoch());
}
 
@Override
public String format(Object o, CellStyle defaultStyle, boolean lenient) {
final Number n = (Number) o;
final Namespace numberNS = this.getElement().getNamespace();
/trunk/OpenConcerto/src/org/openconcerto/openoffice/ODEpoch.java
New file
0,0 → 1,154
/*
* 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.openoffice;
 
import org.openconcerto.utils.TimeUtils;
 
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TimeZone;
 
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.Duration;
 
/**
* The null date of an OpenDocument.
*/
public final class ODEpoch {
 
static private final BigDecimal MS_PER_DAY = BigDecimal.valueOf(24l * 60l * 60l * 1000l);
static private final DateFormat DATE_FORMAT;
static private final ODEpoch DEFAULT_EPOCH;
static private final Map<String, ODEpoch> cache = new LinkedHashMap<String, ODEpoch>(4, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, ODEpoch> eldest) {
return this.size() > 16;
}
};
 
static {
DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
DATE_FORMAT.setCalendar(Calendar.getInstance(TimeZone.getTimeZone("UTC")));
try {
DEFAULT_EPOCH = new ODEpoch("1899-12-30");
} catch (ParseException e) {
// shouldn't happen since string are constants
throw new IllegalStateException(e);
}
}
 
static public final ODEpoch getDefaultEpoch() {
return DEFAULT_EPOCH;
}
 
static public final ODEpoch getInstance(String date) throws ParseException {
if (date == null || date.equals(DEFAULT_EPOCH.getDateString())) {
return DEFAULT_EPOCH;
} else {
ODEpoch res = cache.get(date);
if (res == null) {
res = new ODEpoch(date);
cache.put(date, res);
}
return res;
}
}
 
static private final Calendar parse(final String date) throws ParseException {
final Calendar cal = (Calendar) DATE_FORMAT.getCalendar().clone();
cal.setTime(DATE_FORMAT.parse(date));
return cal;
}
 
private final String dateString;
private final Calendar epochUTC;
 
private ODEpoch(final String date) throws ParseException {
this.dateString = date;
this.epochUTC = parse(date);
assert this.epochUTC.getTimeZone().equals(DATE_FORMAT.getTimeZone());
}
 
public final String getDateString() {
return this.dateString;
}
 
public final Calendar getCalendar() {
return (Calendar) this.epochUTC.clone();
}
 
private final Calendar getDate(final Duration duration) {
// If we don't use the UTC calendar, we go from 0:00 (the epoch), we add n days, we get
// to the last Sunday of March at 0:00, so far so good, but then we add say 10 hours, thus
// going through the change of offset, and arriving at 11:00.
final Calendar res = getCalendar();
duration.addTo(res);
return res;
}
 
public final Duration normalizeToDays(final Duration dur) {
final Duration res = dur.getYears() == 0 && dur.getMonths() == 0 ? dur : getDuration(getDays(dur));
assert res.getYears() == 0 && res.getMonths() == 0;
return res;
}
 
public final Duration normalizeToHours(final Duration dur) {
final Duration durationInDays = normalizeToDays(dur);
final BigInteger days = (BigInteger) durationInDays.getField(DatatypeConstants.DAYS);
final BigInteger hours = ((BigInteger) durationInDays.getField(DatatypeConstants.HOURS)).add(days.multiply(BigInteger.valueOf(24)));
return TimeUtils.getTypeFactory().newDuration(days.signum() >= 0, BigInteger.ZERO, BigInteger.ZERO, BigInteger.ZERO, hours, (BigInteger) durationInDays.getField(DatatypeConstants.MINUTES),
(BigDecimal) durationInDays.getField(DatatypeConstants.SECONDS));
}
 
public final BigDecimal getDays(final Duration duration) {
return getDays(getDate(duration));
}
 
public final BigDecimal getDays(final Calendar cal) {
// can't use Duration.normalizeWith() since it doesn't handle DST, i.e. going from winter to
// summer at midnight will miss a day
final long diff = TimeUtils.normalizeLocalTime(cal) - this.epochUTC.getTimeInMillis();
return BigDecimal.valueOf(diff).divide(MS_PER_DAY, MathContext.DECIMAL128);
}
 
public final Calendar getDate(final BigDecimal days) {
return getDate(days, Calendar.getInstance());
}
 
public final Calendar getDate(final BigDecimal days, final Calendar res) {
final Calendar utcCal = getDate(getDuration(days));
// can't use getTimeZone().getOffset() since we have no idea for the UTC time
return TimeUtils.copyLocalTime(utcCal, res);
}
 
private final static Duration getDuration(final BigDecimal days) {
final BigDecimal posDays = days.abs();
final BigInteger wholeDays = posDays.toBigInteger().abs();
final BigDecimal hours = posDays.subtract(new BigDecimal(wholeDays)).multiply(BigDecimal.valueOf(24));
final BigInteger wholeHours = hours.toBigInteger();
final BigDecimal minutes = hours.subtract(new BigDecimal(wholeHours)).multiply(BigDecimal.valueOf(60));
final BigInteger wholeMinutes = minutes.toBigInteger();
// round to 16 digits, i.e. 10^-14 seconds is more than enough
// it is required since the number coming from getDays() might have been rounded
final BigDecimal seconds = minutes.subtract(new BigDecimal(wholeMinutes)).multiply(BigDecimal.valueOf(60)).round(MathContext.DECIMAL64);
return TimeUtils.getTypeFactory().newDuration(days.signum() >= 0, BigInteger.ZERO, BigInteger.ZERO, wholeDays, wholeHours, wholeMinutes, seconds);
}
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/ContentTypeVersioned.java
128,7 → 128,7
*
* @param version the version.
* @return the body of the created document.
* @see #createContent(boolean)
* @see #createContent(XMLFormatVersion, boolean)
*/
public final Element createContent(final XMLFormatVersion version) {
return this.createContent(version, false);
141,12 → 141,12
* @param singleXML <code>true</code> for {@link RootElement#SINGLE_CONTENT}, <code>false</code>
* for {@link RootElement#CONTENT}.
* @return the body of the created document.
* @see #createPackage()
* @see #createPackage(XMLFormatVersion)
*/
public Element createContent(final XMLFormatVersion version, final boolean singleXML) {
checkVersion(version);
final RootElement rootElement = singleXML ? RootElement.SINGLE_CONTENT : RootElement.CONTENT;
final Document doc = rootElement.createDocument(getVersion(), version.getOfficeVersion());
final Document doc = rootElement.createDocument(version);
final Namespace officeNS = getVersion().getOFFICE();
setType(doc, rootElement, officeNS);
// don't forget that, otherwise OO crash
170,7 → 170,7
}
 
public void setType(final Document doc) {
this.setType(doc, RootElement.fromElementName(doc.getRootElement().getName()), getVersion().getOFFICE());
this.setType(doc, RootElement.fromDocument(doc), getVersion().getOFFICE());
}
 
// not safe
199,7 → 199,7
public Document createStyles(final XMLFormatVersion version) {
checkVersion(version);
final Namespace officeNS = getVersion().getOFFICE();
final Document styles = RootElement.STYLES.createDocument(getVersion(), version.getOfficeVersion());
final Document styles = RootElement.STYLES.createDocument(version);
// some consumers demand empty children
styles.getRootElement().addContent(asList(new Element("styles", officeNS), new Element("automatic-styles", officeNS), new Element("master-styles", officeNS)));
return styles;
212,12 → 212,7
* @return a new package with minimal {@link RootElement#CONTENT} and {@link RootElement#STYLES}
*/
public ODPackage createPackage(final XMLFormatVersion version) {
final ODPackage res = new ODPackage();
res.putFile(RootElement.CONTENT.getZipEntry(), this.createContent(version, false).getDocument());
res.putFile(RootElement.STYLES.getZipEntry(), this.createStyles(version));
// add mimetype since ODPackage cannot find out about templates
res.putFile("mimetype", this.getMimeType().getBytes(ODPackage.MIMETYPE_ENC));
return res;
return ODPackage.createFromDocuments(this, this.createContent(version, false).getDocument(), this.createStyles(version), null, null);
}
 
// *** static
238,6 → 233,27
return null;
}
 
static public ContentTypeVersioned fromMime(byte[] mime) {
return fromMime(new String(mime, ODPackage.MIMETYPE_ENC));
}
 
static ContentTypeVersioned fromContent(final ODXMLDocument content) {
final ContentTypeVersioned res;
final XMLVersion vers = content.getVersion();
if (vers.equals(XMLVersion.OOo)) {
final Element contentRoot = content.getDocument().getRootElement();
final String docClass = contentRoot.getAttributeValue("class", contentRoot.getNamespace("office"));
res = ContentTypeVersioned.fromClass(docClass);
} else if (vers.equals(XMLVersion.OD)) {
final Element bodyChild = (Element) content.getChild("body").getChildren().get(0);
res = ContentTypeVersioned.fromBody(bodyChild.getName());
} else {
throw new IllegalStateException("Unknown content version : " + vers);
}
assert !res.isTemplate() : "template status cannot be inferred from content";
return res;
}
 
static ContentTypeVersioned fromClass(String name) {
return fromShortName(XMLVersion.OOo, name);
}
/trunk/OpenConcerto/src/org/openconcerto/ui/preferences/JavaPrefPreferencePanel.java
21,6 → 21,7
import org.openconcerto.utils.checks.ValidObject;
import org.openconcerto.utils.checks.ValidState;
 
import java.awt.BorderLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
47,12 → 48,16
private final ValidChangeSupport validSupp;
 
public JavaPrefPreferencePanel(final String title, final Preferences prefs) {
super(new BorderLayout());
this.title = title;
this.prefs = prefs;
this.layouter = new AutoLayouter(this);
this.views = new HashSet<PrefView<?>>();
this.modified = false;
this.validSupp = new ValidChangeSupport(this);
// anchor content at the top
final JPanel content = new JPanel();
this.add(content, BorderLayout.PAGE_START);
this.layouter = new AutoLayouter(content);
}
 
public final void setPrefs(Preferences prefs) {
/trunk/OpenConcerto/src/org/openconcerto/ui/preferences/AbstractProps.java
128,8 → 128,11
 
public void load() {
final File file = new File(getPropsFileName());
if (!file.exists())
System.out.println("Loading properties from " + file.getAbsolutePath());
if (!file.exists()) {
System.out.println("Warning: " + file.getAbsolutePath() + " does not exist");
return;
}
BufferedInputStream bufferedInputStream = null;
try {
final FileInputStream fileInputStream = new FileInputStream(file);
/trunk/OpenConcerto/src/org/openconcerto/ui/FormLayouter.java
151,6 → 151,7
 
final int realWidth = w * CELL_WIDTH - 1;
JPanel p = new JPanel();
p.setOpaque(false);
p.setLayout(new GridLayout());
p.setBorder(BorderFactory.createTitledBorder(desc));
p.add(comp);
/trunk/OpenConcerto/src/org/openconcerto/ui/JDate.java
18,10 → 18,12
import org.openconcerto.utils.checks.ValidListener;
import org.openconcerto.utils.checks.ValidState;
 
import java.awt.Component;
import java.beans.PropertyChangeListener;
import java.util.Calendar;
import java.util.Date;
 
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.text.JTextComponent;
 
63,6 → 65,16
this.resetValue();
}
 
@Override
public void updateUI() {
super.updateUI();
// can't change BasicDatePickerUI behavior, so do it here
for (final Component child : this.getComponents()) {
if (child instanceof JButton)
((JComponent) child).setOpaque(false);
}
}
 
public final void resetValue() {
if (this.fillWithCurrentDate) {
this.setValue(new Date());
/trunk/OpenConcerto/src/org/openconcerto/ui/table/AlternateTableCellRenderer.java
35,7 → 35,7
*/
public class AlternateTableCellRenderer extends TableCellRendererDecorator {
 
public static final Color COLOR_LIGHT_GRAY = new Color(243, 243, 243);
public static final Color COLOR_LIGHT_GRAY = new Color(243, 247, 251);
public static final Color DEFAULT_BG_COLOR = Color.WHITE;
/** Default map from white to gray */
public static final Map<Color, Color> DEFAULT_MAP = Collections.singletonMap(DEFAULT_BG_COLOR, COLOR_LIGHT_GRAY);
/trunk/OpenConcerto/src/org/openconcerto/ui/component/ITextSelectorCompletionThread.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/ui/component/ITextSelectorPopup.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/ui/component/ITextSelector.java
File deleted
/trunk/OpenConcerto/src/org/openconcerto/ui/component/MutableListComboPopupListener.java
29,6 → 29,7
import java.util.HashSet;
import java.util.Set;
 
import javax.swing.AbstractAction;
import javax.swing.JComboBox;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
130,7 → 131,15
MutableListComboPopupListener.this.combo.removeCurrentText();
}
});
 
if (this.combo.canReload()) {
this.popup.add(new JMenuItem(new AbstractAction("Recharger la liste") {
public void actionPerformed(ActionEvent e) {
MutableListComboPopupListener.this.combo.reload();
}
}));
}
}
// popups are never closed in a JComboBox (except when choosing a menu item)
if (SwingThreadUtils.getAncestorOrSelf(JComboBox.class, this.combo.getPopupComp()) != null)
addPopup(this.popup);
/trunk/OpenConcerto/src/org/openconcerto/ui/component/ITextComboCache.java
18,9 → 18,16
public interface ITextComboCache {
 
/**
* Can this cache be used.
*
* @return <code>true</code> if this cache can be used.
*/
public boolean isValid();
 
/**
* Force le chargement du cache (en synchrone) et le renvoi
*/
public List<String> loadCache();
public List<String> loadCache(final boolean readCache);
 
/**
* Retourne les éléments du cache, et le charge de manière synchrone si n'a jamais été chargé
/trunk/OpenConcerto/src/org/openconcerto/ui/component/ImmutableITextComboCache.java
29,6 → 29,11
}
 
@Override
public boolean isValid() {
return true;
}
 
@Override
public final void addToCache(String string) {
throw new UnsupportedOperationException();
}
40,11 → 45,11
 
@Override
public final List<String> getCache() {
return this.loadCache();
return this.loadCache(true);
}
 
@Override
public List<String> loadCache() {
public List<String> loadCache(final boolean dsCache) {
return this.cache;
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/component/MutableListCombo.java
28,6 → 28,10
 
void removeCurrentText();
 
boolean canReload();
 
void reload();
 
/**
* The component and where the popup should appear.
*
/trunk/OpenConcerto/src/org/openconcerto/ui/component/IComboCacheListModel.java
New file
0,0 → 1,125
/*
* 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;
 
import org.openconcerto.ui.component.combo.ISearchableCombo;
import org.openconcerto.utils.change.CollectionChangeEvent;
import org.openconcerto.utils.change.IListDataEvent;
import org.openconcerto.utils.model.DefaultIMutableListModel;
import org.openconcerto.utils.model.Reloadable;
 
import java.util.Collection;
import java.util.List;
 
import javax.swing.SwingWorker;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
 
/**
* An IMutableListModel with items from {@link ITextComboCache}.
*
* @author Sylvain CUAZ
* @see #load(Runnable)
*/
public class IComboCacheListModel extends DefaultIMutableListModel<String> implements Reloadable {
 
private final ITextComboCache cache;
private final ListDataListener l;
 
public IComboCacheListModel(final ITextComboCache c) {
this.cache = c;
this.l = new ListDataListener() {
 
@SuppressWarnings("unchecked")
public void contentsChanged(ListDataEvent e) {
// selection change, see DefaultIMutableListModel#setSelectedItem()
if (e.getIndex0() < 0)
return;
 
final CollectionChangeEvent evt = ((IListDataEvent) e).getCollectionChangeEvent();
this.remove(evt);
this.add(evt.getItemsAdded());
}
 
public void intervalAdded(ListDataEvent e) {
this.add(getList().subList(e.getIndex0(), e.getIndex1() + 1));
}
 
public void intervalRemoved(ListDataEvent e) {
this.remove(((IListDataEvent) e).getCollectionChangeEvent());
}
 
private void add(Collection<String> toAdd) {
for (final String s : toAdd) {
IComboCacheListModel.this.cache.addToCache(s);
}
}
 
@SuppressWarnings("unchecked")
private void remove(CollectionChangeEvent evt) {
for (final String s : (Collection<String>) evt.getItemsRemoved())
IComboCacheListModel.this.cache.deleteFromCache(s);
}
};
}
 
public final void load(final Runnable r, final boolean readCache) {
if (this.cache.isValid()) {
new SwingWorker<List<String>, Object>() {
 
@Override
protected List<String> doInBackground() throws Exception {
return IComboCacheListModel.this.cache.loadCache(readCache);
}
 
@Override
protected void done() {
// don't remove and add from the cache, items just came from it
removeListDataListener(IComboCacheListModel.this.l);
removeAllElements();
try {
addAll(get());
} catch (Exception e1) {
// tant pis, pas de cache
e1.printStackTrace();
}
addListDataListener(IComboCacheListModel.this.l);
if (r != null)
r.run();
}
 
}.execute();
}
}
 
@Override
public void reload() {
this.load(null, false);
}
 
/**
* Load this and only afterwards call
* {@link ISearchableCombo#initCache(org.openconcerto.utils.model.IListModel)}.
*
* @param combo the combo to initialise.
*/
public void initCacheLater(final ISearchableCombo<String> combo) {
this.load(new Runnable() {
@Override
public void run() {
combo.initCache(IComboCacheListModel.this);
}
}, true);
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/component/ITextCombo.java
61,7 → 61,7
 
private final String defaultValue;
private final ComboLockedMode locked;
private final ValueChangeSupport supp;
private final ValueChangeSupport<String> supp;
protected final boolean autoComplete;
protected boolean keyPressed;
private boolean completing;
181,9 → 181,19
public void removeCurrentText() {
ITextCombo.this.removeCurrentText();
}
 
@Override
public boolean canReload() {
return true;
}
 
@Override
public void reload() {
ITextCombo.this.loadCache(true);
}
}).listen();
 
this.loadCache();
this.loadCache(false);
 
// ATTN marche car locked est final
if (!this.isLocked()) {
285,15 → 295,16
// *** cache
 
// charge les elements de completion si besoin
private synchronized final void loadCache() {
private synchronized final void loadCache(final boolean force) {
if (!this.cacheLoading) {
this.modeToSet = this.isEnabled();
this.setEnabled(false);
this.objToSelect = this.getValue();
this.cacheLoading = true;
final SwingWorker sw = new SwingWorker<List<String>, Object>() {
final SwingWorker<List<String>, Object> sw = new SwingWorker<List<String>, Object>() {
@Override
protected List<String> doInBackground() throws Exception {
return ITextCombo.this.cache.getCache();
return force ? ITextCombo.this.cache.loadCache(false) : ITextCombo.this.cache.getCache();
}
 
@Override
/trunk/OpenConcerto/src/org/openconcerto/ui/component/combo/ISearchableCombo.java
26,7 → 26,9
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.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;
33,6 → 35,7
import org.openconcerto.utils.model.IListModel;
import org.openconcerto.utils.model.IMutableListModel;
import org.openconcerto.utils.model.ListComboBoxModel;
import org.openconcerto.utils.model.Reloadable;
import org.openconcerto.utils.text.SimpleDocumentListener;
 
import java.awt.Color;
64,6 → 67,7
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
 
408,7 → 412,7
 
/**
* Returns the actions added at the end of the list of items. The name of the action will be
* displayed and its actionPerformed() invoked when choosed.
* displayed and its actionPerformed() invoked when chosen.
*
* @return the list of actions
*/
436,6 → 440,8
if (!(acache instanceof IMutableListModel))
throw new IllegalArgumentException(this + " is unlocked but " + acache + " is not mutable");
final IMutableListModel<T> mutable = (IMutableListModel<T>) acache;
final boolean isReloadable = mutable instanceof Reloadable;
final Reloadable rel = isReloadable ? (Reloadable) mutable : null;
new MutableListComboPopupListener(new MutableListCombo() {
public ComboLockedMode getMode() {
return ISearchableCombo.this.getMode();
454,6 → 460,16
public void removeCurrentText() {
mutable.removeElement(getValue());
}
 
@Override
public boolean canReload() {
return isReloadable;
}
 
@Override
public void reload() {
rel.reload();
}
}).listen();
}
 
497,10 → 513,29
}
 
private void addItems(final int index, final Collection<T> originalItems) {
// selection cannot change
assert SwingUtilities.isEventDispatchThread();
final ISearchableComboItem<T> sel = getSelection();
final T selOriginal = sel == null ? null : sel.getOriginal();
 
final List<ISearchableComboItem<T>> toAdd = new ArrayList<ISearchableComboItem<T>>(originalItems.size());
for (final T originalItem : originalItems) {
final ISearchableComboItem<T> textSelectorItem = createItem(originalItem);
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)
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";
} else {
// reuse the selected value, otherwise the popup will select nothing
if (sel != null && CompareUtils.equals(selOriginal, originalItem))
textSelectorItem = sel;
else
textSelectorItem = createItem(originalItem);
this.itemsByOriginalItem.put(originalItem, textSelectorItem);
}
toAdd.add(textSelectorItem);
}
// only 1 fire
510,7 → 545,8
private void rmItemsFromModel(final int index0, final int index1) {
getModel().removeElementsAt(index0, index1);
// remove from our map
this.itemsByOriginalItem.keySet().retainAll(getCache().getList());
// ATTN for ~35000 items, new HashSet() got us from 6000ms to 8ms !
this.itemsByOriginalItem.keySet().retainAll(new HashSet<T>(getCache().getList()));
}
 
// conversion
579,7 → 615,7
// set
 
public void resetValue() {
this.setValue(null);
this.setValue((T) null);
}
 
public final void setValue(final T val) {
603,15 → 639,20
}
}
 
private final void setValue(final T val, final boolean valid) {
log("entering " + this.getClass().getSimpleName() + ".setValue " + val + " valid: " + valid);
private final boolean setValid(final boolean valid) {
final boolean invalidChange = this.invalidEdit != !valid;
if (invalidChange) {
this.invalidEdit = !valid;
this.text.setForeground(this.invalidEdit ? Color.GRAY : Color.BLACK);
}
return invalidChange;
}
 
if (this.getValue() != val) {
private final void setValue(final T val, final boolean valid) {
log("entering " + this.getClass().getSimpleName() + ".setValue " + val + " valid: " + valid);
final boolean invalidChange = this.setValid(valid);
 
if (!CompareUtils.equals(this.getValue(), val)) {
log("this.getValue() != val :" + this.getValue());
if (val == null)
this.setSelection(null);
634,6 → 675,24
}
}
 
// perhaps try to factor with the other setValue()
final void setValue(final ISearchableComboItem<T> val) {
log("entering " + this.getClass().getSimpleName() + ".setValue(ISearchableComboItem) " + val);
assert new IdentityHashSet<ISearchableComboItem<T>>(this.getModelValues()).contains(val) : "Item not in model, perhaps use setValue(T)";
// valid since val is in our model
final boolean invalidChange = this.setValid(true);
 
if (!CompareUtils.equals(this.getSelection(), val)) {
this.setSelection(val);
} else if (invalidChange) {
log("this.getSelection() == val and invalidChange");
// since val hasn't changed the model won't fire and thus our selectionChanged()
// will not be called, but it has to since invalidEdit did change
// so the text must be changed, and listeners notified
this.selectionChanged();
}
}
 
private final void setSelection(final ISearchableComboItem<T> val) {
log("entering " + this.getClass().getSimpleName() + ".setSelection " + val);
this.getModel().setSelectedItem(val);
787,8 → 846,8
* with the desired rows.
*
* @param rows the new row count.
* @param textArea <code>true</code> if the editor should be a text area (ie can have more than
* one line), <code>null</code> to retain the current editor, ignored if
* @param textArea <code>true</code> if the editor should be a text area (i.e. can have more
* than one line), <code>null</code> to retain the current editor, ignored if
* <code>rows</code> >= 2.
*/
public final void setRows(int rows, final Boolean textArea) {
/trunk/OpenConcerto/src/org/openconcerto/ui/component/combo/ISearchableComboPopup.java
13,8 → 13,6
package org.openconcerto.ui.component.combo;
 
import org.openconcerto.utils.model.SimpleListDataListener;
 
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
36,7 → 34,6
import javax.swing.ListModel;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.event.ListDataEvent;
 
public class ISearchableComboPopup<T> extends JPopupMenu {
 
53,26 → 50,7
uiInit();
// Listeners
this.list.addMouseMotionListener(new ListMouseMotionHandler());
// JList always displays visibleRowCount even when fewer items exists
// so if we put a high number we get a big blank popup
// instead listen to model change to adjust row count
this.getListModel().addListDataListener(new SimpleListDataListener() {
@Override
public void contentsChanged(ListDataEvent e) {
// ATTN row count always gets back to zero when the contents change (because of
// removeAll())
final int rowCount = Math.min(getListModel().getSize(), 30);
// checking if rowCount changes doesn't work (one reason is probably that we're
// called before Swing and so setVisible displays an empty list)
ISearchableComboPopup.this.list.setVisibleRowCount(rowCount);
if (rowCount > 0 && isVisible()) {
// since "visible row count" is not dynamic
setVisible(false);
setVisible(true);
}
}
});
}
 
private ISearchableCombo<T> getCombo() {
return this.text;
98,6 → 76,7
final JLabel comp = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
comp.setFont(getCombo().getFont());
if (value instanceof Action) {
comp.setFont(comp.getFont().deriveFont(Font.ITALIC));
comp.setText((String) ((Action) value).getValue(Action.NAME));
comp.setIcon(null);
} else {
207,7 → 186,9
// if no selection, don't change the combo
if (sel != null) {
if (sel instanceof ISearchableComboItem) {
this.getCombo().setValue(((ISearchableComboItem<T>) sel).getOriginal());
// don't call setValue() with sel.getOriginal() to handle list with the same item
// multiple times.
this.getCombo().setValue((ISearchableComboItem<T>) sel);
} else if (sel instanceof Action) {
((Action) sel).actionPerformed(new ActionEvent(this.getCombo(), ActionEvent.ACTION_PERFORMED, this.getCombo().getName()));
} else
217,6 → 198,24
}
 
public void open() {
// JList always displays visibleRowCount even when fewer items exists
// so if we put a high number we get a big blank popup
// handle this in open() and not with a SimpleListDataListener since :
// 1. open() is called only once whereas add is called multiple times in
// ISearchableCombo.setMatchingCompletions().
// 2. open() is always called when the list is modified.
final int size = getListModel().getSize();
// rowCount == 0 looks like a bug so show 3 empty rows
final int rowCount = size == 0 ? 3 : Math.min(size, 30);
if (this.list.getVisibleRowCount() != rowCount) {
// checking if rowCount changes doesn't work (one reason is probably that we're
// called before Swing and so setVisible displays an empty list)
this.list.setVisibleRowCount(rowCount);
if (this.isShowing()) {
// since "visible row count" is not dynamic
setVisible(false);
}
}
// si on est pas déjà affiché
// afficher même qd pas d'items : si l'user clique il faut qu'il voit la liste même vide
if (!this.isShowing())
/trunk/OpenConcerto/src/org/openconcerto/ui/component/JRadioButtons.java
144,6 → 144,7
 
private final void addBtn(String btnLabel, V id) {
final JRadioButton btn = new JRadioButton(btnLabel);
btn.setOpaque(false);
for (final MouseListener l : this.mouseListeners) {
btn.addMouseListener(l);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/AbstractFileTransfertHandler.java
New file
0,0 → 1,137
/*
* 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.view;
 
import org.openconcerto.sql.Log;
import org.openconcerto.sql.model.SQLTable;
 
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
 
import javax.swing.JComponent;
import javax.swing.TransferHandler;
 
public abstract class AbstractFileTransfertHandler extends TransferHandler {
 
static private DataFlavor URIListFlavor = null;
 
static public synchronized DataFlavor getURIListFlavor() {
if (URIListFlavor == null) {
try {
URIListFlavor = new DataFlavor("text/uri-list;class=java.lang.String");
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
}
}
return URIListFlavor;
}
 
public AbstractFileTransfertHandler() {
}
 
public abstract void handleFile(File f);
 
@Override
public boolean importData(final JComponent c, final Transferable t) {
if (!canImport(c, t.getTransferDataFlavors())) {
return false;
}
final List<File> list = new ArrayList<File>();
try {
if (hasFileFlavor(t.getTransferDataFlavors())) {
list.addAll((List<File>) t.getTransferData(DataFlavor.javaFileListFlavor));
} else if (hasURIListFlavor(t.getTransferDataFlavors())) {
list.addAll(textURIListToFileList((String) t.getTransferData(getURIListFlavor())));
}
} catch (Exception e) {
e.printStackTrace();
}
try {
 
final Thread thread = new Thread("AbstractFileTransfertHandler") {
@Override
public void run() {
for (File realFile : list) {
handleFile(realFile);
}
}
};
thread.start();
} catch (Exception e) {
e.printStackTrace();
}
 
return list.size() > 0;
 
}
 
@Override
public int getSourceActions(JComponent c) {
return COPY_OR_MOVE;
}
 
@Override
public boolean canImport(JComponent c, DataFlavor[] flavors) {
if (hasFileFlavor(flavors) || hasURIListFlavor(flavors)) {
return true;
}
Log.get().config("No files or URL found in dropped object");
return false;
}
 
private boolean hasFileFlavor(DataFlavor[] flavors) {
for (int i = 0; i < flavors.length; i++) {
if (DataFlavor.javaFileListFlavor.equals(flavors[i]) || DataFlavor.javaRemoteObjectMimeType.equals(flavors[i])) {
return true;
}
}
return false;
}
 
private boolean hasURIListFlavor(DataFlavor[] flavors) {
for (int i = 0; i < flavors.length; i++) {
if (getURIListFlavor().equals(flavors[i])) {
return true;
}
}
return false;
}
 
static List<File> textURIListToFileList(String data) {
final List<File> list = new ArrayList<File>(1);
for (StringTokenizer st = new StringTokenizer(data, "\r\n"); st.hasMoreTokens();) {
String s = st.nextToken();
if (s.startsWith("#")) {
// the line is a comment (as per the RFC 2483)
continue;
}
try {
final URI uri = new URI(s);
final File file = new File(uri);
list.add(file);
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
return list;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/KeyTableCellRenderer.java
15,8 → 15,9
 
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTableListener;
import org.openconcerto.sql.model.SQLTableEvent;
import org.openconcerto.sql.model.SQLTableEvent.Mode;
import org.openconcerto.sql.model.SQLTableModifiedListener;
import org.openconcerto.sql.sqlobject.IComboSelectionItem;
 
import java.util.HashMap;
31,7 → 32,7
private Object toSelect;
private boolean isLoading = false;
private final SQLElement el;
static final Map<SQLElement, Map<Integer, IComboSelectionItem>> cacheMap = new HashMap<SQLElement, Map<Integer, IComboSelectionItem>>();
static private final Map<SQLElement, Map<Integer, IComboSelectionItem>> cacheMap = new HashMap<SQLElement, Map<Integer, IComboSelectionItem>>();
 
public KeyTableCellRenderer(final SQLElement el) {
super();
93,19 → 94,13
m.put(comboSelectionItem.getId(), comboSelectionItem);
}
cacheMap.put(KeyTableCellRenderer.this.el, m);
KeyTableCellRenderer.this.el.getTable().addPremierTableListener(new SQLTableListener() {
KeyTableCellRenderer.this.el.getTable().addPremierTableModifiedListener(new SQLTableModifiedListener() {
@Override
public void rowAdded(SQLTable table, int id) {
m.put(id, KeyTableCellRenderer.this.el.getComboRequest().getComboItem(id));
}
 
@Override
public void rowDeleted(SQLTable table, int id) {
public void tableModified(SQLTableEvent evt) {
final int id = evt.getId();
if (evt.getMode() == Mode.ROW_DELETED)
m.remove(id);
}
 
@Override
public void rowModified(SQLTable table, int id) {
else
m.put(id, KeyTableCellRenderer.this.el.getComboRequest().getComboItem(id));
}
});
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/IListe.java
59,6 → 59,7
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.utils.convertor.StringClobConvertor;
 
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Font;
308,6 → 309,11
// don't auto start, otherwise F2 will trigger the edition
this.jTable.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
 
// Better look
this.jTable.setShowHorizontalLines(false);
this.jTable.setGridColor(new Color(230, 230, 230));
this.jTable.setRowHeight(this.jTable.getRowHeight() + 4);
 
this.popup = new JPopupMenu();
TablePopupMouseListener.add(this.jTable, new ITransformer<MouseEvent, JPopupMenu>() {
@Override
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/RowAction.java
59,6 → 59,9
}
 
public final PredicateRowAction setPredicate(IPredicate<? super IListeEvent> pred) {
if (pred == null) {
throw new IllegalArgumentException("null predicate");
}
this.pred = pred;
return this;
}
69,6 → 72,9
 
@Override
public boolean enabledFor(IListeEvent evt) {
if (pred == null) {
throw new IllegalStateException("No predicate for action:" + this.getAction() + ":" + this.getAction().getValue(Action.NAME));
}
return this.pred.evaluateChecked(evt);
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/EditFrame.java
40,6 → 40,11
 
public static final EditMode MODIFICATION = EditPanel.MODIFICATION;
public static final EditMode CREATION = EditPanel.CREATION;
/**
* If this system property is true, then the minimum size of an edit frame will match the one
* from its content pane.
*/
public final static String SMALL_MIN_SIZE = "org.openconcerto.sql.editFrame.smallMinSize";
 
private boolean frameResize;
 
103,8 → 108,17
// The minimum size of the frame must be the size when packed
this.pack();
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
int w = Math.min(d.width - 100, getWidth());
int h = Math.min(d.height - 100, getHeight());
final int wantedW, wantedH;
if (Boolean.getBoolean(SMALL_MIN_SIZE)) {
final Dimension minimumSize = this.getMinimumSize();
wantedW = minimumSize.width;
wantedH = minimumSize.height;
} else {
wantedW = getWidth();
wantedH = getHeight();
}
final int w = Math.min(d.width - 100, wantedW);
final int h = Math.min(d.height - 100, wantedH);
setMinimumSize(new Dimension(w, h));
 
// View resized
/trunk/OpenConcerto/src/org/openconcerto/sql/view/EditPanel.java
257,6 → 257,7
this.p.getVerticalScrollBar().setUnitIncrement(9);
this.p.setOpaque(false);
this.p.getViewport().setOpaque(false);
this.p.setMinimumSize(new Dimension(60, 60));
 
container.add(this.p, c);
 
/trunk/OpenConcerto/src/org/openconcerto/sql/view/FileTransfertHandler.java
19,11 → 19,8
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
 
import javax.swing.JComponent;
import javax.swing.TransferHandler;
30,19 → 27,6
 
public class FileTransfertHandler extends TransferHandler {
 
static private DataFlavor URIListFlavor = null;
 
static public DataFlavor getURIListFlavor() {
if (URIListFlavor == null) {
try {
URIListFlavor = new DataFlavor("text/uri-list;class=java.lang.String");
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
}
}
return URIListFlavor;
}
 
private final SQLTable tableName;
 
public FileTransfertHandler(SQLTable table) {
59,7 → 43,7
if (hasFileFlavor(t.getTransferDataFlavors())) {
list.addAll((List<File>) t.getTransferData(DataFlavor.javaFileListFlavor));
} else if (hasURIListFlavor(t.getTransferDataFlavors())) {
list.addAll(textURIListToFileList((String) t.getTransferData(getURIListFlavor())));
list.addAll(AbstractFileTransfertHandler.textURIListToFileList((String) t.getTransferData(AbstractFileTransfertHandler.getURIListFlavor())));
}
} catch (Exception e) {
e.printStackTrace();
122,7 → 106,7
 
private boolean hasURIListFlavor(DataFlavor[] flavors) {
for (int i = 0; i < flavors.length; i++) {
if (getURIListFlavor().equals(flavors[i])) {
if (AbstractFileTransfertHandler.getURIListFlavor().equals(flavors[i])) {
return true;
}
}
133,24 → 117,4
return DropManager.getInstance().getHandlerForTable(this.tableName);
}
 
private static List<File> textURIListToFileList(String data) {
final List<File> list = new ArrayList<File>(1);
for (StringTokenizer st = new StringTokenizer(data, "\r\n"); st.hasMoreTokens();) {
String s = st.nextToken();
if (s.startsWith("#")) {
// the line is a comment (as per the RFC 2483)
continue;
}
try {
final URI uri = new URI(s);
final File file = new File(uri);
list.add(file);
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
return list;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/PropsConfiguration.java
31,16 → 31,20
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.FileUtils;
import org.openconcerto.utils.LogUtils;
import org.openconcerto.utils.MultipleOutputStream;
import org.openconcerto.utils.StreamUtils;
import org.openconcerto.utils.cc.IClosure;
 
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
512,6 → 516,10
this.setupLogging(dirName, Boolean.getBoolean(REDIRECT_TO_FILE));
}
 
protected boolean keepStandardStreamsWhenRedirectingToFile() {
return true;
}
 
public void setupLogging(final String dirName, final boolean redirectToFile) {
final File logDir;
try {
542,9 → 550,17
logFile.getParentFile().mkdirs();
try {
System.out.println("Log file: " + logFile.getAbsolutePath());
final PrintStream ps = new PrintStream(new FileOutputStream(logFile, true));
System.setErr(ps);
System.setOut(ps);
final OutputStream fileOut = new FileOutputStream(logFile, true);
final OutputStream out, err;
if (this.keepStandardStreamsWhenRedirectingToFile()) {
out = new MultipleOutputStream(fileOut, new FileOutputStream(FileDescriptor.out));
err = new MultipleOutputStream(fileOut, new FileOutputStream(FileDescriptor.err));
} else {
out = fileOut;
err = fileOut;
}
System.setErr(new PrintStream(new BufferedOutputStream(err, 128), true));
System.setOut(new PrintStream(new BufferedOutputStream(out, 128), true));
} catch (FileNotFoundException e) {
throw new IllegalStateException("unable to write to log file", e);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/IComboModel.java
16,7 → 16,8
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTableListener;
import org.openconcerto.sql.model.SQLTableEvent;
import org.openconcerto.sql.model.SQLTableModifiedListener;
import org.openconcerto.sql.request.ComboSQLRequest;
import org.openconcerto.sql.view.search.SearchSpec;
import org.openconcerto.sql.view.search.SearchSpecUtils;
27,7 → 28,6
import org.openconcerto.utils.checks.EmptyListener;
import org.openconcerto.utils.checks.EmptyObj;
import org.openconcerto.utils.checks.MutableValueObject;
import org.openconcerto.utils.model.DefaultIListModel;
import org.openconcerto.utils.model.DefaultIMutableListModel;
 
import java.beans.PropertyChangeEvent;
52,7 → 52,7
*
* @author Sylvain CUAZ
*/
public class IComboModel extends DefaultIMutableListModel<IComboSelectionItem> implements SQLTableListener, MutableValueObject<IComboSelectionItem>, EmptyObj {
public class IComboModel extends DefaultIMutableListModel<IComboSelectionItem> implements SQLTableModifiedListener, MutableValueObject<IComboSelectionItem>, EmptyObj {
 
private final ComboSQLRequest req;
 
72,8 → 72,6
// true from when the combo is filled with the sole "dots" item until it is loaded with actual
// items, no need to synchronize (EDT)
private boolean updating;
// true if the items being changed are for display only and do not reflect the db
private boolean uiItems;
// l'id à sélectionner à la fin du updateAll
private int idToSelect;
 
100,7 → 98,6
this.search = null;
this.runnables = new ArrayList<Runnable>();
this.setWillUpdate(null);
this.uiItems = false;
this.itemsByID = new HashMap<Integer, IComboSelectionItem>();
this.addMissingItem = true;
 
166,7 → 163,7
// selection change
comboValueChanged();
} else {
itemsChanged(((DefaultIListModel) e.getSource()).getList());
itemsChanged();
}
}
});
230,14 → 227,14
* Reload this combo. This method is thread-safe.
*/
public synchronized final void fillCombo() {
this.fillCombo(null);
this.fillCombo(null, true);
}
 
public synchronized final void fillCombo(final Runnable r) {
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) {
this.doUpdateAll(r);
this.doUpdateAll(r, readCache);
} else {
this.isADirtyDrityGirl = true;
}
260,14 → 257,12
 
this.setUpdating(true);
 
this.uiItems = true;
this.removeAllItems();
addItem(new IComboSelectionItem(SQLRow.NONEXISTANT_ID, "...."));
this.uiItems = false;
// Like ITableModel, don't remove all items, so that if the request fails we still
// keep old items (we used to have uiItems=true while setting the list to "Loading...")
}
}
 
private void doUpdateAll(final Runnable r) {
private void doUpdateAll(final Runnable r, final boolean readCache) {
log("entering doUpdateAll");
synchronized (this) {
this.isADirtyDrityGirl = false;
290,13 → 285,12
protected List<IComboSelectionItem> doInBackground() throws InterruptedException {
// attends 1 peu pour voir si on va pas être annulé
Thread.sleep(50);
return SearchSpecUtils.filter(IComboModel.this.req.getComboItems(), search);
return SearchSpecUtils.filter(IComboModel.this.req.getComboItems(readCache), search);
}
 
// Runs on the event-dispatching thread.
@Override
public void done() {
try {
synchronized (IComboModel.this) {
// if cancel() is called after doInBackground() nothing happens
// but updating is set to a new instance
304,38 → 298,49
// une autre maj arrive
return;
 
final List<IComboSelectionItem> items = this.get();
final boolean firstFill = !IComboModel.this.filledOnce;
// store before removing since it can trigger a selection change
final int idToSelect = IComboModel.this.idToSelect;
List<IComboSelectionItem> items = null;
try {
items = this.get();
removeAllItems();
addAllItems(items);
final boolean firstFill = !IComboModel.this.filledOnce;
IComboModel.this.filledOnce = true;
} catch (InterruptedException e) {
// ne devrait pas arriver puisque done() appelée après doInBackground()
e.printStackTrace();
} catch (CancellationException e) {
// ne devrait pas arriver puisqu'on teste isCancelled()
e.printStackTrace();
} catch (ExecutionException e) {
if (!(e.getCause() instanceof RTInterruptedException))
// pas normal
e.printStackTrace();
} finally {
// always clear willUpdate otherwise the combo can't recover
assert IComboModel.this.willUpdate == this;
IComboModel.this.setWillUpdate(null);
 
}
// check if items could be retrieved
// TODO otherwise show the error to the user so he knows that items are
// stale and he could reload them
if (items != null) {
// restaurer l'état
// if there's only one item in the list and no previous ID to select
// and org.openconcerto.sql.sqlCombo.selectSoleItem=true,select the item
final boolean noSelection = IComboModel.this.idToSelect == SQLRow.NONEXISTANT_ID;
final boolean noSelection = idToSelect == SQLRow.NONEXISTANT_ID;
if (items.size() == 1 && noSelection && Boolean.getBoolean("org.openconcerto.sql.sqlCombo.selectSoleItem"))
IComboModel.this.setSelectedItem(items.get(0));
else if (noSelection && firstFill && getFirstFillSelection() != null)
IComboModel.this.setSelectedItem(getFirstFillSelection().transformChecked(items));
else
selectID(IComboModel.this.idToSelect);
selectID(idToSelect);
 
for (final Runnable r : IComboModel.this.runnables)
r.run();
IComboModel.this.runnables.clear();
}
} catch (InterruptedException e) {
// ne devrait pas arriver puisque done() appelée après doInBackground()
e.printStackTrace();
} catch (CancellationException e) {
// ne devrait pas arriver puisqu'on teste isCancelled()
e.printStackTrace();
} catch (ExecutionException e) {
if (!(e.getCause() instanceof RTInterruptedException))
// pas normal
e.printStackTrace();
}
}
};
401,10 → 406,9
}
}
 
private final void itemsChanged(final List newVal) {
private final void itemsChanged() {
final List<IComboSelectionItem> newVal = this.getList();
this.propSupp.firePropertyChange("items", null, newVal);
if (!this.uiItems)
this.propSupp.firePropertyChange("dataItems", null, newVal);
}
 
// *** value
484,17 → 488,10
log("entering selectID " + id);
assert SwingUtilities.isEventDispatchThread();
 
// selectID() caused by the removeAll() of updateAllBegun()
// so we don't want to forget the selection
if (this.uiItems) {
assert id == SQLRow.NONEXISTANT_ID;
return;
}
 
// 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);
this.doUpdateAll(null, true);
 
if (this.isUpdating()) {
this.idToSelect = id;
624,7 → 621,8
* ones (e.g. adding a '-- loading --' item).
*/
public final void addItemsListener(PropertyChangeListener l, final boolean all) {
this.addListener(all ? "items" : "dataItems", l);
// there's no uiItems anymore, so ignore the boolean
this.addListener("items", l);
}
 
public final void rmItemsListener(PropertyChangeListener l) {
633,41 → 631,15
 
// *** une table que nous affichons a changé
 
/*
* (non-Javadoc)
*
* @see org.openconcerto.sql.model.SQLTableListener2#rowModified(org.openconcerto.sql.model.SQLTable, int)
*/
public void rowModified(SQLTable table, int id) {
if (id >= SQLRow.MIN_VALID_ID && this.getForeignTable().equals(table)) {
@Override
public void tableModified(SQLTableEvent evt) {
final int id = evt.getId();
if (id >= SQLRow.MIN_VALID_ID && this.getForeignTable().equals(evt.getTable())) {
this.reloadComboItem(id);
} else
this.fillCombo();
}
 
/*
* (non-Javadoc)
*
* @see org.openconcerto.sql.model.SQLTableListener2#rowAdded(org.openconcerto.sql.model.SQLTable, int)
*/
public void rowAdded(SQLTable table, int id) {
this.fillCombo();
}
 
/*
* (non-Javadoc)
*
* @see org.openconcerto.sql.model.SQLTableListener2#rowDeleted(org.openconcerto.sql.model.SQLTable, int)
*/
public void rowDeleted(SQLTable table, int id) {
if (this.getForeignTable().equals(table)) {
// delete even if we're not on screen, since it takes next to no time
this.reloadComboItem(id);
} else {
this.fillCombo();
}
}
 
// *** search
 
public final void search(SearchSpec spec) {
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/SQLSearchableTextCombo.java
18,18 → 18,9
import org.openconcerto.sql.sqlobject.SQLTextCombo.ITextComboCacheSQL;
import org.openconcerto.sql.sqlobject.itemview.RowItemViewComponent;
import org.openconcerto.ui.component.ComboLockedMode;
import org.openconcerto.ui.component.IComboCacheListModel;
import org.openconcerto.ui.component.combo.ISearchableTextCombo;
import org.openconcerto.utils.change.CollectionChangeEvent;
import org.openconcerto.utils.change.IListDataEvent;
import org.openconcerto.utils.model.DefaultIMutableListModel;
 
import java.util.Collection;
import java.util.List;
 
import javax.swing.SwingWorker;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
 
/**
* An ISearchableTextCombo with the cache from COMPLETION.
*
73,87 → 64,17
* @param cache the cache to set.
*/
public void initCacheLater(final ISQLListModel cache) {
cache.load(new Runnable() {
@Override
public void run() {
initCache(cache);
cache.initCacheLater(this);
}
});
}
 
public static class ISQLListModel extends DefaultIMutableListModel<String> {
public static class ISQLListModel extends IComboCacheListModel {
 
private final ITextComboCacheSQL cache;
private final ListDataListener l;
 
public ISQLListModel(final SQLField f) {
this(new ITextComboCacheSQL(f));
}
 
public ISQLListModel(final ITextComboCacheSQL c) {
this.cache = c;
this.l = new ListDataListener() {
 
@SuppressWarnings("unchecked")
public void contentsChanged(ListDataEvent e) {
// selection change, see DefaultIMutableListModel#setSelectedItem()
if (e.getIndex0() < 0)
return;
 
final CollectionChangeEvent evt = ((IListDataEvent) e).getCollectionChangeEvent();
this.remove(evt);
this.add(evt.getItemsAdded());
super(c);
}
 
public void intervalAdded(ListDataEvent e) {
this.add(getList().subList(e.getIndex0(), e.getIndex1() + 1));
}
 
public void intervalRemoved(ListDataEvent e) {
this.remove(((IListDataEvent) e).getCollectionChangeEvent());
}
 
private void add(Collection<String> toAdd) {
for (final String s : toAdd) {
ISQLListModel.this.cache.addToCache(s);
}
}
 
@SuppressWarnings("unchecked")
private void remove(CollectionChangeEvent evt) {
for (final String s : (Collection<String>) evt.getItemsRemoved())
ISQLListModel.this.cache.deleteFromCache(s);
}
};
}
 
private void load(final Runnable r) {
if (this.cache.isValid()) {
new SwingWorker<List<String>, Object>() {
 
@Override
protected List<String> doInBackground() throws Exception {
return ISQLListModel.this.cache.loadCache();
}
 
@Override
protected void done() {
// don't remove and add from the cache, items just came from it
removeListDataListener(ISQLListModel.this.l);
removeAllElements();
try {
addAll(get());
} catch (Exception e1) {
// tant pis, pas de cache
e1.printStackTrace();
}
addListDataListener(ISQLListModel.this.l);
if (r != null)
r.run();
}
 
}.execute();
}
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/SQLRequestComboBox.java
39,6 → 39,7
 
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.beans.PropertyChangeEvent;
47,6 → 48,7
import java.util.List;
 
import javax.accessibility.Accessible;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.ImageIcon;
120,6 → 122,13
 
this.combo = new ISearchableCombo<IComboSelectionItem>(ComboLockedMode.LOCKED, 1, this.stringStuff.length());
this.combo.setIncludeEmpty(addUndefined);
this.combo.getActions().add(new AbstractAction("Recharger") {
@Override
public void actionPerformed(ActionEvent e) {
// ignore cache since a user explicitly asked for an update
fillCombo(null, false);
}
});
 
this.emptySupp = new EmptyChangeSupport(this);
this.supp = new ValueChangeSupport<Integer>(this);
138,7 → 147,10
@Override
public void init(SQLRowItemView v) {
final SQLTable foreignTable = v.getField().getDBSystemRoot().getGraph().getForeignTable(v.getField());
if (!this.hasModel())
this.uiInit(Configuration.getInstance().getDirectory().getElement(foreignTable).getComboRequest());
else if (this.getRequest().getPrimaryTable() != foreignTable)
throw new IllegalArgumentException("Tables are different " + getRequest().getPrimaryTable().getSQLName() + " != " + foreignTable.getSQLName());
}
 
/**
297,9 → 309,13
}
 
public synchronized final void fillCombo(final Runnable r) {
this.req.fillCombo(r);
this.fillCombo(r, true);
}
 
public synchronized final void fillCombo(final Runnable r, final boolean readCache) {
this.req.fillCombo(r, readCache);
}
 
// combo
 
public final List<IComboSelectionItem> getItems() {
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/itemview/VWRowItemView.java
65,7 → 65,7
 
/**
* The predicate testing whether the value is empty or not. This implementation returns
* {@link EmptyObjFromVO#DEFAULT_PREDICATE}
* {@link EmptyObjFromVO#getDefaultPredicate()}
*
* @return the predicate testing whether the value is empty.
*/
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/ElementComboBox.java
18,6 → 18,7
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.request.ComboSQLRequest;
import org.openconcerto.sql.request.SQLRowItemView;
import org.openconcerto.sql.view.EditFrame;
import org.openconcerto.sql.view.EditPanel;
135,8 → 136,14
* @return this
*/
public final ElementComboBox init(SQLElement element) {
return this.init(element, element.getComboRequest());
}
 
public final ElementComboBox init(SQLElement element, final ComboSQLRequest req) {
if (element.getTable() != req.getPrimaryTable())
throw new IllegalArgumentException("Tables are different " + element.getTable().getSQLName() + " != " + req.getPrimaryTable().getSQLName());
this.element = element;
this.uiInit(this.element.getComboRequest());
this.uiInit(req);
return this;
}
 
146,7 → 153,10
if (foreignTable == null) {
throw new IllegalArgumentException("No foreign table for " + v.getField().getFullName());
}
if (this.getElement() == null)
this.init(Configuration.getInstance().getDirectory().getElement(foreignTable));
else if (this.getElement().getTable() != foreignTable)
throw new IllegalArgumentException("Tables are different " + getElement().getTable().getSQLName() + " != " + foreignTable.getSQLName());
}
 
public final SQLElement getElement() {
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/ITextWithCompletion.java
15,17 → 15,16
 
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTableListener;
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.ui.component.text.TextComponent;
import org.openconcerto.utils.OrderedSet;
 
import org.openconcerto.utils.checks.MutableValueObject;
import org.openconcerto.utils.text.DocumentFilterList;
import org.openconcerto.utils.text.DocumentFilterList.FilterType;
import org.openconcerto.utils.text.LimitedSizeDocumentFilter;
import org.openconcerto.utils.text.DocumentFilterList.FilterType;
 
import java.awt.Component;
import java.awt.GridLayout;
118,19 → 117,12
 
this.isLoading = true;
loadCacheAsynchronous();
this.comboRequest.addTableListener(new SQLTableListener() {
 
public void rowAdded(SQLTable table, int id) {
// FIXME never removed
this.comboRequest.addTableListener(new SQLTableModifiedListener() {
@Override
public void tableModified(SQLTableEvent evt) {
loadCacheAsynchronous();
}
 
public void rowDeleted(SQLTable table, int id) {
loadCacheAsynchronous();
}
 
public void rowModified(SQLTable table, int id) {
loadCacheAsynchronous();
}
});
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/SQLTextCombo.java
15,6 → 15,7
 
import org.openconcerto.sql.Log;
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.model.IResultSetHandler;
import org.openconcerto.sql.model.SQLDataSource;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowValues;
85,13 → 86,25
return this.t.getDBSystemRoot().getDataSource();
}
 
@SuppressWarnings("unchecked")
public List<String> loadCache() {
public List<String> loadCache(final boolean dsCache) {
final SQLSelect sel = new SQLSelect(this.t.getBase());
sel.addSelect(this.t.getField("LABEL"));
sel.setWhere(new Where(this.t.getField("CHAMP"), "=", this.field));
// ignore DS cache to allow the fetching of rows modified by another VM
@SuppressWarnings("unchecked")
final List<String> items = (List<String>) this.getDS().execute(sel.asString(), new IResultSetHandler(SQLDataSource.COLUMN_LIST_HANDLER) {
@Override
public boolean readCache() {
return dsCache;
}
 
@Override
public boolean writeCache() {
return true;
}
});
this.cache.clear();
this.cache.addAll(this.getDS().executeCol(sel.asString()));
this.cache.addAll(items);
 
return this.cache;
}
98,7 → 111,7
 
public List<String> getCache() {
if (!this.loadedOnce) {
this.loadCache();
this.loadCache(true);
this.loadedOnce = true;
}
return this.cache;
/trunk/OpenConcerto/src/org/openconcerto/sql/Configuration.java
119,7 → 119,7
return newFile;
}
 
protected final File getConfDir(DBStructureItem<?> db) {
public final File getConfDir(DBStructureItem<?> db) {
return DBItemFileCache.getDescendant(new File(getConfDir(), "dataDepedent"), DBFileCache.getJDBCAncestorNames(db, true));
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/request/SQLRowView.java
19,7 → 19,9
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTableListener;
import org.openconcerto.sql.model.SQLTableEvent;
import org.openconcerto.sql.model.SQLTableEvent.Mode;
import org.openconcerto.sql.model.SQLTableModifiedListener;
import org.openconcerto.ui.SwingThreadUtils;
 
import java.awt.Component;
49,7 → 51,7
 
// la table cible de cette requete
private final SQLTable table;
private final SQLTableListener tableListener;
private final SQLTableModifiedListener tableListener;
// l'id affiché ou SQLRow.NONEXISTANT_ID si les valeurs affichées ne sont pas liées à une ligne
// dans la base
private int selectedID;
74,7 → 76,15
this.filling = false;
this.updating = false;
this.selectedID = SQLRow.NONEXISTANT_ID;
this.tableListener = new SQLTableListener() {
this.tableListener = new SQLTableModifiedListener() {
@Override
public void tableModified(SQLTableEvent evt) {
if (evt.getMode() == Mode.ROW_UPDATED)
this.rowModified(evt.getTable(), evt.getId());
else if (evt.getMode() == Mode.ROW_DELETED)
this.rowDeleted(evt.getTable(), evt.getId());
// else don't care
}
 
public void rowModified(SQLTable t, int id) {
if (!isUpdating() && existsInDB()) {
88,10 → 98,6
}
}
 
public void rowAdded(SQLTable t, int id) {
// don't care
}
 
public void rowDeleted(SQLTable t, int id) {
if (!isUpdating() && existsInDB()) {
if (!t.equals(getTable())) {
115,12 → 121,12
 
public final void activate(boolean b) {
if (b) {
this.table.addTableListener(this.tableListener);
this.table.addTableModifiedListener(this.tableListener);
if (this.existsInDB())
// to catch up to the changes which happened while we weren't listening
this.select(this.getSelectedID());
} else
this.table.removeTableListener(this.tableListener);
this.table.removeTableModifiedListener(this.tableListener);
}
 
/**
/trunk/OpenConcerto/src/org/openconcerto/sql/request/ComboSQLRequest.java
143,6 → 143,10
}
 
public final List<IComboSelectionItem> getComboItems() {
return this.getComboItems(true);
}
 
public final List<IComboSelectionItem> getComboItems(final boolean readCache) {
if (this.comboFields.isEmpty())
throw new IllegalStateException("La liste des items listitems est vide!! Ils faut utiliser addComboItem...");
 
151,14 → 155,17
final SQLRowValuesListFetcher comboSelect = this.getFetcher(null).freeze();
 
final CacheKey cacheKey = new CacheKey(comboSelect, this.fieldSeparator, this.undefLabel, this.customizeItem);
if (readCache) {
final CacheResult<List<IComboSelectionItem>> l = cache.check(cacheKey);
if (l.getState() == CacheResult.State.INTERRUPTED)
throw new RTInterruptedException("interrupted while waiting for the cache");
else if (l.getState() == CacheResult.State.VALID)
return l.getRes();
}
 
try {
final List<IComboSelectionItem> result = new ArrayList<IComboSelectionItem>();
// SQLRowValuesListFetcher don't cache
for (final SQLRowValues vals : comboSelect.fetch()) {
if (Thread.currentThread().isInterrupted())
throw new RTInterruptedException("interrupted in fill");
/trunk/OpenConcerto/src/org/openconcerto/sql/request/SQLCacheWatcher.java
16,7 → 16,7
import org.openconcerto.sql.model.SQLData;
import org.openconcerto.sql.model.SQLDataListener;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTableListener;
import org.openconcerto.sql.model.SQLTableModifiedListener;
import org.openconcerto.utils.cache.CacheWatcher;
 
/**
27,7 → 27,7
*/
public class SQLCacheWatcher<K> extends CacheWatcher<K, SQLData> {
 
private final SQLTableListener listener;
private final SQLTableModifiedListener listener;
 
SQLCacheWatcher(final SQLCache<K, ?> c, final SQLData t) {
super(c, t);
36,7 → 36,7
clearCache();
}
});
this.getTable().addPremierTableListener(this.listener);
this.getTable().addPremierTableModifiedListener(this.listener);
}
 
private final SQLTable getTable() {
45,7 → 45,7
 
@Override
protected void dying() {
this.getTable().removeTableListener(this.listener);
this.getTable().removeTableModifiedListener(this.listener);
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/sql/request/BaseSQLRequest.java
16,7 → 16,7
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLFieldsSet;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTableListener;
import org.openconcerto.sql.model.SQLTableModifiedListener;
 
import java.util.Collection;
import java.util.Set;
38,15 → 38,15
*/
protected abstract Collection<SQLField> getAllFields();
 
public final void addTableListener(SQLTableListener l) {
public final void addTableListener(SQLTableModifiedListener l) {
for (final SQLTable t : this.getTables()) {
t.addTableListener(l);
t.addTableModifiedListener(l);
}
}
 
public final void removeTableListener(SQLTableListener l) {
public final void removeTableListener(SQLTableModifiedListener l) {
for (final SQLTable t : this.getTables()) {
t.removeTableListener(l);
t.removeTableModifiedListener(l);
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/element/RowBacked.java
21,7 → 21,6
import org.openconcerto.sql.model.SQLTable;
 
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
 
import org.apache.commons.collections.Transformer;
35,6 → 34,7
 
protected static abstract class PropExtractor implements Transformer {
 
@Override
public final Object transform(Object input) {
return this.extract((SQLRowAccessor) input);
}
51,9 → 51,7
this.propExtractors = new HashMap<String, PropExtractor>();
this.values = new HashMap<String, Object>();
 
final Iterator iter = PolymorphFK.findPolymorphFK(this.getTable()).iterator();
while (iter.hasNext()) {
final PolymorphFK f = (PolymorphFK) iter.next();
for (final PolymorphFK f : PolymorphFK.findPolymorphFK(this.getTable())) {
this.addPolymorphFK(f);
}
}
110,6 → 108,7
 
protected final void addPolymorphFK(final PolymorphFK fk) {
this.putExtractor(fk.getName(), new PropExtractor() {
@Override
public Object extract(SQLRowAccessor r) {
final String tableName = r.getString(fk.getTableField().getName());
final SQLTable foreignT = tableName == null ? null : r.getTable().getBase().getTable(tableName);
/trunk/OpenConcerto/src/org/openconcerto/sql/element/DefaultElementSQLObject.java
35,6 → 35,7
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
 
/**
* A default ElementSQLObject that displays a button to create, and when created a button to delete
61,6 → 62,7
super(parent, comp);
 
this.addValidListener(new ValidListener() {
@Override
public void validChange(ValidObject src, ValidState newValue) {
compChanged();
}
69,24 → 71,29
 
public void showSeparator(boolean visible) {
this.isSeparatorVisible = visible;
if (separator != null)
if (this.separator != null)
this.separator.setVisible(visible);
}
 
public void setDecorated(boolean decorated) {
this.isDecorated = decorated;
if (expandBtn != null)
if (this.expandBtn != null)
this.expandBtn.setVisible(decorated);
if (supprBtn != null)
if (this.supprBtn != null)
this.supprBtn.setVisible(decorated);
if (createBtn != null)
if (this.createBtn != null)
this.createBtn.setVisible(decorated);
}
 
@Override
protected void uiInit() {
final boolean isPlastic = UIManager.getLookAndFeel().getClass().getName().startsWith("com.jgoodies.plaf.plastic");
 
this.expandBtn = new JButton("+/-");
this.expandBtn.setEnabled(false);
this.expandBtn.setOpaque(false);
this.expandBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
toggleExpand();
}
95,8 → 102,10
this.supprBtn = new JButton(new ImageIcon(this.getClass().getResource("delete.png")));
this.supprBtn.setToolTipText("Supprimer");
this.supprBtn.setOpaque(false);
if (isPlastic)
this.supprBtn.setBorder(null);
this.supprBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if ((e.getModifiers() & ActionEvent.SHIFT_MASK) != 0 || this.confirm())
setCreated(false);
107,7 → 116,10
}
});
this.createBtn = new JButton("Créer " + this.getSQLChild().getElement().getSingularName());
// false leaves only a line for the button under Plastic3DLookAndFeel
this.createBtn.setOpaque(isPlastic);
this.createBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCreated(true);
}
116,6 → 128,7
this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
}
 
@Override
protected final void setCreatePanel() {
if (this.editP != null)
this.editP.setVisible(false);
138,6 → 151,7
return this.createP;
}
 
@Override
protected final void setEditPanel() {
this.supprBtn.setVisible(!this.required && this.isDecorated);
if (this.createP != null)
195,6 → 209,7
return this.editP;
}
 
@Override
protected void compChanged() {
this.expandBtn.setEnabled(this.getCurrentID() != SQLRow.NONEXISTANT_ID && this.getValidState().isValid());
}
219,6 → 234,7
this.expand(!this.isExpanded());
}
 
@Override
public void setEditable(boolean enabled) {
super.setEditable(enabled);
this.createBtn.setEnabled(enabled);
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SQLElement.java
118,6 → 118,7
private SQLCache<SQLRowAccessor, Object> modelCache;
 
private final Map<String, JComponent> additionalFields;
private final List<SQLTableModelColumn> additionalListCols;
 
public SQLElement(String singular, String plural, SQLTable primaryTable) {
super();
136,6 → 137,7
this.modelCache = null;
 
this.additionalFields = new HashMap<String, JComponent>();
this.additionalListCols = new ArrayList<SQLTableModelColumn>();
}
 
/**
331,6 → 333,15
}
 
/**
* Fields that cannot be empty.
*
* @return fields that cannot be empty.
*/
public Set<String> getRequiredFields() {
return Collections.emptySet();
}
 
/**
* Fields that can only be set on insertion.
*
* @return fields that cannot be modified.
759,7 → 770,8
// shared must be RESTRICT, parent at least CASCADE (to avoid child without a parent),
// normal is free
if (action.compareTo(ReferenceAction.RESTRICT) < 0 && !this.getNormalForeignFields().contains(ff))
throw new IllegalArgumentException(ff + " is not normal: " + this.getNormalForeignFields());
// getField() checks if the field exists
throw new IllegalArgumentException(getTable().getField(ff).getSQLName() + " is not a normal foreign field : " + this.getNormalForeignFields());
this.getActions().put(ff, action);
}
 
877,6 → 889,7
 
private final SQLTableModelSourceOnline createAndInitTableSource() {
final SQLTableModelSourceOnline res = this.createTableSource();
res.getColumns().addAll(this.additionalListCols);
return initTableSource(res);
}
 
915,6 → 928,15
 
abstract protected List<String> getListFields();
 
public final void addListFields(final List<String> fields) {
for (final String f : fields)
this.addListColumn(new SQLTableModelColumnPath(getTable().getField(f)));
}
 
public final void addListColumn(SQLTableModelColumn col) {
this.additionalListCols.add(col);
}
 
public final Collection<IListeAction> getRowActions() {
return this.rowActions;
}
1365,7 → 1387,8
 
public final int hashCode() {
// ne pas mettre getParent car des fois null
return this.getTable().hashCode() + this.getSharedForeignFields().hashCode() + this.getPrivateForeignFields().hashCode();
return this.getTable().hashCode(); // + this.getSharedForeignFields().hashCode() +
// this.getPrivateForeignFields().hashCode();
}
 
@Override
/trunk/OpenConcerto/src/org/openconcerto/sql/element/BaseSQLComponent.java
170,7 → 170,7
}
}
 
private Tuple2<JComponent, SQLType> getComp(String field) {
protected Tuple2<JComponent, SQLType> getComp(String field) {
if (getElement().getPrivateElement(field) != null)
// we create a MutableRowItemView and need SpecParser
throw new IllegalArgumentException("Private fields not supported");
192,6 → 192,7
// regular
comp = new SQLTextCombo();
}
comp.setOpaque(false);
return new Tuple2<JComponent, SQLType>(comp, type);
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/element/UISQLComponent.java
153,6 → 153,8
this.add(this.tabbedPane);
}
this.currentPanel = new JPanel();
// from Guillaume : tabs shouldn't be opaque in Windows L&F
this.currentPanel.setOpaque(false);
this.tabbedPane.addTab(tabTitle, this.currentPanel);
this.setLayouter(w, d);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/element/LayoutHints.java
New file
0,0 → 1,93
/*
* 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.element;
 
public class LayoutHints {
 
private boolean maximizeWidth;
private boolean maximizeHeight;
private boolean showLabel;
private boolean separatedLabel;
private boolean fill;
public static final LayoutHints DEFAULT_FIELD_HINTS = new LayoutHints(false, false, true, false);
public static final LayoutHints DEFAULT_LARGE_FIELD_HINTS = new LayoutHints(true, false, true, false);
public static final LayoutHints DEFAULT_LIST_HINTS = new LayoutHints(true, true, false, false, true);
public static final LayoutHints DEFAULT_GROUP_HINTS = new LayoutHints(false, false, false, false);
public static final LayoutHints DEFAULT_LARGE_GROUP_HINTS = new LayoutHints(true, false, false, false);
 
public LayoutHints(boolean maximizeWidth, boolean maximizeHeight, boolean showLabel, boolean separatedLabel) {
this.maximizeWidth = maximizeWidth;
this.maximizeHeight = maximizeHeight;
this.showLabel = showLabel;
this.separatedLabel = separatedLabel;
this.fill = false;
}
 
public LayoutHints(boolean maximizeWidth, boolean maximizeHeight, boolean showLabel, boolean separatedLabel, boolean fill) {
this.maximizeWidth = maximizeWidth;
this.maximizeHeight = maximizeHeight;
this.showLabel = showLabel;
this.separatedLabel = separatedLabel;
this.fill = fill;
}
 
public boolean maximizeWidth() {
return maximizeWidth;
}
 
public boolean maximizeHeight() {
return maximizeHeight;
}
 
public boolean showLabel() {
return showLabel;
}
 
public boolean separatedLabel() {
return separatedLabel;
}
 
public boolean fill() {
return fill;
}
 
@Override
public String toString() {
String r = "";
if (maximizeHeight && maximizeWidth) {
r += "MaxW&H";
} else {
if (maximizeHeight) {
r += "MaxH";
}
if (maximizeWidth) {
r += "MaxW";
}
}
if (showLabel && separatedLabel) {
r += " SeparatedLabel";
} else {
if (showLabel) {
r += " StdLabel";
} else {
r += " NoLabel";
}
 
}
if (fill) {
r += " Fill";
}
return r;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/element/Group.java
New file
0,0 → 1,156
/*
* 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.element;
 
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
 
import org.openconcerto.utils.Tuple3;
 
public class Group {
 
private String id;
private int order = 0;
private List<Tuple3<Group, LayoutHints, Integer>> list = new ArrayList<Tuple3<Group, LayoutHints, Integer>>();
 
public Group(String id) {
this.id = id.trim();
 
}
 
public String getId() {
return id;
}
 
public void add(Group group) {
order += 100;
list.add(new Tuple3<Group, LayoutHints, Integer>(group, LayoutHints.DEFAULT_GROUP_HINTS, order));
}
 
public void add(Group group, LayoutHints hints) {
order += 100;
list.add(new Tuple3<Group, LayoutHints, Integer>(group, hints, order));
}
 
public void insert(Group group, LayoutHints hints, int order) {
list.add(new Tuple3<Group, LayoutHints, Integer>(group, hints, order));
}
 
public void add(String string) {
this.add(new Group(string), LayoutHints.DEFAULT_FIELD_HINTS);
 
}
 
public void add(String string, LayoutHints hints) {
this.add(new Group(string), hints);
 
}
 
public void dumpOneColumn() {
dumpOneColumn(LayoutHints.DEFAULT_GROUP_HINTS, 0, 1);
}
 
public void dumpOneColumn(LayoutHints localHint, int localOrder, int level) {
for (int i = 0; i < level - 1; i++) {
System.out.print(" ");
}
if (list.size() == 0)
System.out.print("+-- ");
else
System.out.print("+-+ ");
System.out.println(localOrder + " " + this.id + " [" + localHint + "]");
sortSubGroup();
for (Tuple3<Group, LayoutHints, Integer> tuple : list) {
 
((Group) tuple.get0()).dumpOneColumn(tuple.get1(), tuple.get2(), level + 1);
}
// System.out.println("== end" + this.id);
}
 
private void sortSubGroup() {
if (list.size() > 1) {
Collections.sort(list, new Comparator<Tuple3<Group, LayoutHints, Integer>>() {
 
@Override
public int compare(Tuple3<Group, LayoutHints, Integer> o1, Tuple3<Group, LayoutHints, Integer> o2) {
return o1.get2().compareTo(o2.get2());
}
});
}
}
 
public void dumpTwoColumn() {
dumpTwoColumn(0, LayoutHints.DEFAULT_GROUP_HINTS, 0, 1);
}
 
public int dumpTwoColumn(int x, LayoutHints localHint, int localOrder, int level) {
 
if (isEmpty()) {
System.out.print(" (" + x + ")");
 
System.out.print(localOrder + " " + this.id + "[" + localHint + "]");
 
if ((x % 2) == 1) {
System.out.println();
}
}
sortSubGroup();
for (Tuple3<Group, LayoutHints, Integer> tuple : list) {
final Group subGroup = tuple.get0();
final Integer subGroupOrder = (Integer) tuple.get2();
 
x = subGroup.dumpTwoColumn(x, tuple.get1(), subGroupOrder, level + 1);
 
}
if (isEmpty()) {
x++;
}
if (list.size() != 0 && localHint.maximizeWidth()) {
x = 0;
System.out.println();
}
return x;
}
 
public int getSize() {
return this.list.size();
}
 
public boolean isEmpty() {
return this.list.isEmpty();
}
 
public void sort() {
sortSubGroup();
for (Tuple3<Group, LayoutHints, Integer> tuple : list) {
final Group subGroup = tuple.get0();
subGroup.sort();
}
}
 
public Group getGroup(int i) {
return this.list.get(i).get0();
}
 
public LayoutHints getLayoutHints(int i) {
return this.list.get(i).get1();
}
 
public Integer getOrder(int i) {
return this.list.get(i).get2();
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/sql/element/ElementMapper.java
New file
0,0 → 1,83
/*
* 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.element;
 
import org.openconcerto.utils.StringUtils;
 
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
public class ElementMapper {
private final Map<String, Object> idToObject = new HashMap<String, Object>();
private final Map<Object, List<String>> objectsToIds = new HashMap<Object, List<String>>();
 
private static ElementMapper instance = new ElementMapper();
 
public ElementMapper() {
 
}
 
public static ElementMapper getInstance() {
return instance;
}
 
public void map(String id, Object obj) {
idToObject.put(id, obj);
List<String> l = objectsToIds.get(obj);
if (l == null) {
l = new ArrayList<String>(3);
l.add(id);
} else if (!l.contains(obj)) {
l.add(id);
}
}
 
public Object get(String id) {
return idToObject.get(id);
}
 
public String getString(String id) {
final Object object = idToObject.get(id);
if (object instanceof String) {
return (String) object;
}
return null;
}
 
public Group getGroup(String id) {
final Object object = idToObject.get(id);
if (object instanceof Group) {
return (Group) object;
}
return null;
}
 
public List<String> getIds(Object o) {
return objectsToIds.get(o);
}
 
public void dump() {
System.out.println(this.getClass().getName());
List<String> ids = new ArrayList<String>();
ids.addAll(this.idToObject.keySet());
Collections.sort(ids);
for (String id : ids) {
System.out.println(StringUtils.leftAlign(id, 40) + " : " + idToObject.get(id));
}
System.out.println(ids.size() + " identifiers found");
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SQLElementDirectory.java
104,7 → 104,15
for (final DirectoryListener dl : this.listeners) {
dl.elementAdded(elem);
}
String canonicalName = elem.getClass().getCanonicalName();
if (canonicalName.contains("erp.core") && canonicalName.contains(".element")) {
int i = canonicalName.indexOf("erp.core") + 9;
int j = canonicalName.indexOf(".element");
canonicalName = canonicalName.substring(i, j);
}
ElementMapper.getInstance().map(canonicalName + ".element", elem);
ElementMapper.getInstance().map(canonicalName + ".list.table", elem.getTable().getName());
}
 
public synchronized final boolean contains(SQLTable t) {
return this.elements.containsKey(t);
/trunk/OpenConcerto/src/org/openconcerto/sql/element/GroupSQLComponent.java
New file
0,0 → 1,188
/*
* 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.element;
 
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
 
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLType;
import org.openconcerto.ui.DefaultGridBagConstraints;
import org.openconcerto.ui.JLabelBold;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.Tuple3;
 
public class GroupSQLComponent extends BaseSQLComponent {
 
private Group group;
private int columns = 2;
private boolean forceViewOnly = true;
 
public GroupSQLComponent(SQLElement element, Group group) {
super(element);
this.group = group;
 
}
 
@Override
protected void addViews() {
group.dumpOneColumn();
this.setLayout(new GridBagLayout());
group.sort();
GridBagConstraints c = new DefaultGridBagConstraints();
layout(group, 0, LayoutHints.DEFAULT_GROUP_HINTS, 0, 0, c);
}
 
public int layout(Group currentGroup, Integer order, LayoutHints size, int x, int level, GridBagConstraints c) {
if (currentGroup.isEmpty()) {
System.out.print(" (" + x + ")");
String id = currentGroup.getId();
System.out.print(order + " " + id + "[" + size + "]");
c.gridwidth = 1;
if (size.showLabel()) {
c.weightx = 0;
// Label
if (size.separatedLabel()) {
c.gridx = 0;
c.gridwidth = 4;
c.fill = GridBagConstraints.NONE;
} else {
c.fill = GridBagConstraints.HORIZONTAL;
}
this.add(getLabel(id), c);
if (size.separatedLabel()) {
c.gridy++;
} else {
c.gridx++;
}
}
// Editor
c.weightx = 1;
if (size.maximizeWidth() && size.maximizeHeight()) {
c.fill = GridBagConstraints.BOTH;
} else if (size.maximizeWidth()) {
c.fill = GridBagConstraints.HORIZONTAL;
} else if (size.maximizeHeight()) {
c.fill = GridBagConstraints.VERTICAL;
} else {
c.fill = GridBagConstraints.NONE;
}
if (size.fill()) {
c.weighty = 1;
c.gridwidth = columns * 2;
}
 
this.add(getEditor(id), c);
c.weighty = 0;
c.gridx++;
if ((x % columns) != 0) {
c.gridx = 0;
c.gridy++;
}
}
 
final int stop = currentGroup.getSize();
for (int i = 0; i < stop; i++) {
final Group subGroup = currentGroup.getGroup(i);
final Integer subGroupOrder = currentGroup.getOrder(i);
final LayoutHints subGroupSize = currentGroup.getLayoutHints(i);
x = layout(subGroup, subGroupOrder, subGroupSize, x, level + 1, c);
 
}
 
if (currentGroup.isEmpty()) {
x++;
} else {
if (size.maximizeWidth()) {
c.gridx = 0;
c.gridy++;
}
}
 
return x;
}
 
JComponent getEditor(String id) {
if (id.startsWith("(") && id.endsWith(")*")) {
 
try {
String table = id.substring(1, id.length() - 2).trim();
String idEditor = ElementMapper.getInstance().getIds(table).get(0) + ".editor";
System.out.println("Editor: " + idEditor);
Class cl = (Class) ElementMapper.getInstance().get(idEditor);
return (JComponent) cl.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
 
}
 
SQLField field = this.getTable().getFieldRaw(id);
if (field == null) {
final JLabel jLabel = new JLabelBold("No field " + id);
jLabel.setForeground(Color.RED.darker());
String t = "<html>";
 
final Set<SQLField> fields = this.getTable().getFields();
 
for (SQLField sqlField : fields) {
t += sqlField.getFullName() + "<br>";
}
t += "</html>";
jLabel.setToolTipText(t);
return jLabel;
}
// if (/* this.getMode().equals(Mode.VIEW) || */forceViewOnly) {
// final JLabel jLabel = new JLabel();
// jLabel.setForeground(Color.gray);
// return jLabel;
// }
 
Tuple2<JComponent, SQLType> r = getComp(id);
final JComponent editorComp = r.get0();
if (editorComp != null) {
this.addView(editorComp, id);
return editorComp;
}
 
return new JButton(id);
}
 
JLabel getLabel(String id) {
final String fieldLabel = super.getLabelFor(id);
JLabel jLabel;
if (fieldLabel == null) {
jLabel = new JLabel(id);
jLabel.setForeground(Color.RED.darker());
 
} else {
jLabel = new JLabel(fieldLabel);
}
jLabel.setHorizontalAlignment(SwingConstants.RIGHT);
return jLabel;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/navigator/RowsSQLListModel.java
13,17 → 13,17
/*
* Créé le 21 mai 2005
*
*/
package org.openconcerto.sql.navigator;
 
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.IResultSetHandler;
import org.openconcerto.sql.model.SQLDataSource;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowListRSH;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTableListener;
import org.openconcerto.sql.model.SQLTableEvent;
import org.openconcerto.sql.model.SQLTableModifiedListener;
import org.openconcerto.sql.model.Where;
 
import java.util.List;
31,7 → 31,7
 
import org.apache.commons.dbutils.ResultSetHandler;
 
public class RowsSQLListModel extends SQLListModel<SQLRow> implements SQLTableListener {
public class RowsSQLListModel extends SQLListModel<SQLRow> implements SQLTableModifiedListener {
 
private final SQLElement element;
private final ResultSetHandler handler;
40,11 → 40,11
super();
this.element = element;
this.handler = new SQLRowListRSH(this.getElement().getTable(), true);
this.getElement().getTable().addTableListener(this);
this.getElement().getTable().addTableModifiedListener(this);
}
 
@SuppressWarnings("unchecked")
protected void reload() {
@Override
protected void reload(final boolean noCache) {
final Set<Number> ids = getIds();
final String key = this.getElement().getParentForeignField();
 
60,9 → 60,21
 
// cannot just use a SwingWorker, cause some methods (like SQLBrowser#selectPath())
// expect reload() to by synchronous.
this.setAll((List<SQLRow>) source.execute(sel.asString(), this.handler));
@SuppressWarnings("unchecked")
final List<SQLRow> rows = (List<SQLRow>) source.execute(sel.asString(), new IResultSetHandler(this.handler) {
@Override
public boolean readCache() {
return !noCache;
}
 
@Override
public boolean writeCache() {
return true;
}
});
this.setAll(rows);
}
 
/**
* Search the row with the passed <code>id</code> and return its index.
*
86,26 → 98,18
return this.element;
}
 
public void rowAdded(SQLTable table, int id) {
this.reload();
}
 
public void rowModified(SQLTable table, int id) {
@Override
public void tableModified(SQLTableEvent evt) {
// TODO test if that concern us
this.reload();
}
 
public void rowDeleted(SQLTable table, int id) {
// TODO test if that concern us
this.reload();
}
 
public String toString() {
return this.getClass().getName() + " on " + this.getElement();
}
 
protected void die() {
this.getElement().getTable().removeTableListener(this);
this.getElement().getTable().removeTableModifiedListener(this);
}
 
@Override
/trunk/OpenConcerto/src/org/openconcerto/sql/navigator/ElementsSQLListModel.java
44,7 → 44,8
this.elements = new ArrayList<SQLElement>(elements);
}
 
protected void reload() {
@Override
protected void reload(boolean noCache) {
this.counts.clear();
final List<SQLElement> res = new ArrayList<SQLElement>(this.elements.size());
for (final SQLElement elem : this.elements) {
/trunk/OpenConcerto/src/org/openconcerto/sql/navigator/SQLListModel.java
74,8 → 74,12
// more efficient than reload())
}
 
protected abstract void reload();
protected final void reload() {
this.reload(false);
}
 
protected abstract void reload(final boolean noCache);
 
// *** items
 
public final int getSize() {
/trunk/OpenConcerto/src/org/openconcerto/sql/navigator/SQLBrowserColumn.java
22,6 → 22,7
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.sqlobject.ElementComboBox;
import org.openconcerto.ui.KeyLabel;
import org.openconcerto.ui.PopupMouseListener;
import org.openconcerto.ui.list.selection.ListSelectionState;
import org.openconcerto.utils.JImage;
import org.openconcerto.utils.cc.IPredicate;
53,6 → 54,7
import java.util.List;
import java.util.Set;
 
import javax.swing.AbstractAction;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListSelectionModel;
import javax.swing.Icon;
61,6 → 63,7
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
328,10 → 331,20
this.title.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
// On trie
if (e.getButton() == MouseEvent.BUTTON1) {
getModel().sort();
setTitleIcon();
}
}
});
final JPopupMenu menu = new JPopupMenu();
menu.add(new AbstractAction("Recharger") {
@Override
public void actionPerformed(ActionEvent e) {
getModel().reload(true);
}
});
this.title.addMouseListener(new PopupMouseListener(menu));
// this.normalPanel.setBackground(new Color(239, 235, 231));
 
headerPanel.add(this.title, c2);
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLTableListenerData.java
13,7 → 13,7
package org.openconcerto.sql.model;
 
final class SQLTableListenerData<R extends SQLRowAccessor> implements SQLTableListener {
final class SQLTableListenerData<R extends SQLRowAccessor> implements SQLTableModifiedListener {
 
private final R row;
private final SQLDataListener l;
23,19 → 23,11
this.l = l;
}
 
public void rowAdded(SQLTable table, int id) {
// if the row id was cached as non-existant, now it is
if (this.row.getID() == id)
this.l.dataChanged();
}
 
public void rowDeleted(SQLTable table, int id) {
@Override
public void tableModified(SQLTableEvent evt) {
final int id = evt.getId();
// if the row id was cached as non-existent and evt mode is ADDED, now it is
if (id < SQLRow.MIN_VALID_ID || this.row.getID() == id)
this.l.dataChanged();
}
 
public void rowModified(SQLTable table, int id) {
if (id < SQLRow.MIN_VALID_ID || this.row.getID() == id)
this.l.dataChanged();
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLType.java
17,6 → 17,7
package org.openconcerto.sql.model;
 
import org.openconcerto.utils.CompareUtils;
import org.openconcerto.xml.JDOMUtils;
 
import java.math.BigDecimal;
import java.math.BigInteger;
303,7 → 304,7
sb.append(this.decimalDigits);
}
sb.append("\" typeName=\"");
sb.append(this.typeName);
sb.append(JDOMUtils.OUTPUTTER.escapeAttributeEntities(this.typeName));
sb.append("\"/>");
this.xml = sb.toString();
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLField.java
21,6 → 21,7
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.CompareUtils;
import org.openconcerto.utils.ExceptionUtils;
import org.openconcerto.xml.JDOMUtils;
import org.openconcerto.xml.XMLCodecUtils;
 
import java.sql.DatabaseMetaData;
281,7 → 282,7
if (this.xml == null) {
final StringBuilder sb = new StringBuilder(2048);
sb.append("<field name=\"");
sb.append(this.getName());
sb.append(JDOMUtils.OUTPUTTER.escapeAttributeEntities(this.getName()));
sb.append("\" >");
sb.append(this.type.toXML());
sb.append(XMLCodecUtils.encodeSimple(this.metadata));
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowValuesListFetcher.java
454,6 → 454,29
}
 
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + this.fieldCount;
result = prime * result + ((this.from == null) ? 0 : this.from.hashCode());
result = prime * result + this.linkIndex;
result = prime * result + this.t.hashCode();
return result;
}
 
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final GraphNode other = (GraphNode) obj;
return this.fieldCount == other.fieldCount && this.linkIndex == other.linkIndex && this.t.equals(other.t) && CompareUtils.equals(this.from, other.from);
}
 
@Override
public String toString() {
final String link = this.from == null ? "" : " linked to " + getLinkIndex() + " by " + this.getFromName() + (this.isBackwards() ? " backwards" : " forewards");
return this.getFieldCount() + " fields of " + this.getTable() + link;
460,44 → 483,19
}
}
 
/**
* Execute the request transformed by <code>selTransf</code> and return the result as a list of
* SQLRowValues.
*
* @return a list of SQLRowValues, one item per row, each item having the same structure as the
* SQLRowValues passed to the constructor.
*/
public final List<SQLRowValues> fetch() {
return this.fetch(true);
}
static private final class RSH implements ResultSetHandler {
private final List<String> selectFields;
private final List<GraphNode> graphNodes;
 
private final List<SQLRowValues> fetch(final boolean merge) {
final SQLSelect req = this.getReq();
// getName() would take 5% of ResultSetHandler.handle()
final List<String> selectFields = new ArrayList<String>(req.getSelectFields().size());
for (final SQLField f : req.getSelectFields())
selectFields.add(f.getName());
final SQLTable table = getGraph().getTable();
 
// create a flat list of the graph nodes, we just need the table, field count and the index
// in this list of its linked table, eg for CPI -> LOCAL -> BATIMENT -> SITE :
// <LOCAL,2,0>, <BATIMENT,2,0>, <SITE,5,1>, <CPI,4,0>
final int graphSize = this.getGraph().getGraph().size();
final List<GraphNode> l = new ArrayList<GraphNode>(graphSize);
walk(0, new ITransformer<State<Integer>, Integer>() {
@Override
public Integer transformChecked(State<Integer> input) {
final int index = l.size();
l.add(new GraphNode(input));
return index;
private RSH(List<String> selectFields, List<GraphNode> l) {
this.selectFields = selectFields;
this.graphNodes = l;
}
});
assert l.size() == graphSize : "All nodes weren't explored once : " + l.size() + " != " + graphSize;
 
@SuppressWarnings("unchecked")
final List<SQLRowValues> res = (List<SQLRowValues>) table.getBase().getDataSource().execute(req.asString(), new ResultSetHandler() {
@Override
public Object handle(final ResultSet rs) throws SQLException {
final List<GraphNode> l = this.graphNodes;
final int graphSize = l.size();
int nextToLink = 0;
final List<Future<?>> futures = new ArrayList<Future<?>>();
 
510,7 → 508,7
 
// MAYBE cancel() futures
if (Thread.currentThread().isInterrupted())
throw new RTInterruptedException("interrupted while fetching " + SQLRowValuesListFetcher.this);
throw new RTInterruptedException("interrupted while fetching");
final List<SQLRowValues> row = new ArrayList<SQLRowValues>(graphSize);
for (int i = 0; i < graphSize; i++) {
final GraphNode node = l.get(i);
523,7 → 521,7
try {
// -1 since rs starts at 1
// field names checked below
creatingVals.put(selectFields.get(rsIndex - 1), rs.getObject(rsIndex), false);
creatingVals.put(this.selectFields.get(rsIndex - 1), rs.getObject(rsIndex), false);
} catch (SQLException e) {
throw new IllegalStateException("unable to fill " + creatingVals, e);
}
568,7 → 566,70
 
return res;
}
}, false);
 
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + this.graphNodes.hashCode();
result = prime * result + this.selectFields.hashCode();
return result;
}
 
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final RSH other = (RSH) obj;
return this.graphNodes.equals(other.graphNodes) && this.selectFields.equals(other.selectFields);
}
 
}
 
/**
* Execute the request transformed by <code>selTransf</code> and return the result as a list of
* SQLRowValues. NOTE: this method doesn't use the cache of SQLDataSource.
*
* @return a list of SQLRowValues, one item per row, each item having the same structure as the
* SQLRowValues passed to the constructor.
*/
public final List<SQLRowValues> fetch() {
return this.fetch(true);
}
 
private final List<SQLRowValues> fetch(final boolean merge) {
final SQLSelect req = this.getReq();
// getName() would take 5% of ResultSetHandler.handle()
final List<String> selectFields = new ArrayList<String>(req.getSelectFields().size());
for (final SQLField f : req.getSelectFields())
selectFields.add(f.getName());
final SQLTable table = getGraph().getTable();
 
// create a flat list of the graph nodes, we just need the table, field count and the index
// in this list of its linked table, eg for CPI -> LOCAL -> BATIMENT -> SITE :
// <LOCAL,2,0>, <BATIMENT,2,0>, <SITE,5,1>, <CPI,4,0>
final int graphSize = this.getGraph().getGraph().size();
final List<GraphNode> l = new ArrayList<GraphNode>(graphSize);
walk(0, new ITransformer<State<Integer>, Integer>() {
@Override
public Integer transformChecked(State<Integer> input) {
final int index = l.size();
l.add(new GraphNode(input));
return index;
}
});
assert l.size() == graphSize : "All nodes weren't explored once : " + l.size() + " != " + graphSize;
 
// if we wanted to use the cache, we'd need to copy the returned list and its items (i.e.
// deepCopy()), since we modify them afterwards. Or perhaps include the code after this line
// into the result set handler.
final IResultSetHandler rsh = new IResultSetHandler(new RSH(selectFields, l), false);
@SuppressWarnings("unchecked")
final List<SQLRowValues> res = (List<SQLRowValues>) table.getBase().getDataSource().execute(req.asString(), rsh, false);
// e.g. list of batiment pointing to site
final List<SQLRowValues> merged = merge && this.fetchReferents() ? merge(res) : res;
if (this.grafts.size() > 0) {
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLImmutableRowValues.java
80,10 → 80,6
return this.delegate.isForeignEmpty(fieldName);
}
 
public boolean isEmptyLink(String fieldName) {
return this.delegate.isEmptyLink(fieldName);
}
 
public boolean isDefault(String fieldName) {
return this.delegate.isDefault(fieldName);
}
116,7 → 112,7
}
 
@Override
public SQLTableListener createTableListener(SQLDataListener l) {
public SQLTableModifiedListener createTableListener(SQLDataListener l) {
return this.delegate.createTableListener(l);
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowValues.java
27,6 → 27,7
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.CopyUtils;
import org.openconcerto.utils.ExceptionUtils;
import org.openconcerto.utils.NumberUtils;
import org.openconcerto.utils.RecursionType;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.IClosure;
95,13 → 96,12
}
 
public static final Object SQL_DEFAULT = new Object();
// for now the default is put in the default value of the db
/**
* Empty foreign field value.
*
* @see #putEmptyLink(String)
*/
public static final Object SQL_EMPTY_LINK = SQL_DEFAULT;
public static final Object SQL_EMPTY_LINK = new Object();
 
private static boolean checkValidity = true;
 
167,26 → 167,8
*/
public SQLRowValues(SQLRowValues vals, ForeignCopyMode copyForeigns) {
this(vals.getTable());
final Map<String, Object> toAdd;
if (copyForeigns == ForeignCopyMode.COPY_ROW)
toAdd = vals.values;
else {
final Set<Entry<String, Object>> entrySet = vals.values.entrySet();
toAdd = new LinkedHashMap<String, Object>(entrySet.size());
for (final Map.Entry<String, Object> e : entrySet) {
if (!(e.getValue() instanceof SQLRowValues))
toAdd.put(e.getKey(), e.getValue());
else if (copyForeigns != ForeignCopyMode.NO_COPY) {
final SQLRowValues foreign = (SQLRowValues) e.getValue();
if (foreign.hasID())
toAdd.put(e.getKey(), foreign.getIDNumber());
else if (copyForeigns == ForeignCopyMode.COPY_ID_OR_ROW)
toAdd.put(e.getKey(), foreign);
}
}
}
// setAll() takes care of foreigns and referents
this.setAll(toAdd);
this.setAll(vals.getAllValues(copyForeigns));
}
 
@Override
272,7 → 254,9
* @return the foreign row, eg FAMILLE[1].
*/
public final SQLRowValues grow(String fk) {
if (!(this.getObject(fk) instanceof SQLRowValues)) {
final Object val = this.getContainedObject(fk);
// if fk is in our map with a null value, nothing to grow
if (val != null && !(val instanceof SQLRowValues)) {
final SQLRowValues vals = new SQLRowValues(this.getTable());
vals.putRowValues(fk).setAllToNull();
this.grow(vals, true);
402,16 → 386,53
 
@Override
public Map<String, Object> getAbsolutelyAll() {
return Collections.unmodifiableMap(this.values);
return getAllValues(ForeignCopyMode.COPY_ROW);
}
 
protected final Map<String, Object> getAllValues(ForeignCopyMode copyForeigns) {
final Map<String, Object> toAdd;
if (copyForeigns == ForeignCopyMode.COPY_ROW || this.foreigns.size() == 0) {
toAdd = this.values;
} else {
final Set<Entry<String, Object>> entrySet = this.values.entrySet();
toAdd = new LinkedHashMap<String, Object>(entrySet.size());
for (final Map.Entry<String, Object> e : entrySet) {
if (!(e.getValue() instanceof SQLRowValues)) {
toAdd.put(e.getKey(), e.getValue());
} else if (copyForeigns != ForeignCopyMode.NO_COPY) {
final SQLRowValues foreign = (SQLRowValues) e.getValue();
if (foreign.hasID())
toAdd.put(e.getKey(), foreign.getIDNumber());
else if (copyForeigns == ForeignCopyMode.COPY_ID_OR_ROW)
toAdd.put(e.getKey(), foreign);
}
}
}
return Collections.unmodifiableMap(toAdd);
}
 
/**
* Return the foreign row, if any, for the passed field.
*
* @param fieldName name of the foreign field.
* @return if <code>null</code> or a SQLRowValues one was put at <code>fieldName</code>, return
* it ; else assume that an ID was put at <code>fieldName</code> and return a new SQLRow
* with it.
* @throws IllegalArgumentException if fieldName is not a foreign field or if it isn't contained
* in this instance.
* @throws ClassCastException if the value is neither a SQLRowValues, nor <code>null</code> nor
* a Number.
*/
@Override
public final SQLRowAccessor getForeign(String fieldName) {
public final SQLRowAccessor getForeign(String fieldName) throws IllegalArgumentException, ClassCastException {
// keep getForeignTable at the 1st line since it does the check
final SQLTable foreignTable = this.getForeignTable(fieldName);
if (this.getObject(fieldName) instanceof SQLRowAccessor) {
return (SQLRowAccessor) this.getObject(fieldName);
} else if (this.isEmptyLink(fieldName)) {
final Object val = this.getContainedObject(fieldName);
if (val instanceof SQLRowAccessor) {
return (SQLRowAccessor) val;
} else if (val == null) {
// since we used getContainedObject(), it means that a null was put in our map, not that
// fieldName wasn't there
return null;
} else if (this.isDefault(fieldName)) {
throw new IllegalStateException(fieldName + " is DEFAULT");
420,6 → 441,12
}
}
 
private Object getContainedObject(String fieldName) throws IllegalArgumentException {
if (!this.values.containsKey(fieldName))
throw new IllegalArgumentException("Field not present in this : " + this.getFields());
return this.values.get(fieldName);
}
 
/**
* Returns the foreign table of <i>fieldName</i>.
*
439,18 → 466,12
public boolean isForeignEmpty(String fieldName) {
// keep getForeignTable at the 1st line since it does the check
final SQLTable foreignTable = this.getForeignTable(fieldName);
if (this.getObject(fieldName) instanceof Number) {
return this.getInt(fieldName) == foreignTable.getUndefinedID();
} else if (this.getObject(fieldName) instanceof SQLRowValues) {
return ((SQLRowValues) this.getObject(fieldName)).getID() == foreignTable.getUndefinedID();
} else
return this.getObject(fieldName) == null || this.isEmptyLink(fieldName);
final Object val = this.getContainedObject(fieldName);
final Number id = val instanceof SQLRowValues ? ((SQLRowValues) val).getIDNumber() : (Number) val;
final Number undefID = foreignTable.getUndefinedIDNumber();
return NumberUtils.areNumericallyEqual(id, undefID);
}
 
public boolean isEmptyLink(String fieldName) {
return SQL_EMPTY_LINK.equals(this.getObject(fieldName));
}
 
public boolean isDefault(String fieldName) {
return SQL_DEFAULT.equals(this.getObject(fieldName));
}
478,7 → 499,7
public final SQLRow asRow() {
if (!this.hasID())
throw new IllegalStateException(this + " has no ID");
return new SQLRow(this.getTable(), this.values);
return new SQLRow(this.getTable(), this.getAllValues(ForeignCopyMode.COPY_ID_OR_RM));
}
 
@Override
588,6 → 609,9
}
 
private void _put(String fieldName, Object value) {
if (value == SQL_EMPTY_LINK)
// keep getForeignTable since it does the check
value = this.getForeignTable(fieldName).getUndefinedIDNumber();
// use assertion since check() is not perfect
assert check(fieldName, value);
this.updateLinks(fieldName, this.values.put(fieldName, value), value);
635,8 → 659,6
* @return this.
*/
public SQLRowValues putEmptyLink(String fieldName) {
// keep getForeignTable at the 1st line since it does the check
this.getForeignTable(fieldName);
return this.put(fieldName, SQL_EMPTY_LINK);
}
 
1009,7 → 1031,7
// verifie l'intégrité (a rowValues is obviously correct, as is EMPTY,
// DEFAULT is the responsability of the DB)
final Object fieldVal = this.getObject(fieldName);
if (fk.contains(field) && fieldVal != SQL_DEFAULT && fieldVal != SQL_EMPTY_LINK && !(fieldVal instanceof SQLRowValues)) {
if (fk.contains(field) && fieldVal != SQL_DEFAULT && !(fieldVal instanceof SQLRowValues)) {
final SQLRow pb = this.getTable().checkValidity(field.getName(), (Number) fieldVal);
if (pb != null)
return new Object[] { fieldName, pb };
1371,7 → 1393,7
final Object toIns;
if (value instanceof SQLRowValues) {
// TODO if we already point to some row, archive it
toIns = new Integer(((SQLRowValues) value).insert().getID());
toIns = ((SQLRowValues) value).insert().getIDNumber();
} else
toIns = value;
// sql index start at 1
1390,7 → 1412,8
return value == SQL_DEFAULT ? "DEFAULT" : "?";
}
 
public SQLTableListener createTableListener(SQLDataListener l) {
@Override
public SQLTableModifiedListener createTableListener(SQLDataListener l) {
return new SQLTableListenerData<SQLRowValues>(this, l);
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowValuesCluster.java
836,6 → 836,7
public Link(final SQLRowValues src, final SQLField f, final SQLRowValues dest) {
if (src == null)
throw new NullPointerException("src is null");
assert (f == null && dest == null) || (dest != null && f.getTable() == src.getTable());
this.src = src;
this.f = f;
this.dest = dest;
857,8 → 858,8
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + this.src.hashCode();
result = prime * result + ((this.dest == null) ? 0 : this.dest.hashCode());
result = prime * result + System.identityHashCode(this.src);
result = prime * result + System.identityHashCode(this.dest);
result = prime * result + ((this.f == null) ? 0 : this.f.hashCode());
return result;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowAccessor.java
167,6 → 167,14
return res;
}
 
/**
* Return the foreign row, if any, for the passed field.
*
* @param fieldName name of the foreign field.
* @return <code>null</code> if the value of <code>fieldName</code> is <code>null</code>,
* otherwise a SQLRowAccessor with the value of <code>fieldName</code> as its ID.
* @throws IllegalArgumentException if fieldName is not a foreign field.
*/
public abstract SQLRowAccessor getForeign(String fieldName);
 
public abstract boolean isForeignEmpty(String fieldName);
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowValuesMap.java
New file
0,0 → 1,72
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.sql.model;
 
import org.openconcerto.utils.cc.IPredicate;
import org.openconcerto.utils.checks.EmptyObjFromVO;
 
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
 
/**
* Store inserted rows. This class allows to avoid having huge, slow {@link SQLRowValuesCluster} by
* replacing a foreign link with its ID.
*
* @author Sylvain CUAZ
* @param <T> type of source object.
*/
public abstract class SQLRowValuesMap<T> {
 
private final SQLTable t;
private final Map<SQLRowValues, SQLRow> map;
private final IPredicate<? super T> emptyPredicate;
 
public SQLRowValuesMap(final SQLTable t) {
this(t, EmptyObjFromVO.getDefaultPredicate());
}
 
public SQLRowValuesMap(final SQLTable t, final IPredicate<? super T> emptyPredicate) {
this.t = t;
this.map = new HashMap<SQLRowValues, SQLRow>();
this.emptyPredicate = emptyPredicate;
}
 
protected abstract void fill(final SQLRowValues vals, T obj);
 
/**
* Return a non SQLRowValues value, thus avoiding linking two graphs together. If
* <code>obj</code> is empty, returns {@link SQLRowValues#SQL_EMPTY_LINK}, else calls
* {@link #fill(SQLRowValues, Object)} and if these values haven't already been inserted, insert
* them, finally return the ID.
*
* @param obj the source object.
* @return the ID or SQL_EMPTY_LINK.
* @throws SQLException if an error occurs while inserting.
*/
public final Object getValue(final T obj) throws SQLException {
if (this.emptyPredicate.evaluateChecked(obj)) {
return SQLRowValues.SQL_EMPTY_LINK;
} else {
final SQLRowValues key = new SQLRowValues(this.t);
this.fill(key, obj);
SQLRow res = this.map.get(key);
if (res == null) {
res = key.insert();
this.map.put(key, res);
}
return res.getIDNumber();
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLTable.java
26,6 → 26,7
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.CompareUtils;
import org.openconcerto.utils.ExceptionUtils;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.IPredicate;
import org.openconcerto.utils.change.CollectionChangeEventCreator;
import org.openconcerto.xml.JDOMUtils;
42,6 → 43,7
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
172,9 → 174,6
// always immutable so that fire can iterate safely ; to modify it, simply copy it before
// (adding listeners is a lot less common than firing events)
private List<SQLTableModifiedListener> tableModifiedListeners;
private final List<SQLTableListener> listeners;
// copy of listeners while dispatching, so that a listener can modify it
private final List<SQLTableListener> dispatchingListeners;
// the id that foreign keys pointing to this, can use instead of NULL
// a null value meaning not yet known
private Integer undefinedID;
186,8 → 185,6
SQLTable(SQLSchema schema, String name) {
super(schema, name);
this.tableModifiedListeners = Collections.emptyList();
this.listeners = new ArrayList<SQLTableListener>();
this.dispatchingListeners = new ArrayList<SQLTableListener>();
// ne pas se soucier de la casse
this.fields = createMap();
// order matters (eg for indexes)
971,15 → 968,27
*/
 
public void addTableModifiedListener(SQLTableModifiedListener l) {
synchronized (this.listeners) {
final List<SQLTableModifiedListener> newListeners = new ArrayList<SQLTableModifiedListener>(this.tableModifiedListeners);
this.addTableModifiedListener(l, false);
}
 
public void addPremierTableModifiedListener(SQLTableModifiedListener l) {
this.addTableModifiedListener(l, true);
}
 
private void addTableModifiedListener(SQLTableModifiedListener l, final boolean before) {
synchronized (this) {
final List<SQLTableModifiedListener> newListeners = new ArrayList<SQLTableModifiedListener>(this.tableModifiedListeners.size() + 1);
if (before)
newListeners.add(l);
newListeners.addAll(this.tableModifiedListeners);
if (!before)
newListeners.add(l);
this.tableModifiedListeners = Collections.unmodifiableList(newListeners);
}
}
 
public void removeTableModifiedListener(SQLTableModifiedListener l) {
synchronized (this.listeners) {
synchronized (this) {
final List<SQLTableModifiedListener> newListeners = new ArrayList<SQLTableModifiedListener>(this.tableModifiedListeners);
if (newListeners.remove(l))
this.tableModifiedListeners = Collections.unmodifiableList(newListeners);
986,6 → 995,37
}
}
 
private static final class BridgeListener implements SQLTableModifiedListener {
 
private final SQLTableListener l;
 
private BridgeListener(SQLTableListener l) {
super();
this.l = l;
}
 
@Override
public void tableModified(SQLTableEvent evt) {
final Mode mode = evt.getMode();
if (mode == Mode.ROW_ADDED)
this.l.rowAdded(evt.getTable(), evt.getId());
else if (mode == Mode.ROW_UPDATED)
this.l.rowModified(evt.getTable(), evt.getId());
else if (mode == Mode.ROW_DELETED)
this.l.rowDeleted(evt.getTable(), evt.getId());
}
 
@Override
public int hashCode() {
return this.l.hashCode();
}
 
@Override
public boolean equals(Object obj) {
return obj instanceof BridgeListener && this.l.equals(((BridgeListener) obj).l);
}
}
 
/**
* Ajoute un listener sur cette table.
*
993,32 → 1033,13
* @deprecated use {@link #addTableModifiedListener(SQLTableModifiedListener)}
*/
public void addTableListener(SQLTableListener l) {
synchronized (this.listeners) {
if (!this.listeners.contains(l)) {
this.listeners.add(l);
} else
Log.get().fine(l + " already in");
this.addTableModifiedListener(new BridgeListener(l));
}
}
 
public void addPremierTableListener(SQLTableListener l) {
synchronized (this.listeners) {
if (!this.listeners.contains(l)) {
this.listeners.add(0, l);
} else
throw new IllegalStateException(l + " is already listener of " + this);
}
}
 
public void removeTableListener(SQLTableListener l) {
synchronized (this.listeners) {
this.listeners.remove(l);
this.removeTableModifiedListener(new BridgeListener(l));
}
}
 
private static final int NOT_DISPATCHING = -2;
private int dispatchingID = NOT_DISPATCHING;
 
/**
* Previent tous les listeners de la table qu'il y a eu une modification ou ajout si modif de
* d'une ligne particuliere.
1046,61 → 1067,55
}
 
public final void fire(SQLTableEvent evt) {
final int id = evt.getId();
synchronized (this.dispatchingListeners) {
// FIXME peut laisser tomber des changements si un notifié rechange la même ligne
if (this.dispatchingID != id) {
this.dispatchingID = id;
final Mode mode = evt.getMode();
this.dispatchingListeners.clear();
synchronized (this.listeners) {
this.dispatchingListeners.addAll(this.listeners);
this.fireTableModified(evt);
}
final int size = this.dispatchingListeners.size();
for (int i = 0; i < size; i++) {
final SQLTableListener obj = this.dispatchingListeners.get(i);
if (mode == Mode.ROW_UPDATED)
obj.rowModified(this, id);
else if (mode == Mode.ROW_ADDED)
obj.rowAdded(this, id);
else if (mode == Mode.ROW_DELETED)
obj.rowDeleted(this, id);
else
throw new IllegalArgumentException("unknown mode: " + mode);
 
static private final ThreadLocal<LinkedList<Tuple2<Iterator<SQLTableModifiedListener>, SQLTableEvent>>> events = new ThreadLocal<LinkedList<Tuple2<Iterator<SQLTableModifiedListener>, SQLTableEvent>>>() {
@Override
protected LinkedList<Tuple2<Iterator<SQLTableModifiedListener>, SQLTableEvent>> initialValue() {
return new LinkedList<Tuple2<Iterator<SQLTableModifiedListener>, SQLTableEvent>>();
}
this.fireTableModified(evt);
this.dispatchingID = NOT_DISPATCHING;
} else {
System.err.println("dropping a SQLTable.fire() : fired in the listener");
Thread.dumpStack();
};
 
// allow to maintain the dispatching of events in order when a listener itself fires an event
static private void fireTableModified(Tuple2<Iterator<SQLTableModifiedListener>, SQLTableEvent> newTuple) {
final LinkedList<Tuple2<Iterator<SQLTableModifiedListener>, SQLTableEvent>> linkedList = events.get();
// add new event
linkedList.addLast(newTuple);
// process all pending events
Tuple2<Iterator<SQLTableModifiedListener>, SQLTableEvent> currentTuple;
while ((currentTuple = linkedList.peekFirst()) != null) {
final Iterator<SQLTableModifiedListener> iter = currentTuple.get0();
final SQLTableEvent currentEvt = currentTuple.get1();
while (iter.hasNext()) {
final SQLTableModifiedListener l = iter.next();
l.tableModified(currentEvt);
}
// not removeFirst() since the item might have been already removed
linkedList.pollFirst();
}
}
 
private void fireTableModified(final SQLTableEvent evt) {
// no need to copy since this.tableModifiedListeners is immutable
final List<SQLTableModifiedListener> dispatchingListeners;
synchronized (this.listeners) {
synchronized (this) {
dispatchingListeners = this.tableModifiedListeners;
}
// no need to synchronize since dispatchingListeners is immutable
// even better, it also works if the same thread calls fireTableModified() in a callback
// (although in that case some listeners might have events in the wrong order)
for (final SQLTableModifiedListener l : dispatchingListeners) {
l.tableModified(evt);
fireTableModified(Tuple2.create(dispatchingListeners.iterator(), evt));
}
}
 
@SuppressWarnings("unchecked")
public String toXML() {
final StringBuilder sb = new StringBuilder(16000);
sb.append("<table name=\"");
sb.append(this.getName());
sb.append(JDOMUtils.OUTPUTTER.escapeAttributeEntities(this.getName()));
sb.append("\"");
 
final String schemaName = this.getSchema().getName();
if (schemaName != null) {
sb.append(" schema=\"");
sb.append(schemaName);
sb.append(JDOMUtils.OUTPUTTER.escapeAttributeEntities(schemaName));
sb.append('"');
}
 
1114,7 → 1129,7
 
if (getType() != null) {
sb.append(" type=\"");
sb.append(getType());
sb.append(JDOMUtils.OUTPUTTER.escapeAttributeEntities(getType()));
sb.append('"');
}
 
1152,21 → 1167,13
return sb.toString();
}
 
public SQLTableListener createTableListener(final SQLDataListener l) {
return new SQLTableListener() {
 
public void rowModified(SQLTable table, int id) {
@Override
public SQLTableModifiedListener createTableListener(final SQLDataListener l) {
return new SQLTableModifiedListener() {
@Override
public void tableModified(SQLTableEvent evt) {
l.dataChanged();
}
 
public void rowAdded(SQLTable table, int id) {
l.dataChanged();
}
 
public void rowDeleted(SQLTable table, int id) {
l.dataChanged();
}
 
};
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSchema.java
53,7 → 53,7
sb.append(' ');
sb.append(VERSION_XMLATTR);
sb.append("=\"");
sb.append(version);
sb.append(JDOMUtils.OUTPUTTER.escapeAttributeEntities(version));
sb.append('"');
} catch (IOException e) {
throw new IllegalStateException("Couldn't append version of " + schema, e);
218,7 → 218,7
sb.append("<schema ");
if (this.getName() != null) {
sb.append(" name=\"");
sb.append(this.getName());
sb.append(JDOMUtils.OUTPUTTER.escapeAttributeEntities(this.getName()));
sb.append('"');
}
getVersionAttr(this, sb);
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRow.java
400,10 → 400,12
}
}
 
@Override
public SQLRow getForeign(String fieldName) {
return this.getForeignRow(fieldName);
}
 
@Override
public boolean isForeignEmpty(String fieldName) {
final SQLRow foreignRow = this.getForeignRow(fieldName, SQLRowMode.NO_CHECK);
return foreignRow == null || foreignRow.isUndefined();
413,7 → 415,7
* Retourne la ligne sur laquelle pointe le champ passé. Elle peut être archivé ou indéfinie.
*
* @param field le nom de la clef externe.
* @return la ligne sur laquelle pointe le champ passé, jamais <code>null</code>.
* @return la ligne sur laquelle pointe le champ passé.
* @throws IllegalArgumentException si <code>field</code> n'est pas une clef étrangère de la
* table de cette ligne.
* @throws IllegalStateException si <code>field</code> contient l'ID d'une ligne inexistante.
949,7 → 951,8
return path.split(",");
}
 
public SQLTableListener createTableListener(SQLDataListener l) {
@Override
public SQLTableModifiedListener createTableListener(SQLDataListener l) {
return new SQLTableListenerData<SQLRow>(this, l);
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLData.java
15,7 → 15,7
 
public interface SQLData {
 
public SQLTableListener createTableListener(SQLDataListener l);
public SQLTableModifiedListener createTableListener(SQLDataListener l);
 
public SQLTable getTable();
}
/trunk/OpenConcerto/src/org/openconcerto/sql/users/rights/UserRightsManagerModel.java
19,7 → 19,9
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTableListener;
import org.openconcerto.sql.model.SQLTableEvent;
import org.openconcerto.sql.model.SQLTableEvent.Mode;
import org.openconcerto.sql.model.SQLTableModifiedListener;
import org.openconcerto.sql.model.Where;
 
import java.sql.SQLException;
55,26 → 57,17
List<SQLRow> rowsRights = (List<SQLRow>) Configuration.getInstance().getBase().getDataSource().execute(sel2.asString(), new SQLRowListRSH(this.tableRight, true));
this.cache.addAll(rowsRights);
 
this.tableRight.addTableListener(new SQLTableListener() {
this.tableRight.addTableModifiedListener(new SQLTableModifiedListener() {
@Override
public void rowAdded(SQLTable table, int id) {
 
UserRightsManagerModel.this.cache.add(table.getRow(id));
}
 
@Override
public void rowDeleted(SQLTable table, int id) {
 
}
 
@Override
public void rowModified(SQLTable table, int id) {
SQLRow row = table.getRow(id);
 
public void tableModified(SQLTableEvent evt) {
if (evt.getMode() == Mode.ROW_ADDED) {
UserRightsManagerModel.this.cache.add(evt.getRow());
} else {
final SQLRow row = evt.getRow();
for (int i = 0; i < UserRightsManagerModel.this.cache.size(); i++) {
SQLRow row2 = UserRightsManagerModel.this.cache.get(i);
if (row2.getID() == id) {
if (row.isArchived()) {
final SQLRow row2 = UserRightsManagerModel.this.cache.get(i);
if (row2.getID() == row.getID()) {
if (!row.isValid()) {
UserRightsManagerModel.this.cache.remove(i);
} else {
UserRightsManagerModel.this.cache.set(i, row2);
83,15 → 76,24
}
}
}
}
});
 
this.tableUserRight.addTableListener(new SQLTableListener() {
this.tableUserRight.addTableModifiedListener(new SQLTableModifiedListener() {
 
@Override
public void rowAdded(SQLTable table, int id) {
public void tableModified(SQLTableEvent evt) {
if (evt.getMode() == Mode.ROW_ADDED) {
rowAdded(evt);
} else {
rowModified(evt);
}
}
 
SQLRow row = table.getRow(id);
public void rowAdded(SQLTableEvent evt) {
final SQLRow row = evt.getRow();
if (row.getInt("ID_USER_COMMON") == UserRightsManagerModel.this.idUser) {
SQLRowValues rowVals = getSQLRowValuesForID(id);
SQLRowValues rowVals = getSQLRowValuesFor(row);
if (rowVals == null) {
UserRightsManagerModel.this.listRowValues.add(row.createUpdateRow());
fireTableRowsInserted(UserRightsManagerModel.this.listRowValues.size() - 2, UserRightsManagerModel.this.listRowValues.size() - 1);
99,17 → 101,12
}
}
 
@Override
public void rowDeleted(SQLTable table, int id) {
}
 
@Override
public void rowModified(SQLTable table, int id) {
SQLRow row = table.getRow(id);
public void rowModified(SQLTableEvent evt) {
final SQLRow row = evt.getRow();
if (row.getInt("ID_USER_COMMON") == UserRightsManagerModel.this.idUser) {
SQLRowValues rowVals = getSQLRowValuesForID(id);
SQLRowValues rowVals = getSQLRowValuesFor(row);
int index = UserRightsManagerModel.this.listRowValues.indexOf(rowVals);
if (row.isArchived()) {
if (!row.isValid()) {
UserRightsManagerModel.this.listRowValues.removeElement(rowVals);
fireTableRowsDeleted(index - 1, index + 1);
} else {
121,12 → 118,11
});
}
 
private SQLRowValues getSQLRowValuesForID(int id) {
SQLRow row = this.tableUserRight.getRow(id);
private SQLRowValues getSQLRowValuesFor(final SQLRow row) {
final String string2 = row.getString("CODE");
for (SQLRowValues rowVals : this.listRowValues) {
final String string = rowVals.getString("CODE");
if (rowVals.getID() == id || (string != null && string2 != null && string.equalsIgnoreCase(string2))) {
if (rowVals.getID() == row.getID() || (string != null && string2 != null && string.equalsIgnoreCase(string2))) {
return rowVals;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/changer/correct/FixSharedPrivate.java
17,11 → 17,14
import org.openconcerto.sql.changer.Changer;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.DBSystemRoot;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowListRSH;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLSelect.ArchiveMode;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.model.SQLSelect.ArchiveMode;
import org.openconcerto.sql.utils.SQLUtils;
import org.openconcerto.sql.utils.SQLUtils.SQLFactory;
 
41,7 → 44,7
super(b);
}
 
@SuppressWarnings("unchecked")
@Override
protected void changeImpl(final SQLTable t) throws SQLException {
getStream().println(t + "... ");
if (Configuration.getInstance() == null || Configuration.getInstance().getDirectory() == null)
65,9 → 68,14
sel.addBackwardJoin("INNER", "m", t.getField(pff), null);
final String req = sel.asString() + " GROUP BY " + privateTable.getKey().getFieldRef() + " HAVING count(" + privateTable.getKey().getFieldRef() + ")>1";
 
@SuppressWarnings("unchecked")
final List<Number> privateIDs = t.getDBSystemRoot().getDataSource().executeCol(req);
if (privateIDs.size() > 0) {
getStream().println("\t" + pff + " fixing " + privateIDs.size() + " ... ");
final SQLField archF = t.getArchiveField();
final SQLField privateArchF = privateTable.getArchiveField();
if ((archF == null) != (privateArchF == null))
throw new IllegalStateException("Incoherent archive field : " + archF + " / " + privateArchF);
SQLUtils.executeAtomic(t.getDBSystemRoot().getDataSource(), new SQLFactory<Object>() {
@Override
public Object create() throws SQLException {
76,11 → 84,21
final SQLSelect fixSel = new SQLSelect(t.getBase());
fixSel.setArchivedPolicy(ArchiveMode.BOTH);
fixSel.addSelect(t.getKey());
if (archF != null)
fixSel.addSelect(archF);
fixSel.setWhere(new Where(t.getField(pff), "=", privateID));
final List<Number> tIDs = t.getDBSystemRoot().getDataSource().executeCol(fixSel.asString());
final List<SQLRow> tIDs = SQLRowListRSH.execute(fixSel);
for (final SQLRow tID : tIDs) {
// the first one can keep its private
for (final Number tID : tIDs.subList(1, tIDs.size())) {
new SQLRowValues(t).setID(tID).put(pff, privateElement.createCopy(privateID.intValue())).update();
final SQLRowValues reallyPrivate;
if (tID == tIDs.get(0))
reallyPrivate = new SQLRowValues(privateElement.getTable()).setID(privateID);
else
reallyPrivate = privateElement.createCopy(privateID.intValue());
// keep archive coherence
if (archF != null)
reallyPrivate.put(privateArchF.getName(), tID.getObject(archF.getName()));
new SQLRowValues(t).setID(tID.getIDNumber()).put(pff, reallyPrivate).update();
}
}
return null;
/trunk/OpenConcerto/src/org/openconcerto/utils/FileUtils.java
354,6 → 354,14
}
}
 
public static void copyFile(File in, File out, final boolean useTime) throws IOException {
if (!useTime || in.lastModified() != out.lastModified()) {
copyFile(in, out);
if (useTime)
out.setLastModified(in.lastModified());
}
}
 
public static void copyDirectory(File in, File out) throws IOException {
copyDirectory(in, out, Collections.<String> emptySet());
}
361,6 → 369,10
public static final Set<String> VersionControl = CollectionUtils.createSet(".svn", "CVS");
 
public static void copyDirectory(File in, File out, final Set<String> toIgnore) throws IOException {
copyDirectory(in, out, toIgnore, false);
}
 
public static void copyDirectory(File in, File out, final Set<String> toIgnore, final boolean useTime) throws IOException {
if (toIgnore.contains(in.getName()))
return;
 
371,11 → 383,11
 
String[] children = in.list();
for (int i = 0; i < children.length; i++) {
copyDirectory(new File(in, children[i]), new File(out, children[i]), toIgnore);
copyDirectory(new File(in, children[i]), new File(out, children[i]), toIgnore, useTime);
}
} else {
if (!in.getName().equals("Thumbs.db")) {
copyFile(in, out);
copyFile(in, out, useTime);
}
}
}
404,6 → 416,21
return dir.delete();
}
 
public static void rm_R(File dir) throws IOException {
if (dir.isDirectory()) {
for (final File child : dir.listFiles()) {
rmR(child);
}
}
// The directory is now empty so delete it
rm(dir);
}
 
public static void rm(File f) throws IOException {
if (f.exists() && !f.delete())
throw new IOException("cannot delete " + f);
}
 
public static final File mkdir_p(File dir) throws IOException {
if (!dir.exists()) {
if (!dir.mkdirs()) {
/trunk/OpenConcerto/src/org/openconcerto/utils/CompareUtils.java
57,6 → 57,32
}
 
/**
* Compare two objects if they're numbers or comparable.
*
* @param o1 first object.
* @param o2 second object.
* @return a negative integer, zero, or a positive integer as o1 is less than, equal to, or
* greater than o2.
* @throws ClassCastException if o1 is neither a {@link Number} nor a {@link Comparable}, or if
* o2's type prevents it from being compared to o1.
* @throws NullPointerException if o1 or o2 is <code>null</code>.
* @see Comparable#compareTo(Object)
* @see NumberUtils#compare(Number, Number)
*/
static public final int compare(final Object o1, final Object o2) throws ClassCastException {
if (o1 == null || o2 == null)
throw new NullPointerException();
if (o1 instanceof Number && o2 instanceof Number) {
return NumberUtils.compare((Number) o1, (Number) o2);
} else {
// see Arrays.mergeSort()
@SuppressWarnings({ "rawtypes", "unchecked" })
final int res = ((Comparable) o1).compareTo(o2);
return res;
}
}
 
/**
* Renvoie un comparateur qui utilise successivement la liste passée tant que les objets sont
* égaux.
*
/trunk/OpenConcerto/src/org/openconcerto/utils/TimeUtils.java
New file
0,0 → 1,76
/*
* 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.utils;
 
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Calendar;
 
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
 
public class TimeUtils {
static private DatatypeFactory typeFactory = null;
 
static public final DatatypeFactory getTypeFactory() {
if (typeFactory == null)
try {
typeFactory = DatatypeFactory.newInstance();
} catch (DatatypeConfigurationException e) {
throw new IllegalStateException(e);
}
return typeFactory;
}
 
/**
* Convert the time part of a calendar to a duration.
*
* @param cal a calendar, e.g. 23/12/2011 11:55:33.066 GMT+02.
* @return a duration, e.g. P0Y0M0DT11H55M33.066S.
*/
public final static Duration timePartToDuration(final Calendar cal) {
final BigDecimal seconds = BigDecimal.valueOf(cal.get(Calendar.SECOND)).add(BigDecimal.valueOf(cal.get(Calendar.MILLISECOND)).movePointLeft(3));
return getTypeFactory().newDuration(true, BigInteger.ZERO, BigInteger.ZERO, BigInteger.ZERO, BigInteger.valueOf(cal.get(Calendar.HOUR_OF_DAY)), BigInteger.valueOf(cal.get(Calendar.MINUTE)),
seconds);
}
 
/**
* Normalize <code>cal</code> so that any Calendar with the same local time have the same
* result. If you don't need a Calendar this is faster than
* {@link #copyLocalTime(Calendar, Calendar)}.
*
* @param cal a calendar, e.g. 0:00 CEST.
* @return the time in millisecond of the UTC calendar with the same local time, e.g. 0:00 UTC.
*/
public final static long normalizeLocalTime(final Calendar cal) {
return cal.getTimeInMillis() + cal.getTimeZone().getOffset(cal.getTimeInMillis());
}
 
/**
* Copy the local time from one calendar to another. Except if both calendars have the same time
* zone, from.getTimeInMillis() will be different from to.getTimeInMillis().
*
* @param from the source calendar, e.g. 23/12/2011 11:55:33.066 GMT-12.
* @param to the destination calendar, e.g. 01/01/2000 0:00 GMT+13.
* @return the modified destination calendar, e.g. 23/12/2011 11:55:33.066 GMT+13.
*/
public final static Calendar copyLocalTime(final Calendar from, final Calendar to) {
to.clear();
for (final int field : new int[] { Calendar.YEAR, Calendar.DAY_OF_YEAR, Calendar.HOUR_OF_DAY, Calendar.MINUTE, Calendar.SECOND, Calendar.MILLISECOND }) {
to.set(field, from.get(field));
}
return to;
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/MultipleOutputStream.java
New file
0,0 → 1,68
/*
* 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.utils;
 
import java.io.IOException;
import java.io.OutputStream;
 
public class MultipleOutputStream extends OutputStream {
private OutputStream[] streams;
 
/**
* OutputStream forwarding writes to multiple OutputStreams
* */
public MultipleOutputStream(OutputStream o1, OutputStream o2) {
this(new OutputStream[] { o1, o2 });
}
 
public MultipleOutputStream(OutputStream[] outputStreams) {
this.streams = outputStreams;
}
 
@Override
public void write(int b) throws IOException {
for (int i = 0; i < streams.length; i++) {
streams[i].write(b);
}
}
 
@Override
public void write(byte[] b) throws IOException {
for (int i = 0; i < streams.length; i++) {
streams[i].write(b);
}
}
 
@Override
public void write(byte[] b, int off, int len) throws IOException {
for (int i = 0; i < streams.length; i++) {
streams[i].write(b, off, len);
}
}
 
@Override
public void close() throws IOException {
for (int i = 0; i < streams.length; i++) {
streams[i].close();
}
}
 
@Override
public void flush() throws IOException {
for (int i = 0; i < streams.length; i++) {
streams[i].flush();
}
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/utils/checks/EmptyObjFromVO.java
18,8 → 18,6
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
 
import org.apache.commons.collections.Predicate;
 
/**
* Implement EmptyObj with a ValueObject and a predicate.
*
28,21 → 26,20
*/
public class EmptyObjFromVO<V> implements EmptyObj {
 
/**
* A predicate returning <code>true</code> if the passed object is <code>null</code> or the
* empty string.
*
* @param <T> type of the value object.
* @return a predicate returning <code>true</code> if the object is empty.
*/
@SuppressWarnings("unchecked")
public static final <T> IPredicate<T> getDefaultPredicate() {
return new IPredicate<T>() {
@Override
public boolean evaluateChecked(T input) {
return DEFAULT_PREDICATE.evaluate(input);
return (IPredicate<T>) DEFAULT_PREDICATE;
}
};
}
 
/**
* This predicate returns <code>true</code> if the passed object is <code>null</code> or the
* empty string.
*/
public static final Predicate DEFAULT_PREDICATE = new Predicate() {
public boolean evaluate(Object object) {
private static final IPredicate<Object> DEFAULT_PREDICATE = new IPredicate<Object>() {
public boolean evaluateChecked(Object object) {
if (object instanceof String)
return ((String) object).length() == 0;
else
/trunk/OpenConcerto/src/org/openconcerto/utils/model/Reloadable.java
New file
0,0 → 1,18
/*
* 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.utils.model;
 
public interface Reloadable {
void reload();
}
/trunk/OpenConcerto/src/org/openconcerto/utils/StringUtils.java
222,7 → 222,7
 
if (lastString.length() == nbCharMaxLine) {
int esp = lastString.lastIndexOf(" ");
if (result.length() > 0) {
if (result.length() > 0 && result.charAt(result.length() - 1) != '\n') {
result.append("\n");
}
if (esp > 0) {
232,6 → 232,7
result.append(lastString.toString().trim());
lastString = new StringBuffer();
}
result.append("\n");
}
 
char charAt = s.charAt(i);
240,12 → 241,11
result.append(lastString);
lastString = new StringBuffer();
} else {
 
lastString.append(charAt);
}
}
 
if (result.length() > 0) {
if (result.length() > 0 && result.charAt(result.length() - 1) != '\n') {
result.append("\n");
}
 
338,4 → 338,21
}
}
 
public static String rightAlign(String s, int width) {
String r = s;
int n = width - s.length();
for (int i = 0; i < n; i++) {
r = ' ' + r;
}
return r;
}
 
public static String leftAlign(String s, int width) {
String r = s;
int n = width - s.length();
for (int i = 0; i < n; i++) {
r += ' ';
}
return r;
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/ExceptionHandler.java
71,6 → 71,14
clipboard.setContents(data, data);
}
 
/**
* Display the passed message. Note: this method doesn't block.
*
* @param comp the modal parent of the error window.
* @param msg the message to display.
* @param originalExn the cause, can be <code>null</code>.
* @return an exception.
*/
static public RuntimeException handle(Component comp, String msg, Throwable originalExn) {
return new ExceptionHandler(comp, msg, originalExn, false);
}
83,6 → 91,14
return handle(msg, null);
}
 
/**
* Display the passed message and quit. Note: this method blocks until the user closes the
* window (then exits).
*
* @param msg the message to display.
* @param originalExn the cause, can be <code>null</code>.
* @return an exception.
*/
static public RuntimeException die(String msg, Throwable originalExn) {
return new ExceptionHandler(null, msg, originalExn);
}
115,14 → 131,25
if (!GraphicsEnvironment.isHeadless() || forceUI) {
if (SwingUtilities.isEventDispatchThread()) {
showMsg(msg, error);
} else
SwingUtilities.invokeLater(new Runnable() {
} else {
final Runnable run = new Runnable() {
public void run() {
showMsg(msg, error);
}
});
};
if (error) {
try {
SwingUtilities.invokeAndWait(run);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
} else {
SwingUtilities.invokeLater(run);
}
}
}
}
 
protected final void showMsg(final String msg, final boolean quit) {
final JPanel p = new JPanel();
/trunk/OpenConcerto/src/org/openconcerto/task/TodoListPanel.java
236,8 → 236,9
}
 
this.addButton = new JButton("Ajouter une tâche");
 
this.addButton.setOpaque(false);
this.removeButton = new JButton("Effacer");
this.removeButton.setOpaque(false);
this.removeButton.setEnabled(false);
this.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
263,6 → 264,7
c.anchor = GridBagConstraints.EAST;
c.gridx++;
JMenuBar b = new JMenuBar();
b.setOpaque(false);
b.setBorderPainted(false);
b.add(this.comboUser);
// Pour que le menu ne disparaisse pas quand on rapetisse trop la fenetre en bas
273,6 → 275,7
c.gridx++;
c.weightx = 1;
this.detailCheckBox = new JCheckBox("Affichage détaillé");
this.detailCheckBox.setOpaque(false);
this.detailCheckBox.setSelected(false);
this.add(this.detailCheckBox, c);
 
279,6 → 282,7
//
c.gridx++;
this.hideOldCheckBox = new JCheckBox("Masquer l'historique");
this.hideOldCheckBox.setOpaque(false);
this.hideOldCheckBox.setSelected(true);
this.add(this.hideOldCheckBox, c);
 
286,6 → 290,7
 
c.weightx = 0;
c.anchor = GridBagConstraints.EAST;
this.reloadPanel.setOpaque(false);
this.add(this.reloadPanel, c);
 
// Table
503,7 → 508,10
this.t.setBlockEventOnColumn(false);
this.t.setBlockRepaint(false);
this.t.getColumnModel().getColumn(1).setCellRenderer(this.iconRenderer);
 
// Better look
this.t.setShowHorizontalLines(false);
this.t.setGridColor(new Color(230, 230, 230));
this.t.setRowHeight(this.t.getRowHeight() + 4);
AlternateTableCellRenderer.UTILS.setAllColumns(this.t);
this.t.repaint();
 
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/DocumentGeneratorManager.java
New file
0,0 → 1,66
/*
* 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.erp.generationDoc;
 
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
 
public class DocumentGeneratorManager {
private static DocumentGeneratorManager instance = new DocumentGeneratorManager();
private Map<String, DocumentGenerator> generators = new HashMap<String, DocumentGenerator>();
private DocumentGenerator defautGenerator;
 
public static DocumentGeneratorManager getInstance() {
return instance;
}
 
public void add(String templateId, DocumentGenerator generator) {
this.generators.put(templateId, generator);
}
 
public void remove(DocumentGenerator generator) {
final Set<String> keys = generators.keySet();
for (String key : keys) {
if (generators.get(key).equals(generator)) {
generators.remove(key);
}
}
}
 
public void setDefaultGenerator(DocumentGenerator generator) {
this.defautGenerator = generator;
}
 
/**
* Returns the document generator a specific templateId
* */
public DocumentGenerator getGenerator(String templateId) {
DocumentGenerator generator = this.generators.get(templateId);
if (generator == null) {
generator = defautGenerator;
}
return generator;
}
 
public void dump() {
System.out.println(this.getClass().getCanonicalName());
System.out.println("Default generator:" + this.defautGenerator);
Set<String> ids = generators.keySet();
for (String templateId : ids) {
System.out.println("'" + templateId + "' : " + generators.get(templateId));
}
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/AbstractJOOReportsSheet.java
41,7 → 41,6
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
 
 
public abstract class AbstractJOOReportsSheet {
private static final String defaultLocationTemplate = SpreadSheetGenerator.defaultLocationTemplate;
protected static final DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
49,8 → 48,7
protected static final DateFormat yearFormat = new SimpleDateFormat("yyyy");
private String year;
protected String locationTemplate = TemplateNXProps.getInstance().getStringProperty("LocationTemplate");
private String locationOO, locationPDF;
protected String templateFileName;
protected String templateId;
private String printer;
protected boolean askOverwriting = false;
 
61,11 → 59,9
 
abstract public String getFileName();
 
protected void init(String year, String templateFileName, String attributePrinter, Tuple2<String, String> t) {
protected void init(String year, String templateId, String attributePrinter) {
this.year = year;
this.templateFileName = templateFileName;
this.locationOO = SheetXml.getLocationForTuple(t, false) + File.separator + this.year;
this.locationPDF = SheetXml.getLocationForTuple(t, true) + File.separator + this.year;
this.templateId = templateId;
this.printer = PrinterNXProps.getInstance().getStringProperty(attributePrinter);
}
 
88,27 → 84,23
try {
 
String fileName = getFileName();
final InputStream fileTemplate = getStream(this.templateFileName, this.locationTemplate, defaultLocationTemplate);
 
File fileOutOO = new File(this.locationOO, fileName + ".odt");
// File fileOutPDF = new File(locationPropositionPDF, fileName);
 
final InputStream fileTemplate = TemplateManager.getInstance().getTemplate(this.templateId);
File outputDir = DocumentLocalStorageManager.getInstance().getDocumentOutputDirectory(this.templateId);
File fileOutOO = getDocumentFile();
if (fileOutOO.exists() && overwrite) {
if (this.askOverwriting) {
int answer = JOptionPane.showConfirmDialog(null, "Voulez vous écraser le document ?", "Remplacement d'un document", JOptionPane.YES_NO_OPTION);
if (answer == JOptionPane.YES_OPTION) {
SheetUtils.getInstance().convertToOldFile(fileName, new File(this.locationOO), fileOutOO, ".odt");
SheetUtils.convertToOldFile(fileName, outputDir, fileOutOO, ".odt");
}
} else {
SheetUtils.getInstance().convertToOldFile(fileName, new File(this.locationOO), fileOutOO, ".odt");
SheetUtils.convertToOldFile(fileName, outputDir, fileOutOO, ".odt");
}
}
 
if (!fileOutOO.exists()) {
fileOutOO.getParentFile().mkdirs();
Template template;
// try {
template = new Template(new BufferedInputStream(fileTemplate));
Template template = new Template(new BufferedInputStream(fileTemplate));
 
// creation du document
final Map createMap = createMap();
116,11 → 108,7
 
model.putAll(createMap);
template.createDocument(model).saveAs(fileOutOO);
// template.createDocument(model, new BufferedOutputStream(new
// FileOutputStream(fileOutOO)));
// } catch (JDOMException e) {
// e.printStackTrace();
// }
 
}
 
// ouverture de OO
133,9 → 121,10
}
final Component doc = ooConnexion.loadDocument(fileOutOO, !show);
 
if (this.savePDF())
doc.saveToPDF(new File(this.locationPDF, fileName + ".pdf"), "writer_pdf_Export");
 
if (this.savePDF()) {
File pdfOutputDir = DocumentLocalStorageManager.getInstance().getPDFOutputDirectory(templateId);
doc.saveToPDF(new File(pdfOutputDir, fileName + ".pdf"), "writer_pdf_Export");
}
if (print) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("Name", printer);
164,7 → 153,7
}
 
public void showDocument() {
File fileOutOO = new File(this.locationOO, getFileName() + ".odt");
File fileOutOO = getDocumentFile();
if (fileOutOO.exists()) {
try {
final OOConnexion ooConnexion = ComptaPropsConfiguration.getOOConnexion();
183,8 → 172,13
}
}
 
private File getDocumentFile() {
File outputDir = DocumentLocalStorageManager.getInstance().getDocumentOutputDirectory(templateId);
return new File(outputDir, getFileName() + ".odt");
}
 
public void printDocument() {
File fileOutOO = new File(this.locationOO, getFileName() + ".odt");
File fileOutOO = getDocumentFile();
if (fileOutOO.exists()) {
 
try {
211,7 → 205,7
 
public void fastPrintDocument() {
 
final File f = new File(this.locationOO, getFileName() + ".odt");
final File f = getDocumentFile();
 
if (!f.exists()) {
generate(true, false, this.printer);
239,13 → 233,6
}
}
 
public boolean exists() {
 
String fileName = getFileName();
File fileOutOO = new File(this.locationOO, fileName + ".odt");
return fileOutOO.exists();
}
 
protected String getInitiales(SQLRow row) {
String init = "";
if (row != null) {
264,9 → 251,10
public void exportToPdf() {
 
// Export vers PDF
String fileName = getFileName();
File fileOutOO = new File(this.locationOO, fileName + ".odt");
File fileOutPDF = new File(this.locationPDF, fileName);
final String fileName = getFileName();
final File fileOutOO = getDocumentFile();
final File outputPDFDirectory = DocumentLocalStorageManager.getInstance().getPDFOutputDirectory(this.templateId);
final File fileOutPDF = new File(outputPDFDirectory, fileName + ".pdf");
 
if (!fileOutOO.exists()) {
generate(false, false, "");
290,7 → 278,7
int result = JOptionPane.showOptionDialog(null, "Ouvrir le pdf ?", "Ouverture du PDF", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null);
 
if (result == JOptionPane.YES_OPTION) {
Gestion.openPDF(new File(fileOutPDF.getAbsolutePath() + ".pdf"));
Gestion.openPDF(fileOutPDF);
} else {
try {
FileUtils.openFile(fileOutPDF.getParentFile());
317,10 → 305,6
return ville.getName();
}
 
public String getLocationOO() {
return locationOO;
}
 
protected static String getVilleCP(String name) {
Ville ville = Ville.getVilleFromVilleEtCode(name);
if (ville == null) {
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/DocumentGenerator.java
New file
0,0 → 1,27
/*
* 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.erp.generationDoc;
 
import java.util.Map;
 
public interface DocumentGenerator {
 
/**
* Define the context for the creation of the document Keys of the context are defined in the
* objects implementing the interface
* */
public void setContext(Map<String, Object> context);
 
public void createDocument();
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/AbstractLocalTemplateProvider.java
New file
0,0 → 1,73
/*
* 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.erp.generationDoc;
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
 
public abstract class AbstractLocalTemplateProvider implements TemplateProvider {
 
@Override
public InputStream getTemplate(String templateId, String langage, String type) {
try {
final File templateFile = getTemplateFile(templateId, langage, type);
if (templateFile == null || !templateFile.exists()) {
return null;
}
return new FileInputStream(templateFile);
} catch (FileNotFoundException e) {
return null;
}
}
 
@Override
public InputStream getTemplatePrintConfiguration(String templateId, String langage, String type) {
final File t = getTemplateFile(templateId, langage, type);
final String name = t.getName();
if (name.toLowerCase().endsWith(".ods")) {
final File file = new File(t.getParent(), name.substring(0, name.length() - 4) + ".odsp");
try {
return new FileInputStream(file);
} catch (FileNotFoundException e) {
System.err.println("No print configuration " + file.getAbsolutePath() + " for template id: " + templateId);
e.printStackTrace();
}
}
return null;
}
 
@Override
public InputStream getTemplateConfiguration(String templateId, String langage, String type) {
final File t = getTemplateFile(templateId, langage, type);
final String name = t.getName();
if (name.toLowerCase().endsWith(".ods")) {
final File file = new File(t.getParent(), name.substring(0, name.length() - 4) + ".xml");
try {
return new FileInputStream(file);
} catch (FileNotFoundException e) {
System.err.println("No template configuration " + file.getAbsolutePath() + " for template id: " + templateId);
e.printStackTrace();
}
}
return null;
}
 
public abstract File getTemplateFile(String templateId, String langage, String type);
 
@Override
public abstract String getTemplatePath(String templateId, String langage, String type);
 
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/TemplateProvider.java
New file
0,0 → 1,38
/*
* 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.erp.generationDoc;
 
import java.io.InputStream;
 
public interface TemplateProvider {
/**
* Path: ex ILM/Devis
* */
public String getTemplatePath(String templateId, String language, String type);
 
/**
* Returns the content of template file (ex: the ODS file)
* */
public InputStream getTemplate(String templateId, String language, String type);
 
/**
* Returns the content of template configuration file (ex: the odsp file)
* */
public InputStream getTemplateConfiguration(String templateId, String language, String type);
 
/**
* Returns the content of template print configuration file (ex: the odsp file)
* */
public InputStream getTemplatePrintConfiguration(String templateId, String language, String type);
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/SheetXml.java
17,7 → 17,8
import org.openconcerto.erp.core.common.ui.FastPrintAskFrame;
import org.openconcerto.erp.core.common.ui.PreviewFrame;
import org.openconcerto.erp.generationDoc.element.TypeModeleSQLElement;
import org.openconcerto.erp.preferences.TemplateNXProps;
import org.openconcerto.erp.storage.StorageEngine;
import org.openconcerto.erp.storage.StorageEngines;
import org.openconcerto.openoffice.OOUtils;
import org.jopendocument.link.Component;
import org.openconcerto.sql.Configuration;
25,13 → 26,17
import org.openconcerto.sql.model.SQLBase;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.Tuple2;
 
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
46,6 → 51,23
 
public abstract class SheetXml {
 
// return null to keep default value
public interface StorageDirs {
public File getDocumentOutputDirectory(SheetXml sheet);
 
public File getPDFOutputDirectory(SheetXml sheet);
 
public String getStoragePath(SheetXml sheet);
}
 
private static StorageDirs STORAGE_DIRS;
 
// allow to redirect all documents
public static void setStorageDirs(StorageDirs d) {
STORAGE_DIRS = d;
}
 
public static final String DEFAULT_PROPERTY_NAME = "Default";
protected SQLElement elt;
 
// nom de l'imprimante à utiliser
57,16 → 79,6
// Language du document
protected SQLRow rowLanguage;
 
// emplacement du fichier OO généré
protected String locationOO;
 
// emplacement du fichier PDF généré
protected String locationPDF;
 
protected File f;
 
public static final Tuple2<String, String> tupleDefault = Tuple2.create("Default", "Autres");
 
protected static final SQLBase base = ((ComptaPropsConfiguration) Configuration.getInstance()).getSQLBaseSociete();
 
// single threaded and kill its thread after 3 seconds (to allow the program to exit)
79,62 → 91,98
}
};
 
public void useOO(final File f, final boolean visu, final boolean impression, final String fileName) {
useOO(f, visu, impression, fileName, true);
public final SQLElement getElement() {
return this.elt;
}
 
public void useOO(final File f, final boolean visu, final boolean impression, final String fileName, boolean exportPDF) {
/**
* Show, print and export the document to PDF. This method is asynchronous, but is executed in a
* single threaded queue shared with createDocument
* */
public Future<SheetXml> showPrintAndExportAsynchronous(final boolean showDocument, final boolean printDocument, final boolean exportToPDF) {
final Callable<SheetXml> c = new Callable<SheetXml>() {
@Override
public SheetXml call() throws Exception {
showPrintAndExport(showDocument, printDocument, exportToPDF);
return SheetXml.this;
}
};
return runnableQueue.submit(c);
 
if (f == null || fileName.trim().length() == 0) {
ExceptionHandler.handle("Erreur lors de la génération du fichier " + fileName);
}
 
/**
* Show, print and export the document to PDF. This method is synchronous
* */
public void showPrintAndExport(final boolean showDocument, final boolean printDocument, boolean exportToPDF) {
 
final File generatedFile = getGeneratedFile();
final File pdfFile = getGeneratedPDFFile();
if (generatedFile == null || !generatedFile.exists()) {
ExceptionHandler.handle("Fichier généré manquant: " + generatedFile);
return;
}
 
try {
if (!Boolean.getBoolean("org.openconcerto.oo.useODSViewer")) {
final Component doc = ComptaPropsConfiguration.getOOConnexion().loadDocument(f, !visu);
final Component doc = ComptaPropsConfiguration.getOOConnexion().loadDocument(generatedFile, !showDocument);
 
if (exportPDF) {
doc.saveToPDF(getFilePDF()).get();
}
 
if (impression) {
if (printDocument) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("Name", this.printer);
doc.printDocument(map);
}
if (exportToPDF) {
doc.saveToPDF(pdfFile).get();
}
doc.close();
} else {
final OpenDocument doc = new OpenDocument(f);
final OpenDocument doc = new OpenDocument(generatedFile);
 
if (exportPDF) {
final Thread t = new Thread("PDF Export: " + fileName) {
if (showDocument) {
showPreviewDocument();
}
if (printDocument) {
// Print !
DefaultDocumentPrinter printer = new DefaultDocumentPrinter();
printer.print(doc);
 
}
if (exportToPDF) {
 
try {
SheetUtils.convert2PDF(doc, pdfFile);
 
} catch (Throwable e) {
ExceptionHandler.handle("Impossible de créer le PDF");
}
 
Thread t = new Thread(new Runnable() {
 
@Override
public void run() {
 
List<StorageEngine> engines = StorageEngines.getInstance().getActiveEngines();
for (StorageEngine storageEngine : engines) {
if (storageEngine.isConfigured() && storageEngine.allowAutoStorage()) {
try {
SheetUtils.getInstance().convert2PDF(doc, f, fileName);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
storageEngine.connect();
final BufferedInputStream inStream = new BufferedInputStream(new FileInputStream(pdfFile));
final String path = getStoragePath();
storageEngine.store(inStream, path, pdfFile.getName(), true);
inStream.close();
storageEngine.disconnect();
} catch (IOException e) {
ExceptionHandler.handle("Impossible de sauvegarder le PDF");
}
}
};
t.setPriority(Thread.MIN_PRIORITY);
t.start();
 
}
if (visu) {
showPreviewDocument();
}
if (impression) {
// Print !
DefaultDocumentPrinter printer = new DefaultDocumentPrinter();
printer.print(doc);
 
}
});
t.start();
 
}
}
 
} catch (Exception e) {
e.printStackTrace();
142,19 → 190,61
}
}
 
public abstract String getDefaultModele();
public abstract String getDefaultTemplateId();
 
// nom du modele sans extension
public String getModele() {
if (this.row.getTable().getFieldsName().contains("ID_MODELE")) {
/**
* Path of the directory used for storage. Ex: Devis/2010
* */
public final String getStoragePath() {
final String res = STORAGE_DIRS == null ? null : STORAGE_DIRS.getStoragePath(this);
if (res != null)
return res;
else
return this.getStoragePathP();
}
 
public final File getDocumentOutputDirectory() {
final File res = STORAGE_DIRS == null ? null : STORAGE_DIRS.getDocumentOutputDirectory(this);
if (res != null)
return res;
else
return this.getDocumentOutputDirectoryP();
}
 
public final File getPDFOutputDirectory() {
final File res = STORAGE_DIRS == null ? null : STORAGE_DIRS.getPDFOutputDirectory(this);
if (res != null)
return res;
else
return this.getPDFOutputDirectoryP();
}
 
protected abstract String getStoragePathP();
 
protected abstract File getDocumentOutputDirectoryP();
 
protected abstract File getPDFOutputDirectoryP();
 
/**
* Name of the generated document (without extension), do not rely on this name.
*
* Use getGeneratedFile().getName() to get the generated file name.
* */
public abstract String getName();
 
/**
* @return the template id for this template (ex: "sales.quote")
* */
public String getTemplateId() {
if (this.row != null && this.row.getTable().getFieldsName().contains("ID_MODELE")) {
SQLRow rowModele = this.row.getForeignRow("ID_MODELE");
if (rowModele.isUndefined()) {
TypeModeleSQLElement typeModele = Configuration.getInstance().getDirectory().getElement(TypeModeleSQLElement.class);
String modele = typeModele.getTemplateMapping().get(this.row.getTable());
String modele = typeModele.getTemplateMapping().get(this.row.getTable().getName());
if (modele == null) {
System.err.println("No default modele in table TYPE_MODELE for table " + this.row.getTable().getName());
Thread.dumpStack();
return getDefaultModele();
return getDefaultTemplateId();
} else {
return modele;
}
162,23 → 252,24
return rowModele.getString("NOM");
}
}
return getDefaultModele();
return getDefaultTemplateId();
}
 
public abstract Future<File> genere(final boolean visu, final boolean impression);
public abstract Future<SheetXml> createDocumentAsynchronous();
 
public abstract String getFileName();
 
private String getOOName() {
return getValidFileName(getFileName()) + ".ods";
public void createDocument() throws InterruptedException, ExecutionException {
createDocumentAsynchronous().get();
}
 
private String getPDFName() {
return getValidFileName(getFileName()) + ".pdf";
}
/**
* get the File that is, or must be generated.
*
* @return a file (not null)
* */
public abstract File getGeneratedFile();
 
private String getOO1Name() {
return getValidFileName(getFileName()) + ".sxc";
public File getGeneratedPDFFile() {
return SheetUtils.getFileWithExtension(getGeneratedFile(), ".pdf");
}
 
public SQLRow getRowLanguage() {
190,64 → 281,49
}
 
/**
* retourne l'emplacement de destination d'un Tuple<id (ex:LocationDevis), Nom (ex:Devis)>
*/
public static String getLocationForTuple(Tuple2<String, String> t, boolean pdf) {
 
final String stringProperty = TemplateNXProps.getInstance().getStringProperty(t.get0() + (pdf ? "PDF" : "OO"));
if (stringProperty.equalsIgnoreCase(TemplateNXProps.getInstance().getDefaultStringValue())) {
return stringProperty + File.separator + t.get1();
} else {
return stringProperty;
}
}
 
private File getFile() {
if (this.f != null) {
return f;
}
File f = new File(this.locationOO + File.separator + getOOName());
* Creates the document if needed and returns the generated file (OpenDocument)
* */
public File getOrCreateDocumentFile() throws Exception {
File f = getGeneratedFile();
if (!f.exists()) {
File f2 = new File(this.locationOO + File.separator + getOO1Name());
if (f2.exists()) {
return f2;
return createDocumentAsynchronous().get().getGeneratedFile();
} else {
return f;
}
} else {
return f;
}
}
 
public File getFilePDF() {
File f = new File(this.locationPDF + File.separator + getPDFName());
return f;
}
/**
* Open the document with the native application
*
* @param synchronous
* */
public void openDocument(boolean synchronous) {
Runnable r = new Runnable() {
 
public File getFileWithoutExt() {
File f = new File(this.locationPDF + File.separator + getValidFileName(getFileName()));
return f;
}
 
public File getFileODS() {
File f = new File(this.locationOO + File.separator + getOOName());
return f;
}
 
public void showDocument() {
final File f = getFile();
@Override
public void run() {
File f;
try {
f = getOrCreateDocumentFile();
OOUtils.open(f);
} catch (IOException e) {
ExceptionHandler.handle("Impossible d'ouvrir " + f.getAbsolutePath(), e);
} catch (Exception e) {
ExceptionHandler.handle("Impossible d'ouvrir le document.", e);
}
}
};
if (synchronous) {
r.run();
} else {
Thread thread = new Thread(r, "openDocument: " + getGeneratedFile().getAbsolutePath());
thread.setDaemon(true);
thread.start();
}
 
public void showPreviewDocument() {
if (!isFileOOExist()) {
genere(false, false);
}
final File f = getFile();
 
public void showPreviewDocument() throws Exception {
File f = null;
f = getOrCreateDocumentFile();
PreviewFrame.show(f);
}
 
257,9 → 333,10
}
 
public void fastPrintDocument(short copies) {
final File f = getFile();
 
try {
final File f = getOrCreateDocumentFile();
 
if (!Boolean.getBoolean("org.openconcerto.oo.useODSViewer")) {
 
final Component doc = ComptaPropsConfiguration.getOOConnexion().loadDocument(f, true);
295,9 → 372,9
}
 
public void printDocument() {
final File f = getFile();
 
try {
final File f = getOrCreateDocumentFile();
 
if (!Boolean.getBoolean("org.openconcerto.oo.useODSViewer")) {
 
320,20 → 397,10
}
}
 
public boolean isFileOOExist() {
final File f = getFile();
return f.exists();
}
 
public SQLRow getSQLRow() {
return this.row;
}
 
public boolean isFileODSExist() {
final File f = getFileODS();
return f.exists();
}
 
/**
* Remplace tous les caracteres non alphanumeriques (seul le _ est autorisé) par un -. Cela
* permet d'avoir toujours un nom de fichier valide.
341,14 → 408,14
* @param fileName nom du fichier à créer ex:FACTURE_2007/03/001
* @return un nom fichier valide ex:FACTURE_2007-03-001
*/
public static String getValidFileName(String fileName) {
StringBuffer result = new StringBuffer(fileName.length());
static String getValidFileName(String fileName) {
final StringBuffer result = new StringBuffer(fileName.length());
for (int i = 0; i < fileName.length(); i++) {
char c = fileName.charAt(i);
char ch = fileName.charAt(i);
 
// Si c'est un caractere alphanumerique
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || (c == '_') || (c == ' ')) {
result.append(c);
if (Character.isLetterOrDigit(ch) || (ch == '_') || (ch == ' ')) {
result.append(ch);
} else {
result.append('-');
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/SheetUtils.java
13,6 → 13,8
package org.openconcerto.erp.generationDoc;
 
import org.openconcerto.utils.ExceptionHandler;
 
import java.awt.Graphics2D;
import java.io.File;
import java.io.FileOutputStream;
36,16 → 38,8
 
public class SheetUtils {
 
static SheetUtils instance;
 
public static SheetUtils getInstance() {
if (instance == null) {
instance = new SheetUtils();
}
return instance;
}
 
public File convertToOldFile(String fileName, File pathDest, File fDest) {
public static File convertToOldFile(String fileName, File pathDest, File fDest) {
// FIXME: !!!!!!!!
return convertToOldFile(fileName, pathDest, fDest, ".ods");
}
 
57,7 → 51,7
* @param fDest
* @return
*/
public File convertToOldFile(String fileName, File pathDest, File fDest, String extension) {
public static File convertToOldFile(String fileName, File pathDest, File fDest, String extension) {
if (fDest.exists()) {
int i = 0;
String destName = fileName;
92,7 → 86,7
return fDest;
}
 
public List<File> getHistorique(final String fileName, File pathDest) {
public static List<File> getHistorique(final String fileName, File pathDest) {
File pathOld = new File(pathDest, "Historique");
File[] files = pathOld.listFiles(new FilenameFilter() {
 
118,44 → 112,24
return result;
}
 
public static void main(String[] args) {
final OpenDocument doc = new OpenDocument(new File("E:/Facture_FACT1108-5785.ods"));
try {
SheetUtils.getInstance().convert2PDF(doc, new File("E:/test"), "test");
} catch (Exception exn) {
// TODO Bloc catch auto-généré
exn.printStackTrace();
}
}
public static void convert2PDF(final OpenDocument doc, final File pdfFileToCreate) throws Exception {
 
public void convert2PDF(final OpenDocument doc, final File f, final String fileName) throws Exception {
 
SwingUtilities.invokeLater(new Runnable() {
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
// TODO Raccord de méthode auto-généré
 
// Open the PDF document
Document document = new Document(PageSize.A4, 50, 50, 50, 50);
File outFile = new File(f.getParent(), fileName + ".pdf");
FileOutputStream fileOutputStream;
 
try {
 
fileOutputStream = new FileOutputStream(outFile);
FileOutputStream fileOutputStream = new FileOutputStream(pdfFileToCreate);
 
// Create the writer
PdfWriter writer = PdfWriter.getInstance(document, fileOutputStream);
writer.setPdfVersion(PdfWriter.VERSION_1_6);
writer.setFullCompression();
// writer.setPageEmpty(true);
 
document.open();
// System.out.println(writer.getPageNumber());
// // Create a template and a Graphics2D object
// com.itextpdf.text.Rectangle pageSize = document.getPageSize();
// int w = (int) (pageSize.getWidth() * 0.9);
// int h = (int) (pageSize.getHeight() * 0.95);
 
PdfContentByte cb = writer.getDirectContent();
 
190,11 → 164,27
// writer.close();
fileOutputStream.close();
 
} catch (Exception exn) {
// TODO Bloc catch auto-généré
exn.printStackTrace();
} catch (Exception originalExn) {
ExceptionHandler.handle("Impossible de créer le PDF " + pdfFileToCreate.getAbsolutePath(), originalExn);
}
}
});
}
 
/**
* Get a new file with an other extension
*
* @param the file (ex: Test.ods)
* @param the extension (ex: pdf)
* */
static File getFileWithExtension(File file, String extension) {
if (!extension.startsWith(".")) {
extension = "." + extension;
}
String name = file.getName();
int i = name.lastIndexOf(".");
name = name.substring(0, i) + extension;
final File f = new File(file.getParent(), name);
return f;
}
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/SheetInterface.java
18,11 → 18,12
import org.openconcerto.sql.model.SQLBase;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.utils.StringUtils;
 
import java.io.File;
import java.util.HashMap;
import java.util.Map;
 
 
public abstract class SheetInterface {
 
// SQLRow de l'élément de la table qui est utilisé pour la génération
46,12 → 47,6
// modele du document
protected String modele;
 
// emplacement du fichier OO généré
protected String locationOO;
 
// emplacement du fichier PDF généré
protected String locationPDF;
 
// nom du futur fichier
protected String fileName;
 
64,14 → 59,6
 
protected static final SQLBase base = ((ComptaPropsConfiguration) Configuration.getInstance()).getSQLBaseSociete();
 
public String getLocationOO() {
return this.locationOO;
}
 
public String getLocationPDF() {
return this.locationPDF;
}
 
public int getNbPage() {
return this.nbPage;
}
114,6 → 101,17
return this.mCell;
}
 
protected abstract String getYear();
 
public File getDocumentOutputDirectory() {
return new File(DocumentLocalStorageManager.getInstance().getDocumentOutputDirectory(this.getTemplateId()), getYear());
}
 
public File getPDFOutputDirectory() {
return new File(DocumentLocalStorageManager.getInstance().getPDFOutputDirectory(this.getTemplateId()), getYear());
}
 
protected abstract void createMap();
 
public abstract String getTemplateId();
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/gestcomm/AvoirFournisseurXmlSheet.java
13,42 → 13,29
package org.openconcerto.erp.generationDoc.gestcomm;
 
import org.openconcerto.erp.generationDoc.AbstractSheetXml;
import org.openconcerto.erp.generationDoc.SheetXml;
import org.openconcerto.erp.generationDoc.AbstractSheetXMLWithDate;
import org.openconcerto.erp.preferences.PrinterNXProps;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.utils.Tuple2;
 
import java.io.File;
import java.util.Calendar;
import java.util.Date;
public class AvoirFournisseurXmlSheet extends AbstractSheetXMLWithDate {
 
public class AvoirFournisseurXmlSheet extends AbstractSheetXml {
public static final String TEMPLATE_ID = "AvoirF";
public static final String TEMPLATE_PROPERTY_NAME = "LocationAvoirF";
 
private static final Tuple2<String, String> tuple = Tuple2.create("LocationAvoirF", "Avoir Fournisseur");
 
public static Tuple2<String, String> getTuple2Location() {
return tuple;
}
 
public AvoirFournisseurXmlSheet(SQLRow row) {
super(row);
this.printer = PrinterNXProps.getInstance().getStringProperty("BonPrinter");
this.elt = Configuration.getInstance().getDirectory().getElement("AVOIR_FOURNISSEUR");
Calendar cal = Calendar.getInstance();
cal.setTime((Date) row.getObject("DATE"));
this.locationOO = SheetXml.getLocationForTuple(tuple, false) + File.separator + cal.get(Calendar.YEAR);
this.locationPDF = SheetXml.getLocationForTuple(tuple, true) + File.separator + cal.get(Calendar.YEAR);
}
 
@Override
public String getDefaultModele() {
// TODO Raccord de méthode auto-généré
return "AvoirF";
public String getDefaultTemplateId() {
return TEMPLATE_ID;
}
 
public String getFileName() {
return getValidFileName("AvoirF_" + this.row.getID());
@Override
public String getName() {
return "AvoirF_" + this.row.getID();
}
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/gestcomm/PointageXmlSheet.java
14,7 → 14,6
package org.openconcerto.erp.generationDoc.gestcomm;
 
import org.openconcerto.erp.generationDoc.AbstractListeSheetXml;
import org.openconcerto.erp.generationDoc.SheetXml;
import org.openconcerto.erp.preferences.PrinterNXProps;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.element.SQLElement;
36,16 → 35,18
 
public class PointageXmlSheet extends AbstractListeSheetXml {
 
public static final String TEMPLATE_ID = "Pointage";
public static final String TEMPLATE_PROPERTY_NAME = DEFAULT_PROPERTY_NAME;
private Map<Integer, List<Map<String, Object>>> listAllSheetValues;
private Map<Integer, Map<Integer, String>> styleAllSheetValues;
private Map<Integer, Map<String, Object>> mapAllSheetValues;
private Calendar c = Calendar.getInstance();
private Date date = new Date();
private final long MILLIS_IN_HOUR = 3600000;
 
public PointageXmlSheet(int mois, int year) {
this.printer = PrinterNXProps.getInstance().getStringProperty("BonPrinter");
this.mapAllSheetValues = new HashMap<Integer, Map<String, Object>>();
this.locationOO = SheetXml.getLocationForTuple(tupleDefault, false);
this.locationPDF = SheetXml.getLocationForTuple(tupleDefault, true);
this.c.set(Calendar.DAY_OF_MONTH, 1);
this.c.set(Calendar.YEAR, year);
this.c.set(Calendar.MONTH, mois);
57,11 → 58,14
}
 
@Override
public String getDefaultModele() {
return "Pointage";
public String getName() {
return "Pointage" + this.date;
}
 
private long oneHour = 3600000;
@Override
public String getDefaultTemplateId() {
return TEMPLATE_ID;
}
 
protected void createListeValues() {
SQLElement eltPointage = Configuration.getInstance().getDirectory().getElement("POINTAGE");
124,6 → 128,7
sel.setHaving(Where.createRaw("COUNT (\"POINTAGE\".\"ID_USER_COMMON\") > 0", tablePointage.getField("ID_USER_COMMON")));
sel.addFieldOrder(tableUser.getField("NOM"));
System.err.println(sel.asString());
@SuppressWarnings("unchecked")
List<SQLRow> listUser = (List<SQLRow>) Configuration.getInstance().getBase().getDataSource().execute(sel.asString(), SQLRowListRSH.createFromSelect(sel, tableUser));
 
String entete = "Horaires de travail du mois de " + formatMonth.format(d1);
142,7 → 147,7
mapSheetValue.put("A1", userName);
mapSheetValue.put("F1", entete);
 
long tempsDePause = this.oneHour;
long tempsDePause = this.MILLIS_IN_HOUR;
 
// calcul du temps de pause si possible
if (row.getObject("HEURE_MATIN_D") != null && row.getObject("MINUTE_MATIN_D") != null && row.getObject("HEURE_MIDI_A") != null && row.getObject("MINUTE_MIDI_A") != null) {
173,8 → 178,8
// Temps travaillé dans la semaine
if (semaine > 0 && semaine != this.c.get(Calendar.WEEK_OF_YEAR)) {
Map<String, Object> mValues2 = new HashMap<String, Object>();
long hour = heureTotalSemaine / this.oneHour;
long minute = (heureTotalSemaine % this.oneHour) / 60000;
long hour = heureTotalSemaine / this.MILLIS_IN_HOUR;
long minute = (heureTotalSemaine % this.MILLIS_IN_HOUR) / 60000;
mValues2.put("HEURE_TOTAL", hour + "h" + minute);
heureTotalSemaine = 0;
listValues.add(mValues2);
207,6 → 212,7
sel2.setWhere(wDay);
sel2.addFieldOrder(table2.getField("DATE"));
 
@SuppressWarnings("unchecked")
List<SQLRow> list2 = (List<SQLRow>) Configuration.getInstance().getBase().getDataSource().execute(sel2.asString(), SQLRowListRSH.createFromSelect(sel2, table2));
 
if (list2.size() > 2) {
258,8 → 264,8
System.err.println("time " + time + " Worked :" + timeWorked);
heureTotalSemaine += timeWorked;
heureTotalMois += timeWorked;
long hour = timeWorked / this.oneHour;
long minute = (timeWorked % this.oneHour) / 60000;
long hour = timeWorked / this.MILLIS_IN_HOUR;
long minute = (timeWorked % this.MILLIS_IN_HOUR) / 60000;
mValues.put("HEURE_TOTAL", hour + "h" + minute);
 
} else {
278,8 → 284,8
 
// Heure de la derniere semaine
Map<String, Object> mValues2 = new HashMap<String, Object>();
long hour = heureTotalSemaine / this.oneHour;
long minute = (heureTotalSemaine % this.oneHour) / 60000;
long hour = heureTotalSemaine / this.MILLIS_IN_HOUR;
long minute = (heureTotalSemaine % this.MILLIS_IN_HOUR) / 60000;
mValues2.put("HEURE_TOTAL", hour + "h" + minute);
heureTotalSemaine = 0;
listValues.add(mValues2);
289,8 → 295,8
 
// Heure total du mois
Map<String, Object> mValuesMois = new HashMap<String, Object>();
hour = heureTotalMois / this.oneHour;
minute = (heureTotalMois % this.oneHour) / 60000;
hour = heureTotalMois / this.MILLIS_IN_HOUR;
minute = (heureTotalMois % this.MILLIS_IN_HOUR) / 60000;
mValuesMois.put("HEURE_TOTAL", hour + "h" + minute);
heureTotalMois = 0;
 
315,9 → 321,4
 
}
 
Date date = new Date();
 
public String getFileName() {
return getValidFileName("Pointage" + this.date);
}
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/gestcomm/FicheRelanceSheet.java
14,11 → 14,9
package org.openconcerto.erp.generationDoc.gestcomm;
 
import org.openconcerto.erp.generationDoc.AbstractListeSheetXml;
import org.openconcerto.erp.generationDoc.SheetXml;
import org.openconcerto.erp.preferences.PrinterNXProps;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.utils.GestionDevise;
import org.openconcerto.utils.Tuple2;
 
import java.text.DateFormat;
import java.text.SimpleDateFormat;
31,29 → 29,23
public class FicheRelanceSheet extends AbstractListeSheetXml {
 
private static final DateFormat dateFormat = new SimpleDateFormat("dd/MM/yy");
private SQLRow row;
public static final String TEMPLATE_ID = "FicheRelance";
public static final String TEMPLATE_PROPERTY_NAME = DEFAULT_PROPERTY_NAME;
 
public static Tuple2<String, String> getTuple2Location() {
return tupleDefault;
}
 
public FicheRelanceSheet(SQLRow row) {
this.printer = PrinterNXProps.getInstance().getStringProperty("BonPrinter");
this.row = row;
this.locationOO = SheetXml.getLocationForTuple(tupleDefault, false);
this.locationPDF = SheetXml.getLocationForTuple(tupleDefault, true);
}
 
@Override
public String getDefaultModele() {
return "FicheRelance";
public String getDefaultTemplateId() {
return TEMPLATE_ID;
}
 
@Override
protected void createListeValues() {
// TODO Auto-generated method stub
final Map<String, Object> values = new HashMap<String, Object>();
List<Map<String, Object>> listValues = new ArrayList<Map<String, Object>>();
final List<Map<String, Object>> listValues = new ArrayList<Map<String, Object>>();
 
final SQLRow clientRow = this.row.getForeignRow("ID_CLIENT");
final SQLRow factureRow = this.row.getForeignRow("ID_SAISIE_VENTE_FACTURE");
70,8 → 62,9
 
}
 
public String getFileName() {
return getValidFileName("FicheRelance" + new Date().getTime());
@Override
public String getName() {
return "FicheRelance" + new Date().getTime();
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/gestcomm/CourrierClientSheet.java
15,12 → 15,10
 
import org.openconcerto.erp.config.ComptaPropsConfiguration;
import org.openconcerto.erp.generationDoc.AbstractJOOReportsSheet;
import org.openconcerto.erp.generationDoc.AbstractSheetXml;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.utils.Tuple2;
 
import java.util.Date;
import java.util.HashMap;
31,24 → 29,21
 
private SQLRow rowCourrier;
 
private static final Tuple2<String, String> tuple = Tuple2.create("LocationCourrier", "Courrier");
public static final String TEMPLATE_ID = "Courrier";
public static final String TEMPLATE_PROPERTY_NAME = "LocationCourrier";
 
public static Tuple2<String, String> getTuple2Location() {
return tuple;
}
 
public CourrierClientSheet(SQLRow row) {
super();
this.rowCourrier = row;
Date d = (Date) this.rowCourrier.getObject("DATE");
String year = yearFormat.format(d);
init(year, "Courrier.odt", "CourrierPrinter", tuple);
init(year, "Courrier.odt", "CourrierPrinter");
}
 
/**
* @return une Map contenant les valeurs à remplacer dans la template
*/
protected Map createMap() {
protected Map<String, Object> createMap() {
 
Map<String, Object> m = new HashMap<String, Object>();
 
84,8 → 79,7
}
 
public String getFileName() {
String fileName = "Courrier_" + AbstractSheetXml.getValidFileName(this.rowCourrier.getString("NUMERO"));
return fileName;
return "Courrier_" + this.rowCourrier.getString("NUMERO");
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/gestcomm/RelanceSheet.java
16,13 → 16,11
import org.openconcerto.erp.config.ComptaPropsConfiguration;
import org.openconcerto.erp.core.finance.payment.element.ModeDeReglementSQLElement;
import org.openconcerto.erp.generationDoc.AbstractJOOReportsSheet;
import org.openconcerto.erp.generationDoc.AbstractSheetXml;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.Where;
import org.openconcerto.utils.GestionDevise;
import org.openconcerto.utils.Tuple2;
 
import java.util.Date;
import java.util.HashMap;
33,11 → 31,9
 
private SQLRow rowRelance;
 
private static final Tuple2<String, String> tuple = Tuple2.create("LocationRelance", "Relance");
public static final String TEMPLATE_ID = "Relance";
 
public static Tuple2<String, String> getTuple2Location() {
return tuple;
}
public static final String TEMPLATE_PROPERTY_NAME = "LocationRelance";
 
/**
* @return une Map contenant les valeurs à remplacer dans la template
122,10 → 118,11
sel.addSelect(this.rowRelance.getTable().getKey());
sel.setWhere(new Where(this.rowRelance.getTable().getField("ID_SAISIE_VENTE_FACTURE"), "=", this.rowRelance.getInt("ID_SAISIE_VENTE_FACTURE")));
sel.addFieldOrder(this.rowRelance.getTable().getField("DATE").getFullName());
List<Map> listResult = Configuration.getInstance().getBase().getDataSource().execute(sel.asString());
@SuppressWarnings("unchecked")
List<Map<String, Number>> listResult = Configuration.getInstance().getBase().getDataSource().execute(sel.asString());
if (listResult != null && listResult.size() > 0) {
Map o = listResult.get(0);
Number n = (Number) o.get(this.rowRelance.getTable().getKey().getName());
Map<String, Number> o = listResult.get(0);
Number n = o.get(this.rowRelance.getTable().getKey().getName());
SQLRow rowOldRelance = this.rowRelance.getTable().getRow(n.intValue());
Date dOldRelance = (Date) rowOldRelance.getObject("DATE");
map.put("DatePremiereRelance", dateFormat2.format(dOldRelance));
144,7 → 141,7
 
final String string = rowLettre.getString("MODELE");
System.err.println(this.locationTemplate + "/" + string);
init(year, string, "RelancePrinter", tuple);
init(year, string, "RelancePrinter");
}
 
protected boolean savePDF() {
152,7 → 149,6
}
 
public String getFileName() {
String fileName = "Relance_" + AbstractSheetXml.getValidFileName(this.rowRelance.getString("NUMERO"));
return fileName;
return "Relance_" + this.rowRelance.getString("NUMERO");
}
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/gestcomm/ReleveChequeEmisSheet.java
41,19 → 41,23
createMap();
}
 
private static final Tuple2<String, String> tuple = Tuple2.create("LocationReleveCheque", "Relevé chéques émis");
public static final String TEMPLATE_ID = "ReleveChequeEmis";
public static final String TEMPLATE_PROPERTY_NAME = "LocationReleveCheque";
 
public static Tuple2<String, String> getTuple2Location() {
return tuple;
}
 
private void init() {
this.modele = "ReleveChequeEmis.ods";
// this.printer = PrinterNXProps.getInstance().getStringProperty("FacturePrinter");
this.locationOO = SheetXml.getLocationForTuple(tuple, false);
this.locationPDF = SheetXml.getLocationForTuple(tuple, true);
}
 
@Override
public String getTemplateId() {
return TEMPLATE_ID;
}
 
@Override
protected String getYear() {
return "";
}
 
protected void createMap() {
 
long montantTotal = 0;
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/gestcomm/ReleveChequeSheet.java
15,13 → 15,11
 
import org.openconcerto.erp.config.ComptaPropsConfiguration;
import org.openconcerto.erp.generationDoc.AbstractListeSheetXml;
import org.openconcerto.erp.generationDoc.SheetXml;
import org.openconcerto.erp.preferences.PrinterNXProps;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.utils.GestionDevise;
import org.openconcerto.utils.Tuple2;
 
import java.text.DateFormat;
import java.text.SimpleDateFormat;
39,7 → 37,7
private long total = 0;
private long nb = 0;
private boolean apercu = false;
 
private static final SQLTable tableCheque = base.getTable("CHEQUE_A_ENCAISSER");
private List<Integer> listeIds;
 
public ReleveChequeSheet(List<Integer> listeIds, Date date) {
46,16 → 44,11
this(listeIds, date, false);
}
 
private static final Tuple2<String, String> tuple = Tuple2.create("LocationReleveChequeCli", "Relevé chéques clients");
public static final String TEMPLATE_ID = "ReleveCheque";
public static final String TEMPLATE_PROPERTY_NAME = "LocationReleveChequeCli";
 
public static Tuple2<String, String> getTuple2Location() {
return tuple;
}
 
public ReleveChequeSheet(List<Integer> listeIds, Date date, boolean apercu) {
this.printer = PrinterNXProps.getInstance().getStringProperty("BonPrinter");
this.locationOO = SheetXml.getLocationForTuple(tuple, false);
this.locationPDF = SheetXml.getLocationForTuple(tuple, true);
this.date = date;
this.apercu = apercu;
this.listeIds = listeIds;
62,11 → 55,14
}
 
@Override
public String getDefaultModele() {
return "ReleveCheque";
public String getName() {
return "ReleveCheque" + this.date.getTime();
}
 
private static final SQLTable tableCheque = base.getTable("CHEQUE_A_ENCAISSER");
@Override
public String getDefaultTemplateId() {
return TEMPLATE_ID;
}
 
protected void createListeValues() {
 
113,8 → 109,4
this.mapAllSheetValues.put(0, values);
}
 
public String getFileName() {
return getValidFileName("ReleveCheque" + this.date.getTime());
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/gestcomm/AvoirClientXmlSheet.java
14,27 → 14,16
package org.openconcerto.erp.generationDoc.gestcomm;
 
import org.openconcerto.erp.config.ComptaPropsConfiguration;
import org.openconcerto.erp.generationDoc.AbstractSheetXml;
import org.openconcerto.erp.generationDoc.SheetXml;
import org.openconcerto.erp.generationDoc.AbstractSheetXMLWithDate;
import org.openconcerto.erp.preferences.PrinterNXProps;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.utils.Tuple2;
 
import java.io.File;
import java.util.Calendar;
import java.util.Date;
public class AvoirClientXmlSheet extends AbstractSheetXMLWithDate {
 
public class AvoirClientXmlSheet extends AbstractSheetXml {
public static final String TEMPLATE_ID = "Avoir";
public static final String TEMPLATE_PROPERTY_NAME = "LocationAvoir";
 
private String startName;
 
private static final Tuple2<String, String> tuple = Tuple2.create("LocationAvoir", "Avoir");
 
public static Tuple2<String, String> getTuple2Location() {
return tuple;
}
 
@Override
public SQLRow getRowLanguage() {
SQLRow rowClient = this.row.getForeignRow("ID_CLIENT");
49,24 → 38,16
super(row);
this.printer = PrinterNXProps.getInstance().getStringProperty("BonPrinter");
this.elt = Configuration.getInstance().getDirectory().getElement("AVOIR_CLIENT");
}
 
Calendar cal = Calendar.getInstance();
cal.setTime((Date) row.getObject("DATE"));
this.locationOO = SheetXml.getLocationForTuple(tuple, false) + File.separator + cal.get(Calendar.YEAR);
this.locationPDF = SheetXml.getLocationForTuple(tuple, true) + File.separator + cal.get(Calendar.YEAR);
 
this.startName = "Avoir_";
 
@Override
public String getDefaultTemplateId() {
return TEMPLATE_ID;
}
 
@Override
public String getDefaultModele() {
 
return "Avoir";
public String getName() {
return "Avoir_" + this.row.getString("NUMERO");
}
 
public String getFileName() {
 
return getValidFileName(this.startName + this.row.getString("NUMERO"));
}
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/gestcomm/EtatVentesXmlSheet.java
14,6 → 14,7
package org.openconcerto.erp.generationDoc.gestcomm;
 
import org.openconcerto.erp.generationDoc.AbstractListeSheetXml;
import org.openconcerto.erp.generationDoc.DocumentLocalStorageManager;
import org.openconcerto.erp.generationDoc.SheetXml;
import org.openconcerto.erp.preferences.PrinterNXProps;
import org.openconcerto.sql.Configuration;
25,6 → 26,7
import org.openconcerto.sql.model.Where;
import org.openconcerto.utils.Tuple2;
 
import java.io.File;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
46,12 → 48,12
 
private static final DateFormat dateFormat = new SimpleDateFormat("dd/MM/yy");
 
public static final String TEMPLATE_ID = "EtatVentes";
 
public static final String TEMPLATE_PROPERTY_NAME = DEFAULT_PROPERTY_NAME;
 
private Timestamp du, au;
 
public static Tuple2<String, String> getTuple2Location() {
return tupleDefault;
}
 
public EtatVentesXmlSheet(Date du, Date au) {
this.printer = PrinterNXProps.getInstance().getStringProperty("BonPrinter");
du.setHours(0);
61,18 → 63,18
this.du = new Timestamp(du.getTime());
this.au = new Timestamp(au.getTime());
 
this.locationOO = SheetXml.getLocationForTuple(tupleDefault, false);
this.locationPDF = SheetXml.getLocationForTuple(tupleDefault, true);
}
 
 
 
@Override
public String getDefaultModele() {
 
return "EtatVentes";
public String getDefaultTemplateId() {
return TEMPLATE_ID;
}
 
public String getFileName() {
return getValidFileName("EtatVentes" + new Date().getTime());
@Override
public String getName() {
return "EtatVentes" + new Date().getTime();
}
 
protected void createListeValues() {
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/gestcomm/CommandeXmlSheet.java
13,25 → 13,16
package org.openconcerto.erp.generationDoc.gestcomm;
 
import org.openconcerto.erp.generationDoc.AbstractSheetXml;
import org.openconcerto.erp.generationDoc.SheetXml;
import org.openconcerto.erp.generationDoc.AbstractSheetXMLWithDate;
import org.openconcerto.erp.preferences.PrinterNXProps;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.utils.Tuple2;
 
import java.io.File;
import java.util.Calendar;
import java.util.Date;
public class CommandeXmlSheet extends AbstractSheetXMLWithDate {
 
public class CommandeXmlSheet extends AbstractSheetXml {
public static final String TEMPLATE_ID = "Commande";
public static final String TEMPLATE_PROPERTY_NAME = "LocationCmd";
 
private static final Tuple2<String, String> tuple = Tuple2.create("LocationCmd", "Commande fournisseur");
 
public static Tuple2<String, String> getTuple2Location() {
return tuple;
}
 
@Override
public SQLRow getRowLanguage() {
SQLRow rowFournisseur = this.row.getForeignRow("ID_FOURNISSEUR");
47,21 → 38,15
super(row);
this.printer = PrinterNXProps.getInstance().getStringProperty("CmdPrinter");
this.elt = Configuration.getInstance().getDirectory().getElement("COMMANDE");
Calendar cal = Calendar.getInstance();
cal.setTime((Date) row.getObject("DATE"));
this.locationOO = SheetXml.getLocationForTuple(tuple, false) + File.separator + cal.get(Calendar.YEAR);
this.locationPDF = SheetXml.getLocationForTuple(tuple, false) + File.separator + cal.get(Calendar.YEAR);
 
}
 
@Override
public String getDefaultModele() {
 
return "Commande";
public String getDefaultTemplateId() {
return TEMPLATE_ID;
}
 
public String getFileName() {
 
return getValidFileName("Commande_" + row.getString("NUMERO"));
@Override
public String getName() {
return "Commande_" + row.getString("NUMERO");
}
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/OOXMLField.java
179,7 → 179,7
String condField = this.elt.getAttributeValue("conditionField");
String condValue = this.elt.getAttributeValue("conditionExpValue");
 
boolean bIsCondValid = condValue == null || this.row.getObject(condField).toString().equalsIgnoreCase(condValue);
boolean bIsCondValid = condValue == null || !this.row.getObject(condField).toString().equalsIgnoreCase(condValue);
if (condValue == null) {
boolean bIsBooleanCondValid = false;
if (condField == null) {
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/TemplateManager.java
New file
0,0 → 1,165
/*
* 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.erp.generationDoc;
 
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
 
public class TemplateManager {
private static TemplateManager instance = new TemplateManager();
private List<TemplateProvider> providers = new ArrayList<TemplateProvider>();
private Map<String, TemplateProvider> defaultMap = new HashMap<String, TemplateProvider>();
private TemplateProvider defautProvider;
private List<String> knownTemplateIds = new ArrayList<String>();
 
public static TemplateManager getInstance() {
return instance;
}
 
public void add(TemplateProvider provider) {
this.providers.add(provider);
}
 
public void remove(TemplateProvider provider) {
this.providers.remove(provider);
final Set<String> keys = defaultMap.keySet();
for (String key : keys) {
if (defaultMap.get(key).equals(provider)) {
defaultMap.remove(key);
}
}
}
 
public void setDefaultProvider(String templateId, TemplateProvider provider) {
defaultMap.put(templateId, provider);
knownTemplateIds.add(templateId);
}
 
public void setDefaultProvider(TemplateProvider provider) {
this.defautProvider = provider;
}
 
/**
* Get the template using first the default template providers
*
*
* @return the template file, IllegalStateException if no template is found
* */
public InputStream getTemplate(String templateId, String language, String type) {
TemplateProvider provider = defaultMap.get(templateId);
if (provider == null) {
for (TemplateProvider pr : providers) {
InputStream stream = pr.getTemplate(templateId, language, type);
if (stream != null) {
return stream;
}
}
if (defautProvider == null) {
throw new IllegalStateException("Not default provider registered when using template id:" + templateId + " language:" + language + " type:" + type);
}
return defautProvider.getTemplate(templateId, language, type);
}
return provider.getTemplate(templateId, language, type);
}
 
/**
* Get the template print configuration using first the default template providers
*
*
* @return the template file, IllegalStateException if no template is found
* */
public InputStream getTemplatePrintConfiguration(String templateId, String language, String type) {
TemplateProvider provider = defaultMap.get(templateId);
if (provider == null) {
for (TemplateProvider pr : providers) {
InputStream stream = pr.getTemplatePrintConfiguration(templateId, language, type);
if (stream != null) {
return stream;
}
}
if (defautProvider == null) {
throw new IllegalStateException("Not default provider registered when using template id:" + templateId + " langage:" + language + " type:" + type);
}
return defautProvider.getTemplatePrintConfiguration(templateId, language, type);
}
return provider.getTemplatePrintConfiguration(templateId, language, type);
}
 
/**
* Get the template cofiguration using first the default template providers
*
*
* @return the template file, IllegalStateException if no template is found
* */
public InputStream getTemplateConfiguration(String templateId, String language, String type) {
TemplateProvider provider = defaultMap.get(templateId);
if (provider == null) {
for (TemplateProvider pr : providers) {
InputStream stream = pr.getTemplateConfiguration(templateId, language, type);
if (stream != null) {
return stream;
}
}
if (defautProvider == null) {
throw new IllegalStateException("Not default provider registered when using template id:" + templateId + " langage:" + language + " type:" + type);
}
return defautProvider.getTemplateConfiguration(templateId, language, type);
}
return provider.getTemplateConfiguration(templateId, language, type);
}
 
public InputStream getTemplate(String templateId) {
return getTemplate(templateId, null, null);
}
 
public void register(String templateId) {
knownTemplateIds.add(templateId);
}
 
public void dump() {
System.out.println(this.getClass().getCanonicalName());
System.out.println("Default provider: " + this.defautProvider);
System.out.println("Templates:");
for (String templateId : this.knownTemplateIds) {
try {
InputStream stream = this.getTemplate(templateId);
if (stream == null) {
System.out.println(rightAlign("'" + templateId + "'") + " : stream missing");
} else {
System.out.println(rightAlign("'" + templateId + "'") + " : ok");
}
} catch (Exception e) {
System.out.println(rightAlign("'" + templateId + "'") + ": stream error");
}
}
 
}
 
private String rightAlign(String s) {
String r = s;
int n = 20 - s.length();
for (int i = 0; i < n; i++) {
r = ' ' + r;
}
return r;
}
 
public boolean isKnwonTemplate(String templateId) {
return this.knownTemplateIds.contains(templateId);
}
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/DefaultLocalTemplateProvider.java
New file
0,0 → 1,66
/*
* 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.erp.generationDoc;
 
import java.io.File;
 
public class DefaultLocalTemplateProvider extends AbstractLocalTemplateProvider {
 
private File baseDirectory;
 
public DefaultLocalTemplateProvider() {
baseDirectory = new File("Configuration/Template/Default");
}
 
public void setBaseDirectory(File dir) {
this.baseDirectory = dir;
}
 
@Override
public File getTemplateFile(String templateId, String langage, String type) {
File file = getFile(templateId, langage, type);
if (!file.exists()) {
file = getFile(templateId + ".ods", langage, type);
}
return file;
}
 
private File getFile(String templateId, String langage, String type) {
String path = templateId;
if (type != null) {
path += type;
}
if (langage != null) {
path = langage + File.separatorChar + path;
}
File file = new File(baseDirectory, path);
if (!file.exists()) {
file = new File("Configuration/Template/Default", path);
}
return file;
}
 
@Override
public String getTemplatePath(String templateId, String langage, String type) {
String path = "Configuration/Template/Default";
if (type != null) {
path += type;
}
if (langage != null) {
path = langage + '/' + path;
}
return path;
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/AbstractSheetXMLWithDate.java
New file
0,0 → 1,47
/*
* 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.erp.generationDoc;
 
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.utils.StringUtils;
 
import java.io.File;
import java.util.Calendar;
 
public abstract class AbstractSheetXMLWithDate extends AbstractSheetXml {
 
public AbstractSheetXMLWithDate(SQLRow row) {
super(row);
}
 
protected final String getYear() {
final Calendar cal = this.row.getDate("DATE");
return cal == null ? "Date inconnue" : String.valueOf(cal.get(Calendar.YEAR));
}
 
@Override
public File getDocumentOutputDirectoryP() {
return new File(DocumentLocalStorageManager.getInstance().getDocumentOutputDirectory(this.getTemplateId()), getYear());
}
 
@Override
public File getPDFOutputDirectoryP() {
return new File(DocumentLocalStorageManager.getInstance().getPDFOutputDirectory(this.getTemplateId()), getYear());
}
 
@Override
public String getStoragePathP() {
return StringUtils.firstUp(this.elt.getPluralName() + File.separator + getYear());
}
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/DocumentLocalStorageManager.java
New file
0,0 → 1,160
/*
* 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.erp.generationDoc;
 
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
public class DocumentLocalStorageManager {
private static DocumentLocalStorageManager instance = new DocumentLocalStorageManager();
private Map<String, File> dirs = new HashMap<String, File>();
private Map<String, File> dirsPDF = new HashMap<String, File>();
private File documentDefaultDirectory;
private File PDFDefaultDirectory;
 
public static DocumentLocalStorageManager getInstance() {
return instance;
}
 
/**
* Returns the directory to store the document created from a template
*
* @param templateId : the id of the template used to create the document
* */
public File getDocumentOutputDirectory(String templateId) {
if (templateId == null) {
throw new IllegalArgumentException("null template id");
}
final File f = dirs.get(templateId);
if (f != null) {
return f;
}
return documentDefaultDirectory;
 
}
 
/**
* Returns the directory to store the PDF document created from a template
*
* @param templateId : the id of the template used to create the document
* */
public File getPDFOutputDirectory(String templateId) {
if (templateId == null) {
throw new IllegalArgumentException("null template id");
}
final File f = dirsPDF.get(templateId);
if (f != null) {
return f;
}
return PDFDefaultDirectory;
}
 
public void addDocumentDirectory(String templateId, File directory) {
if (templateId == null) {
throw new IllegalArgumentException("null template id");
}
this.dirs.put(templateId, directory);
TemplateManager.getInstance().register(templateId);
}
 
public void removeDocumentDirectory(String templateId, File directory) {
if (templateId == null) {
throw new IllegalArgumentException("null template id");
}
this.dirs.remove(templateId);
}
 
public void addPDFDirectory(String templateId, File directory) {
if (templateId == null) {
throw new IllegalArgumentException("null template id");
}
this.dirsPDF.put(templateId, directory);
TemplateManager.getInstance().register(templateId);
}
 
public void removePDFDirectory(String templateId, File directory) {
if (templateId == null) {
throw new IllegalArgumentException("null template id");
}
this.dirsPDF.remove(templateId);
}
 
public void setDocumentDefaultDirectory(File directory) {
this.documentDefaultDirectory = directory;
}
 
public void setPDFDefaultDirectory(File directory) {
this.PDFDefaultDirectory = directory;
}
 
public List<File> getAllPDFDirectories() {
List<File> list = new ArrayList<File>();
for (String id : this.dirsPDF.keySet()) {
list.add(this.dirsPDF.get(id));
}
return list;
}
 
public List<File> getAllDocumentDirectories() {
List<File> list = new ArrayList<File>();
for (String id : this.dirs.keySet()) {
list.add(this.dirs.get(id));
}
return list;
}
 
public void dump() {
System.out.println(this.getClass().getCanonicalName());
System.out.println("Default document directory: " + getAndTest(this.documentDefaultDirectory));
System.out.println("Default PFD directory : " + getAndTest(this.PDFDefaultDirectory));
System.out.println("Document directories:");
for (String key : this.dirs.keySet()) {
System.out.println(rightAlign("'" + key + "'") + " : " + getAndTest(this.dirs.get(key)));
}
System.out.println("PDF directories:");
for (String key : this.dirsPDF.keySet()) {
System.out.println(rightAlign("'" + key + "'") + " : " + getAndTest(this.dirsPDF.get(key)));
}
}
 
private String rightAlign(String s) {
String r = s;
int n = 20 - s.length();
for (int i = 0; i < n; i++) {
r = ' ' + r;
}
return r;
}
 
private String getAndTest(File dir) {
if (dir == null) {
return "null !!!!!!";
}
String r = "'" + dir.getAbsolutePath() + "'";
if (dir.exists()) {
if (!dir.isDirectory()) {
r += " is not a directory!!";
} else if (!dir.canWrite()) {
r += " is write protected!!";
}
} else {
r += " does not exist !!";
}
return r;
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/OOgenerationXML.java
73,8 → 73,8
 
private static int answer = JOptionPane.NO_OPTION;
 
public static synchronized File genere(String modele, String pathDest, final String fileDest, SQLRow row, SQLRow rowLanguage) {
 
public static synchronized File createDocument(String templateId, File outputDirectory, final String expectedFileName, SQLRow row, SQLRow rowLanguage) {
final String langage = rowLanguage != null ? rowLanguage.getString("CHEMIN") : null;
cacheStyle.clear();
OOXMLCache.clearCache();
rowsEltCache.clear();
81,7 → 81,7
taxe.clear();
cacheForeign.clear();
 
File fDest = new File(pathDest, fileDest + ".ods");
File fDest = new File(outputDirectory, expectedFileName);
 
if (fDest.exists()) {
 
112,20 → 112,20
SAXBuilder builder = new SAXBuilder();
try {
 
if (needAnnexe(modele, row, rowLanguage)) {
try {
if (needAnnexe(templateId, row, rowLanguage)) {
// check if it exists
getOOTemplate(modele + "_annexe", rowLanguage);
modele += "_annexe";
System.err.println("modele With annexe " + modele);
} catch (FileNotFoundException e) {
 
final String annexeTemplateId = templateId + "_annexe";
InputStream annexeStream = TemplateManager.getInstance().getTemplate(annexeTemplateId, langage, null);
if (annexeStream != null) {
templateId = annexeTemplateId;
System.err.println("modele With annexe " + templateId);
}
}
 
System.err.println("modele " + modele);
System.err.println("Using template id: " + templateId);
final InputStream xmlConfiguration = TemplateManager.getInstance().getTemplateConfiguration(templateId, langage, null);
 
Document doc = builder.build(getXmlTemplate(modele, rowLanguage));
Document doc = builder.build(xmlConfiguration);
 
// On initialise un nouvel élément racine avec l'élément racine du document.
Element racine = doc.getRootElement();
134,7 → 134,9
List<Element> listElts = racine.getChildren("element");
 
// Création et génération du fichier OO
SpreadSheet spreadSheet = SpreadSheet.create(new ODPackage(getOOTemplate(modele, rowLanguage)));
final InputStream template = TemplateManager.getInstance().getTemplate(templateId, langage, null);
 
final SpreadSheet spreadSheet = new ODPackage(template).getSpreadSheet();
try {
// On remplit les cellules de la feuille
parseElementsXML(listElts, row, spreadSheet);
147,10 → 149,10
parseTableauXML(tableChild, row, spreadSheet, rowLanguage);
}
} catch (Exception e) {
ExceptionHandler.handle("Impossible de remplir le document " + modele + " " + ((rowLanguage == null) ? "" : rowLanguage.getString("CHEMIN")), e);
ExceptionHandler.handle("Impossible de remplir le document " + templateId + " " + ((rowLanguage == null) ? "" : rowLanguage.getString("CHEMIN")), e);
}
// Sauvegarde du fichier
return saveSpreadSheet(spreadSheet, new File(pathDest), fileDest, modele, rowLanguage);
return saveSpreadSheet(spreadSheet, outputDirectory, expectedFileName, templateId, rowLanguage);
 
} catch (final JDOMException e) {
 
157,7 → 159,7
e.printStackTrace();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ExceptionHandler.handle("Erreur lors de la génération du fichier " + fileDest, e);
ExceptionHandler.handle("Erreur lors de la génération du fichier " + expectedFileName, e);
}
});
} catch (final IOException e) {
165,7 → 167,7
e.printStackTrace();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ExceptionHandler.handle("Erreur lors de la création du fichier " + fileDest, e);
ExceptionHandler.handle("Erreur lors de la création du fichier " + expectedFileName, e);
}
});
}
706,8 → 708,9
* @return un File pointant sur le fichier créé
* @throws IOException
*/
private static File saveSpreadSheet(SpreadSheet ssheet, File pathDest, String fileName, String modele, SQLRow rowLanguage) throws IOException {
 
private static File saveSpreadSheet(SpreadSheet ssheet, File pathDest, String fileName, String templateId, SQLRow rowLanguage) throws IOException {
final String langage = rowLanguage != null ? rowLanguage.getString("CHEMIN") : null;
// Test des arguments
if (ssheet == null || pathDest == null || fileName.trim().length() == 0) {
throw new IllegalArgumentException();
720,7 → 723,7
pathDest.mkdirs();
}
 
fDest = SheetUtils.getInstance().convertToOldFile(fileName, pathDest, fDest);
fDest = SheetUtils.convertToOldFile(fileName, pathDest, fDest);
 
// Sauvegarde
try {
743,7 → 746,7
// Copie de l'odsp
try {
File odspOut = new File(pathDest, fileName + ".odsp");
InputStream odspIn = getTemplate(modele + ".odsp", rowLanguage);
final InputStream odspIn = TemplateManager.getInstance().getTemplatePrintConfiguration(templateId, langage, null);
if (odspIn != null) {
StreamUtils.copy(odspIn, odspOut);
}
753,18 → 756,6
return fDest;
}
 
public static InputStream getOOTemplate(String name, SQLRow language) throws FileNotFoundException {
return OOgenerationListeXML.getOOTemplate(name, language);
}
 
public static InputStream getXmlTemplate(String name, SQLRow language) throws FileNotFoundException {
return OOgenerationListeXML.getXmlTemplate(name, language);
}
 
public static InputStream getTemplate(String name, SQLRow language) throws FileNotFoundException {
return OOgenerationListeXML.getTemplate(name, language);
}
 
/**
* parcourt l'ensemble de la feuille pour trouver les style définit
*/
827,15 → 818,16
return mapStyleDef;
}
 
public static boolean needAnnexe(String modele, SQLRow row, SQLRow rowLanguage) {
 
SAXBuilder builder = new SAXBuilder();
public static boolean needAnnexe(String templateId, SQLRow row, SQLRow rowLanguage) {
final String langage = rowLanguage != null ? rowLanguage.getString("CHEMIN") : null;
final SAXBuilder builder = new SAXBuilder();
try {
final InputStream xmlConfiguration = TemplateManager.getInstance().getTemplateConfiguration(templateId, langage, null);
final Document doc = builder.build(xmlConfiguration);
final InputStream template = TemplateManager.getInstance().getTemplate(templateId, langage, null);
 
Document doc = builder.build(getXmlTemplate(modele, rowLanguage));
final SpreadSheet spreadSheet = new ODPackage(template).getSpreadSheet();
 
SpreadSheet spreadSheet = SpreadSheet.create(new ODPackage(getOOTemplate(modele, rowLanguage)));
 
// On initialise un nouvel élément racine avec l'élément racine du document.
Element racine = doc.getRootElement();
 
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/OOgenerationListeXML.java
49,34 → 49,32
// Cache pour la recherche des styles
private static Map<Sheet, Map<String, Map<Integer, String>>> cacheStyle = new HashMap<Sheet, Map<String, Map<Integer, String>>>();
 
public static File genere(String modele, String pathDest, String fileDest, Map<Integer, List<Map<String, Object>>> liste, Map<Integer, Map<String, Object>> values) {
public static File genere(String modele, File pathDest, String fileDest, Map<Integer, List<Map<String, Object>>> liste, Map<Integer, Map<String, Object>> values) {
return genere(modele, pathDest, fileDest, liste, values, new HashMap<Integer, Map<Integer, String>>(), null, null);
}
 
public static File genere(String modele, String pathDest, String fileDest, Map<Integer, List<Map<String, Object>>> liste, Map<Integer, Map<String, Object>> values,
public static File genere(String templateId, File pathDest, String fileDest, Map<Integer, List<Map<String, Object>>> liste, Map<Integer, Map<String, Object>> values,
Map<Integer, Map<Integer, String>> mapStyle, List<String> sheetName, SQLRow rowLanguage) {
// SQLRow row = elt.getTable().getRow(id);
cacheStyle.clear();
SAXBuilder builder = new SAXBuilder();
final SAXBuilder builder = new SAXBuilder();
try {
Document doc = builder.build(getXmlTemplate(modele, rowLanguage));
InputStream xmlConfiguration = TemplateManager.getInstance().getTemplateConfiguration(templateId, rowLanguage != null ? rowLanguage.getString("CHEMIN") : null, null);
Document doc = builder.build(xmlConfiguration);
 
// On initialise un nouvel élément racine avec l'élément racine du
// document.
Element racine = doc.getRootElement();
final Element racine = doc.getRootElement();
 
// Création et génération du fichier OO
SpreadSheet spreadSheet = SpreadSheet.create(new ODPackage(getOOTemplate(modele, rowLanguage)));
final InputStream template = TemplateManager.getInstance().getTemplate(templateId, rowLanguage != null ? rowLanguage.getString("CHEMIN") : null, null);
 
final SpreadSheet spreadSheet = new ODPackage(template).getSpreadSheet();
Sheet sheet0 = spreadSheet.getSheet(0);
if (sheetName != null && sheetName.size() > 0) {
for (int i = 1; i < sheetName.size(); i++) {
sheet0.copy(i, (sheetName != null) ? sheetName.get(i) : "Feuille " + i);
}
 
spreadSheet.getSheet(0).setName(sheetName.get(0));
 
System.err.println("add " + sheetName.size() + " sheet");
 
}
 
for (Integer i : liste.keySet()) {
86,7 → 84,6
children = racine.getChildren("element");
}
parseElementsXML(children, sheet, values.get(i));
 
Element child = racine.getChild("table" + i);
if (child == null) {
child = racine.getChild("table");
94,15 → 91,11
parseListeXML(child, liste.get(i), sheet, mapStyle.get(i));
}
// Sauvegarde du fichier
return saveSpreadSheet(spreadSheet, new File(pathDest), fileDest, modele, rowLanguage);
return saveSpreadSheet(spreadSheet, pathDest, fileDest, templateId, rowLanguage);
 
} catch (JDOMException e) {
 
e.printStackTrace();
ExceptionHandler.handle("Erreur lors de la génération du fichier " + fileDest, e);
} catch (IOException e) {
 
e.printStackTrace();
ExceptionHandler.handle("Erreur lors de la création du fichier " + fileDest, e);
}
return null;
478,7 → 471,7
* @return un File pointant sur le fichier créé
* @throws IOException
*/
private static File saveSpreadSheet(SpreadSheet ssheet, File pathDest, String fileName, String modele, SQLRow rowLanguage) throws IOException {
private static File saveSpreadSheet(SpreadSheet ssheet, File pathDest, String fileName, String templateId, SQLRow rowLanguage) throws IOException {
 
// Test des arguments
if (ssheet == null || pathDest == null || fileName.trim().length() == 0) {
492,7 → 485,7
pathDest.mkdirs();
}
 
SheetUtils.getInstance().convertToOldFile(fileName, pathDest, fDest);
SheetUtils.convertToOldFile(fileName, pathDest, fDest);
 
// Sauvegarde
try {
515,7 → 508,7
// Copie de l'odsp
try {
File odspOut = new File(pathDest, fileName + ".odsp");
InputStream odspIn = getTemplate(modele + ".odsp", rowLanguage);
InputStream odspIn = TemplateManager.getInstance().getTemplatePrintConfiguration(templateId, rowLanguage != null ? rowLanguage.getString("CHEMIN") : null, null);
if (odspIn != null) {
StreamUtils.copy(odspIn, odspOut);
}
526,34 → 519,7
return fDest;
}
 
public static InputStream getOOTemplate(String name, SQLRow rowLanguage) throws FileNotFoundException {
return getTemplate(name + ".ods", rowLanguage);
}
 
public static InputStream getXmlTemplate(String name, SQLRow rowLanguage) throws FileNotFoundException {
return getTemplate(name + ".xml", rowLanguage);
}
 
/**
* Permet d'obtenir l'emplacement du modele passé en argument. Si le modéle ne se pas trouve
* dans le répertoire spécifié dans les préférences alors on recherche dans le répertoire par
* défaut des modéles.
*
* @param name nom du modéle à trouver
* @return le modéle
* @throws FileNotFoundException si le fichier est introuvable
*/
public static InputStream getTemplate(String name, SQLRow rowLanguage) throws FileNotFoundException {
String modelDir = TemplateNXProps.getInstance().getStringProperty("LocationTemplate");
 
if (rowLanguage != null) {
return ComptaBasePropsConfiguration.getStream(name, modelDir + File.separator + rowLanguage.getString("CHEMIN"), modelDir, SpreadSheetGenerator.defaultLocationTemplate);
} else {
return ComptaBasePropsConfiguration.getStream(name, modelDir, SpreadSheetGenerator.defaultLocationTemplate);
}
}
 
/**
* parcourt l'ensemble de la feuille pour trouver les style définit
*/
private static Map<String, Map<Integer, String>> searchStyle(Sheet sheet, int colEnd, int rowEnd) {
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/AbstractSheetXml.java
14,8 → 14,10
package org.openconcerto.erp.generationDoc;
 
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.utils.StringUtils;
 
import java.io.File;
import java.io.InputStream;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
 
23,35 → 25,36
import javax.swing.SwingUtilities;
 
public abstract class AbstractSheetXml extends SheetXml {
private File generatedOpenDocumentFile;
 
public AbstractSheetXml(SQLRow row) {
this.row = row;
}
 
public final Future<File> genere(final boolean visu, final boolean impression) {
Callable<File> c = new Callable<File>() {
@Override
public File call() throws Exception {
public final Future<SheetXml> createDocumentAsynchronous() {
Callable<SheetXml> c = new Callable<SheetXml>() {
@Override
public SheetXml call() throws Exception {
try {
String modele = getModele();
final String modeleFinal = modele;
try {
OOgenerationXML.getOOTemplate(modele, getRowLanguage());
} catch (Exception e) {
String templateId = getTemplateId();
final String modeleFinal = templateId;
 
String langage = getRowLanguage() != null ? getRowLanguage().getString("CHEMIN") : null;
InputStream templateStream = TemplateManager.getInstance().getTemplate(templateId, langage, getType());
if (templateStream == null) {
SwingUtilities.invokeLater(new Runnable() {
 
@Override
public void run() {
// TODO Raccord de méthode auto-généré
JOptionPane.showMessageDialog(null, "Impossible de trouver le modele " + modeleFinal + ". \n Le modéle par défaut sera utilisé!");
}
});
modele = getDefaultModele();
templateId = getDefaultTemplateId();
}
File fGen = OOgenerationXML.genere(modele, AbstractSheetXml.this.locationOO, getFileName(), AbstractSheetXml.this.row, getRowLanguage());
AbstractSheetXml.this.f = fGen;
useOO(fGen, visu, impression, getFileName());
return fGen;
AbstractSheetXml.this.generatedOpenDocumentFile = OOgenerationXML.createDocument(templateId, getDocumentOutputDirectory(), getValidFileName(getName()), AbstractSheetXml.this.row,
getRowLanguage());
 
} catch (Exception e) {
DEFAULT_HANDLER.uncaughtException(null, e);
// rethrow exception so that the unsuspecting caller can use this as the
59,11 → 62,27
throw e;
} catch (Throwable e) {
DEFAULT_HANDLER.uncaughtException(null, e);
return null;
}
 
}
return AbstractSheetXml.this;
}
};
return runnableQueue.submit(c);
}
 
public String getType() {
return null;
}
 
@Override
public String getStoragePathP() {
return StringUtils.firstUp(elt.getPluralName());
}
 
@Override
public File getGeneratedFile() {
if (this.generatedOpenDocumentFile == null)
this.generatedOpenDocumentFile = new File(getDocumentOutputDirectory(), getValidFileName(getName()) + ".ods");
return generatedOpenDocumentFile;
}
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/AbstractListeSheetXml.java
13,6 → 13,9
package org.openconcerto.erp.generationDoc;
 
import static org.openconcerto.erp.generationDoc.SheetXml.getValidFileName;
import org.openconcerto.utils.StringUtils;
 
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
35,16 → 38,21
// Nom des feuilles
protected List<String> sheetNames = new ArrayList<String>();
 
public final Future<File> genere(final boolean visu, final boolean impression) {
Callable<File> c = new Callable<File>() {
private File generatedOpenDocumentFile;
 
public AbstractListeSheetXml() {
generatedOpenDocumentFile = new File(getDocumentOutputDirectory(), getValidFileName(getName()) + ".ods");
}
 
public final Future<SheetXml> createDocumentAsynchronous() {
Callable<SheetXml> c = new Callable<SheetXml>() {
@Override
public File call() throws Exception {
public SheetXml call() throws Exception {
try {
createListeValues();
File fGen = OOgenerationListeXML.genere(getModele(), locationOO, getFileName(), listAllSheetValues, mapAllSheetValues, styleAllSheetValues, sheetNames, null);
AbstractListeSheetXml.this.f = fGen;
useOO(fGen, visu, impression, getFileName());
return fGen;
generatedOpenDocumentFile = OOgenerationListeXML.genere(getTemplateId(), getDocumentOutputDirectory(), getValidFileName(getName()), listAllSheetValues, mapAllSheetValues,
styleAllSheetValues, sheetNames, null);
return AbstractListeSheetXml.this;
} catch (Exception e) {
DEFAULT_HANDLER.uncaughtException(null, e);
// rethrow exception so that the unsuspecting caller can use this as the
57,7 → 65,7
 
}
};
return this.runnableQueue.submit(c);
return runnableQueue.submit(c);
}
 
/**
64,4 → 72,24
* To fill listAllSheetValues, styleAllSheetValues, mapAllSheetValues, sheetNames
*/
protected abstract void createListeValues();
 
@Override
public String getStoragePathP() {
return StringUtils.firstUp(elt.getPluralName());
}
 
@Override
public File getGeneratedFile() {
return generatedOpenDocumentFile;
}
 
@Override
public File getDocumentOutputDirectoryP() {
return DocumentLocalStorageManager.getInstance().getDocumentOutputDirectory(this.getTemplateId());
}
 
@Override
public File getPDFOutputDirectoryP() {
return DocumentLocalStorageManager.getInstance().getPDFOutputDirectory(this.getTemplateId());
}
}
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/SpreadSheetGenerator.java
193,7 → 193,7
return null;
}
}
final SpreadSheet res = SpreadSheet.create(new ODPackage(f));
final SpreadSheet res = new ODPackage(f).getSpreadSheet();
f.close();
return res;
}
250,9 → 250,9
 
this.modele = sheet.modele;
this.mCell = sheet.mCell;
this.destDirOO = new File(sheet.locationOO);
this.destDirOO = sheet.getDocumentOutputDirectory();
this.destDirOO.mkdirs();
this.destDirPDF = new File(sheet.locationPDF);
this.destDirPDF = sheet.getPDFOutputDirectory();
this.destDirPDF.mkdirs();
this.nbPage = sheet.nbPage;
this.nbRowsPerPage = sheet.nbRowsPerPage;
272,7 → 272,7
try {
 
f = generateWithStyle();
 
final File pdfFileToCreate = new File(this.destDirPDF.getAbsolutePath(), this.destFileName + ".pdf");
try {
 
if (!Boolean.getBoolean("org.openconcerto.oo.useODSViewer")) {
279,7 → 279,8
 
final Component doc = ComptaPropsConfiguration.getOOConnexion().loadDocument(f, !this.visu);
if (this.exportPDF) {
doc.saveToPDF(new File(this.destDirPDF.getAbsolutePath(), this.destFileName + ".pdf"));
 
doc.saveToPDF(pdfFileToCreate);
}
 
if (this.impression) {
295,7 → 296,7
PreviewFrame.show(f);
}
 
SheetUtils.getInstance().convert2PDF(doc, f, this.destFileName);
SheetUtils.convert2PDF(doc, pdfFileToCreate);
if (this.impression) {
// Print !
DefaultNXDocumentPrinter printer = new DefaultNXDocumentPrinter();
/trunk/OpenConcerto/src/org/openconcerto/erp/core/customerrelationship/customer/report/FicheClientXmlSheet.java
13,38 → 13,31
package org.openconcerto.erp.core.customerrelationship.customer.report;
 
import org.openconcerto.erp.generationDoc.AbstractSheetXml;
import org.openconcerto.erp.generationDoc.SheetXml;
import org.openconcerto.erp.generationDoc.AbstractSheetXMLWithDate;
import org.openconcerto.erp.preferences.PrinterNXProps;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.utils.Tuple2;
 
import java.io.File;
import java.util.Calendar;
import java.util.Date;
 
public class FicheClientXmlSheet extends AbstractSheetXml {
public class FicheClientXmlSheet extends AbstractSheetXMLWithDate {
 
public static Tuple2<String, String> getTuple2Location() {
return tupleDefault;
}
public static final String TEMPLATE_ID = "FicheClient";
public static final String TEMPLATE_PROPERTY_NAME = DEFAULT_PROPERTY_NAME;
 
public FicheClientXmlSheet(SQLRow row) {
super(row);
this.printer = PrinterNXProps.getInstance().getStringProperty("DevisPrinter");
this.elt = Configuration.getInstance().getDirectory().getElement("CLIENT");
Calendar cal = Calendar.getInstance();
this.locationOO = SheetXml.getLocationForTuple(tupleDefault, false) + File.separator + cal.get(Calendar.YEAR);
this.locationPDF = SheetXml.getLocationForTuple(tupleDefault, true) + File.separator + cal.get(Calendar.YEAR);
}
 
@Override
public String getDefaultModele() {
return "FicheClient";
public String getDefaultTemplateId() {
return TEMPLATE_ID;
}
 
public String getFileName() {
return getValidFileName("FicheClient_" + new Date().getTime());
@Override
public String getName() {
return "FicheClient_" + new Date().getTime();
}
}
/trunk/OpenConcerto/src/org/openconcerto/erp/core/customerrelationship/customer/element/ClientNormalSQLElement.java
44,6 → 44,7
import org.openconcerto.sql.sqlobject.JUniqueTextField;
import org.openconcerto.sql.sqlobject.SQLTextCombo;
import org.openconcerto.ui.DefaultGridBagConstraints;
import org.openconcerto.ui.FormLayouter;
import org.openconcerto.ui.TitledSeparator;
import org.openconcerto.ui.component.ITextArea;
 
86,8 → 87,11
protected List<String> getListFields() {
final List<String> l = new ArrayList<String>();
l.add("CODE");
l.add("FORME_JURIDIQUE");
// l.add("FORME_JURIDIQUE");
l.add("NOM");
if (getTable().getFieldsName().contains("LOCALISATION")) {
l.add("LOCALISATION");
}
l.add("RESPONSABLE");
l.add("ID_ADRESSE");
l.add("TEL");
115,9 → 119,13
 
protected List<String> getComboFields() {
final List<String> l = new ArrayList<String>();
l.add("FORME_JURIDIQUE");
// l.add("FORME_JURIDIQUE");
l.add("NOM");
if (getTable().getFieldsName().contains("LOCALISATION")) {
l.add("LOCALISATION");
} else {
l.add("CODE");
}
return l;
}
 
234,7 → 242,20
this.add(boxPays, c);
this.addView(boxPays, "ID_PAYS");
}
 
if (getTable().getFieldsName().contains("LOCALISATION")) {
c.gridy++;
c.gridx = 0;
c.weightx = 0;
JLabel comp2 = new JLabel(getLabelFor("LOCALISATION"));
comp2.setHorizontalAlignment(SwingConstants.RIGHT);
this.add(comp2, c);
JTextField loc = new JTextField();
c.gridx++;
c.weightx = 1;
// DefaultGridBagConstraints.lockMinimumSize(boxPays);
this.add(loc, c);
this.addView(loc, "LOCALISATION");
}
// Numero intracomm
JLabel labelIntraComm = new JLabel("N° TVA");
labelIntraComm.setHorizontalAlignment(SwingConstants.RIGHT);
380,6 → 401,17
// Secteur activité
final boolean customerIsKD;
 
// Champ Module
c.gridx = 0;
c.gridy++;
c.gridwidth = GridBagConstraints.REMAINDER;
final JPanel addP = new JPanel();
this.setAdditionalFieldsPanel(new FormLayouter(addP, 1));
this.add(addP, c);
 
c.gridy++;
c.gridwidth = 1;
 
// Adresse
c.gridx = 0;
c.gridy++;
/trunk/OpenConcerto/src/org/openconcerto/erp/core/finance/payment/action/ListeDesRelancesAction.java
26,6 → 26,7
import org.openconcerto.sql.view.list.SQLTableModelColumn;
import org.openconcerto.sql.view.list.SQLTableModelColumnPath;
import org.openconcerto.sql.view.list.SQLTableModelSourceOnline;
import org.openconcerto.utils.ExceptionHandler;
 
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
103,14 → 104,20
// Impression
AbstractAction actionPrintFact = new AbstractAction("Imprimer la facture") {
public void actionPerformed(ActionEvent e) {
final Thread t = new Thread(new Runnable() {
 
VenteFactureXmlSheet sheet = new VenteFactureXmlSheet(rowRelance.getForeignRow("ID_SAISIE_VENTE_FACTURE"));
if (sheet.getFileODS().exists()) {
sheet.fastPrintDocument();
} else {
sheet.genere(false, true);
@Override
public void run() {
try {
printInvoice(rowRelance);
} catch (Exception e) {
ExceptionHandler.handle("Impression impossible", e);
}
}
});
t.start();
 
}
};
menu.add(actionPrintFact);
 
118,14 → 125,19
 
AbstractAction actionPrintBoth = new AbstractAction("Imprimer la facture et la relance") {
public void actionPerformed(ActionEvent e) {
final Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
s.fastPrintDocument();
VenteFactureXmlSheet sheet = new VenteFactureXmlSheet(rowRelance.getForeignRow("ID_SAISIE_VENTE_FACTURE"));
if (sheet.getFileODS().exists()) {
sheet.fastPrintDocument();
} else {
sheet.genere(false, true);
printInvoice(rowRelance);
} catch (Exception e) {
ExceptionHandler.handle("Impression impossible", e);
}
}
});
t.start();
}
};
menu.add(actionPrintBoth);
 
138,13 → 150,17
}
});
 
// Générer
// Créer la fiche de relance
menu.add(new AbstractAction("Créer la fiche de relance") {
public void actionPerformed(ActionEvent e) {
 
try {
FicheRelanceSheet sheet = new FicheRelanceSheet(rowRelance);
sheet.genere(true, false);
sheet.createDocumentAsynchronous();
sheet.showPrintAndExportAsynchronous(true, false, true);
} catch (Exception ex) {
ExceptionHandler.handle("Impression impossible", ex);
}
}
});
 
menu.show(e.getComponent(), e.getPoint().x, e.getPoint().y);
159,4 → 175,11
 
public void mouseExited(MouseEvent e) {
}
 
private void printInvoice(final SQLRow rowRelance) throws Exception {
final VenteFactureXmlSheet sheet = new VenteFactureXmlSheet(rowRelance.getForeignRow("ID_SAISIE_VENTE_FACTURE"));
sheet.getOrCreateDocumentFile();
sheet.fastPrintDocument();
sheet.showPrintAndExport(false, false, true);
}
}
/trunk/OpenConcerto/src/org/openconcerto/erp/core/finance/accounting/report/JournauxSheet.java
57,6 → 57,9
protected int lettrage;
private String compteDeb, compteEnd;
 
public static String TEMPLATE_ID = "Journaux";
public static String TEMPLATE_PROPERTY_NAME = "LocationJournaux";
 
public static void setSize(int debut, int fin) {
debutFill = debut;
endFill = fin;
66,10 → 69,14
setSize(7, 68);
}
 
private static final Tuple2<String, String> tuple = Tuple2.create("LocationJournaux", "Journaux");
@Override
public String getTemplateId() {
return TEMPLATE_ID;
}
 
public static Tuple2<String, String> getTuple2Location() {
return tuple;
@Override
protected String getYear() {
return "";
}
 
public JournauxSheet(int[] id, Date du, Date au, int lettrage, String compteDeb, String compteEnd) {
78,8 → 85,6
cal.setTime(au);
this.printer = PrinterNXProps.getInstance().getStringProperty("JournauxPrinter");
this.modele = "Journaux.ods";
this.locationOO = SheetXml.getLocationForTuple(tuple, false) + File.separator + cal.get(Calendar.YEAR);
this.locationPDF = SheetXml.getLocationForTuple(tuple, true) + File.separator + cal.get(Calendar.YEAR);
this.dateAu = au;
this.dateDu = du;
this.idS = id;
/trunk/OpenConcerto/src/org/openconcerto/erp/core/finance/accounting/report/GrandLivreSheet.java
16,7 → 16,6
import org.openconcerto.erp.config.ComptaPropsConfiguration;
import org.openconcerto.erp.core.finance.accounting.element.ComptePCESQLElement;
import org.openconcerto.erp.generationDoc.SheetInterface;
import org.openconcerto.erp.generationDoc.SheetXml;
import org.openconcerto.erp.preferences.PrinterNXProps;
import org.openconcerto.erp.rights.ComptaUserRight;
import org.openconcerto.sql.Configuration;
28,10 → 27,8
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.users.UserManager;
import org.openconcerto.utils.GestionDevise;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.ITransformer;
 
import java.io.File;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
66,6 → 63,9
private boolean centralFourn = false;
int idJrnlExclude = -1;
 
public static String TEMPLATE_ID = "Grand Livre";
public static String TEMPLATE_PROPERTY_NAME = "LocationGrandLivre";
 
public static void setSize(int debut, int fin) {
debutFill = debut;
endFill = fin;
75,10 → 75,9
setSize(7, 69);
}
 
private static final Tuple2<String, String> tuple = Tuple2.create("LocationGrandLivre", "Grand Livre");
 
public static Tuple2<String, String> getTuple2Location() {
return tuple;
@Override
protected String getYear() {
return "";
}
 
public GrandLivreSheet(Date du, Date au, String compteDep, String compteEnd, int lettrage, boolean cumul, boolean excludeCptSolde, boolean centralClient, boolean centralFourn, int idJrnlExclude) {
89,8 → 88,6
this.idJrnlExclude = idJrnlExclude;
this.printer = PrinterNXProps.getInstance().getStringProperty("GrandLivrePrinter");
this.modele = "GrandLivre.ods";
this.locationOO = SheetXml.getLocationForTuple(tuple, false) + File.separator + cal.get(Calendar.YEAR);
this.locationPDF = SheetXml.getLocationForTuple(tuple, true) + File.separator + cal.get(Calendar.YEAR);
this.dateAu = au;
this.dateDu = du;
this.compteDeb = compteDep.trim();
621,4 → 618,10
 
return map;
}
 
@Override
public String getTemplateId() {
// TODO Auto-generated method stub
return TEMPLATE_ID;
}
}
/trunk/OpenConcerto/src/org/openconcerto/erp/core/finance/accounting/report/BalanceSheet.java
14,8 → 14,8
package org.openconcerto.erp.core.finance.accounting.report;
 
import org.openconcerto.erp.config.ComptaPropsConfiguration;
import org.openconcerto.erp.generationDoc.DocumentLocalStorageManager;
import org.openconcerto.erp.generationDoc.SheetInterface;
import org.openconcerto.erp.generationDoc.SheetXml;
import org.openconcerto.erp.preferences.PrinterNXProps;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.model.SQLRow;
23,9 → 23,7
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.utils.GestionDevise;
import org.openconcerto.utils.Tuple2;
 
import java.io.File;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
42,7 → 40,8
private boolean centralClient, centralFourn;
private final static DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM);
private final static DateFormat dateFormatEcr = DateFormat.getDateInstance(DateFormat.SHORT);
 
public static String TEMPLATE_ID = "Balance";
public static String TEMPLATE_PROPERTY_NAME = "LocationBalance";
private Date dateDu, dateAu;
private String compteDeb, compteEnd;
 
56,10 → 55,14
 
}
 
private static final Tuple2<String, String> tuple = Tuple2.create("LocationBalance", "Balance");
@Override
protected String getYear() {
return "";
}
 
public static Tuple2<String, String> getTuple2Location() {
return tuple;
@Override
public String getTemplateId() {
return TEMPLATE_ID;
}
 
public BalanceSheet(Date du, Date au, String compteDeb, String compteEnd, boolean centralClient, boolean centralFourn) {
72,10 → 75,11
this.nbRowsPerPage = 72;
this.printer = PrinterNXProps.getInstance().getStringProperty("BalancePrinter");
this.modele = "Balance.ods";
final String locationForTuple = SheetXml.getLocationForTuple(tuple, false);
System.err.println("Emplacement balance :::: " + locationForTuple);
this.locationOO = locationForTuple + File.separator + cal.get(Calendar.YEAR);
this.locationPDF = SheetXml.getLocationForTuple(tuple, true) + File.separator + cal.get(Calendar.YEAR);
 
final DocumentLocalStorageManager storage = DocumentLocalStorageManager.getInstance();
 
// this.locationOO = storage.getDocumentOutputDirectory(TEMPLATE_ID);
// this.locationPDF = storage.getPDFOutputDirectory(TEMPLATE_ID);
this.dateAu = au;
this.dateDu = du;
this.compteDeb = compteDeb;
/trunk/OpenConcerto/src/org/openconcerto/erp/core/finance/accounting/report/BalanceAgeeListeSheetXML.java
15,7 → 15,6
 
import org.openconcerto.erp.core.finance.payment.element.ModeDeReglementSQLElement;
import org.openconcerto.erp.generationDoc.AbstractListeSheetXml;
import org.openconcerto.erp.generationDoc.SheetXml;
import org.openconcerto.erp.preferences.PrinterNXProps;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.element.SQLElement;
23,7 → 22,6
import org.openconcerto.sql.model.SQLRowListRSH;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.Where;
import org.openconcerto.utils.Tuple2;
 
import java.util.ArrayList;
import java.util.Calendar;
34,33 → 32,26
 
public class BalanceAgeeListeSheetXML extends AbstractListeSheetXml {
 
// private final DateFormat dateFormat = new SimpleDateFormat("dd/MM/yy");
private Date deb, fin;
public static String TEMPLATE_ID = "Balance agée";
 
public static Tuple2<String, String> getTuple2Location() {
return tupleDefault;
}
 
public BalanceAgeeListeSheetXML(Date deb, Date fin) {
this.printer = PrinterNXProps.getInstance().getStringProperty("BonPrinter");
this.deb = deb;
this.fin = fin;
 
this.locationOO = SheetXml.getLocationForTuple(tupleDefault, false);
this.locationPDF = SheetXml.getLocationForTuple(tupleDefault, true);
}
 
@Override
public String getDefaultModele() {
public String getDefaultTemplateId() {
return "BalanceAgee";
}
 
public String getFileName() {
return getValidFileName("BalanceAgee" + new Date().getTime());
@Override
public String getName() {
return "BalanceAgee" + new Date().getTime();
}
 
protected void createListeValues() {
 
SQLElement ecr = Configuration.getInstance().getDirectory().getElement("ECRITURE");
SQLElement cpt = Configuration.getInstance().getDirectory().getElement("COMPTE_PCE");
SQLElement fact = Configuration.getInstance().getDirectory().getElement("SAISIE_VENTE_FACTURE");
106,7 → 97,6
long time = c.getTimeInMillis() - date;
long day = time / 86400000;
if (day < 0) {
// System.err.println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
continue;
}
final SQLRow rowCpt = sqlRow.getForeignRow("ID_COMPTE_PCE");
185,7 → 175,7
valuesTab.add(e);
}
}
Map<String, Object> totalMap = new HashMap<String, Object>();
final Map<String, Object> totalMap = new HashMap<String, Object>();
totalMap.put("NOM", "TOTAL");
totalMap.put("30", total30 / 100.0);
totalMap.put("60", total60 / 100.0);
194,29 → 184,7
totalMap.put("TOTAL", totalFull / 100.0);
valuesTab.add(totalMap);
 
// Map<String, Object> values = this.mapAllSheetValues.get(0);
// if (values == null) {
// values = new HashMap<String, Object>();
// }
// valuesHA.put("TOTAL", totalHA);
// valuesE.put("TOTAL_HA", totalHA);
// valuesE.put("TOTAL", totalE);
// valuesE.put("TOTAL_VT", totalTPVTTC);
// values.put("TOTAL", totalVC);
// values.put("TOTAL_MARGE", totalTPVTTC - totalTPA);
//
// valuesE.put("TOTAL_GLOBAL", totalTPVTTC + totalHA);
// values.put("TOTAL_PA", totalTPA);
// values.put("TOTAL_PV_TTC", totalTPVTTC);
//
// String periode = "Période Du " + dateFormat.format(this.du) + " au " +
// dateFormat.format(this.au);
// values.put("DATE", periode);
// valuesHA.put("DATE", periode);
// valuesE.put("DATE", periode);
 
this.listAllSheetValues.put(0, valuesTab);
// this.mapAllSheetValues.put(0, values);
 
}
}
/trunk/OpenConcerto/src/org/openconcerto/erp/core/finance/accounting/ui/BalanceAgeePanel.java
66,19 → 66,10
BalanceAgeeListeSheetXML l = new BalanceAgeeListeSheetXML(dateDeb.getDate(), dateFin.getDate());
 
try {
l.genere(false, false).get();
// FIXME Probleme avec l'odsviewer
if (!Boolean.getBoolean("org.openconcerto.oo.useODSViewer")) {
l.showDocument();
} else {
l.showPreviewDocument();
}
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
l.createDocument();
l.openDocument(false);
} catch (Exception e1) {
e1.printStackTrace();
} catch (ExecutionException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
});
/trunk/OpenConcerto/src/org/openconcerto/erp/core/finance/accounting/ui/GenerationPointagePanel.java
13,7 → 13,6
package org.openconcerto.erp.core.finance.accounting.ui;
 
 
import org.openconcerto.erp.generationDoc.gestcomm.PointageXmlSheet;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.element.SQLElement;
69,7 → 68,6
 
this.add(this.spinYear, c);
 
JPanel panelButton = new JPanel();
panelButton.add(this.gen);
panelButton.add(this.close);
100,7 → 98,8
int mois = this.combo.getValue() - 2;
int year = Integer.valueOf(this.spinYear.getValue().toString());
PointageXmlSheet sheet = new PointageXmlSheet(mois, year);
sheet.genere(true, false);
sheet.createDocumentAsynchronous();
sheet.showPrintAndExportAsynchronous(true, false, true);
} else {
if (e.getSource() == this.close) {
((JFrame) SwingUtilities.getRoot(this)).dispose();
/trunk/OpenConcerto/src/org/openconcerto/erp/core/finance/accounting/element/ExerciceCommonSQLElement.java
123,6 → 123,9
 
@Override
public synchronized ValidState getValidState() {
if (this.dateDeb.getValue() == null || this.dateFin.getValue() == null) {
return new ValidState(false, "Date de début ou de fin d'exercice non définie");
}
return super.getValidState().and(ValidState.createCached(this.dateDeb.getValue().before(this.dateFin.getValue()), "Date de début après date de fin"));
}
};
/trunk/OpenConcerto/src/org/openconcerto/erp/core/humanresources/payroll/report/FichePayeSheet.java
14,8 → 14,8
package org.openconcerto.erp.core.humanresources.payroll.report;
 
import org.openconcerto.erp.config.ComptaPropsConfiguration;
import org.openconcerto.erp.generationDoc.DocumentLocalStorageManager;
import org.openconcerto.erp.generationDoc.SheetInterface;
import org.openconcerto.erp.generationDoc.SheetXml;
import org.openconcerto.erp.generationDoc.SpreadSheetGeneratorGestComm;
import org.openconcerto.erp.preferences.PrinterNXProps;
import org.openconcerto.map.model.Ville;
26,7 → 26,6
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.Tuple2;
 
import java.io.File;
import java.text.DateFormat;
163,26 → 162,38
 
// Emplacement des fichiers générés
public static String getLocation(int id, int type) {
 
return getLocation(tableFiche.getRow(id), type);
}
 
private static final Tuple2<String, String> tuple = Tuple2.create("LocationFichePaye", "Fiche de paye");
public static final String TEMPLATE_ID = "Fiche de paye";
public static final String TEMPLATE_PROPERTY_NAME = "LocationFichePaye";
 
public static Tuple2<String, String> getTuple2Location() {
return tuple;
@Override
public String getTemplateId() {
return TEMPLATE_ID;
}
 
@Override
protected String getYear() {
// TODO Auto-generated method stub
return this.row.getString("ANNEE");
}
 
public static String getLocation(SQLRow r, int type) {
DocumentLocalStorageManager storage = DocumentLocalStorageManager.getInstance();
String path;
if (type == FichePayeSheet.typeOO) {
path = storage.getDocumentOutputDirectory(TEMPLATE_ID).getAbsolutePath();
} else {
path = storage.getPDFOutputDirectory(TEMPLATE_ID).getAbsolutePath();
}
 
return SheetXml.getLocationForTuple(tuple, !(type == FichePayeSheet.typeOO)) + File.separator + r.getString("ANNEE");
return path + File.separator + r.getString("ANNEE");
}
 
private void init() {
this.modele = "FichePaye.ods";
this.printer = PrinterNXProps.getInstance().getStringProperty("FichePayePrinter");
this.locationOO = getLocation(this.row, typeOO);
this.locationPDF = getLocation(this.row, typePDF);
}
 
protected void createMap() {
/trunk/OpenConcerto/src/org/openconcerto/erp/core/humanresources/payroll/report/LivrePayeSheet.java
60,18 → 60,18
setSize(8, 65, 3);
}
 
private static final Tuple2<String, String> tuple = Tuple2.create("LocationLivrePaye", "Livre de paye");
 
public static Tuple2<String, String> getTuple2Location() {
return tuple;
@Override
protected String getYear() {
return "";
}
 
public static final String TEMPLATE_ID = "Livre de paye";
public static final String TEMPLATE_PROPERTY_NAME = "LocationLivrePaye";
 
public LivrePayeSheet(int moisDu, int moisAu, String annee) {
super();
this.printer = PrinterNXProps.getInstance().getStringProperty("LivrePayePrinter");
this.modele = "LivrePaye.ods";
this.locationOO = SheetXml.getLocationForTuple(tuple, false) + File.separator + annee;
this.locationPDF = SheetXml.getLocationForTuple(tuple, true) + File.separator + annee;
this.moisAu = moisAu;
this.moisDu = moisDu;
this.annee = annee;
96,6 → 96,11
this.mCell.put("A" + row, "Période de " + rowMoisDu.getString("NOM") + " à " + rowMoisAu.getString("NOM") + " " + this.annee);
}
 
@Override
public String getTemplateId() {
return TEMPLATE_ID;
}
 
protected void createMap() {
 
this.mapReplace = new HashMap();
/trunk/OpenConcerto/src/org/openconcerto/erp/core/humanresources/payroll/report/EtatChargesPayeSheet.java
57,19 → 57,24
setSize(7, 66);
}
 
private static final Tuple2<String, String> tuple = Tuple2.create("LocationEtatChargesPaye", "Etat des charges");
public static String TEMPLATE_ID = "Etat des charges";
public static String TEMPLATE_PROPERTY_NAME = "LocationEtatChargesPaye";
 
public static Tuple2<String, String> getTuple2Location() {
return tuple;
@Override
public String getTemplateId() {
return TEMPLATE_ID;
}
 
@Override
protected String getYear() {
return "";
}
 
public EtatChargesPayeSheet(int moisDu, int moisAu, String annee) {
super();
 
this.printer = PrinterNXProps.getInstance().getStringProperty("EtatChargesPayePrinter");
this.modele = "EtatChargesPaye.ods";
this.locationOO = SheetXml.getLocationForTuple(tuple, false) + File.separator + annee;
this.locationPDF = SheetXml.getLocationForTuple(tuple, true) + File.separator + annee;
this.moisAu = moisAu;
this.moisDu = moisDu;
this.annee = annee;
/trunk/OpenConcerto/src/org/openconcerto/erp/core/humanresources/employe/report/N4DS.java
New file
0,0 → 1,458
/*
* 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.erp.core.humanresources.employe.report;
 
import org.openconcerto.erp.config.ComptaPropsConfiguration;
import org.openconcerto.map.model.Ville;
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowListRSH;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.Where;
 
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.text.DateFormat;
import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
 
public class N4DS {
 
private static DateFormat format = new SimpleDateFormat("ddMMyyyy");
private ComptaPropsConfiguration conf = ((ComptaPropsConfiguration) Configuration.getInstance());
private long masseSalarialeBrute;
private static final byte[] retour = "'\n".getBytes();
private PrintStream stream = null;
 
// FIXME Salarie renvoye
 
/**
* Déclaration normale (type 51)
* */
public N4DS() {
 
}
 
public void createDocument() {
masseSalarialeBrute = 0;
File f = new File("N4DS_" + format.format(new Date()) + ".txt");
 
try {
 
stream = new PrintStream(f, "ISO-8859-1");
 
SQLElement eltSalarie = this.conf.getDirectory().getElement("SALARIE");
 
// Infos emetteur
SQLRow rowSociete = this.conf.getRowSociete();
 
writeS10(stream, rowSociete);
 
writeS20(stream, rowSociete);
 
SQLSelect sel = new SQLSelect(this.conf.getBase());
sel.addSelect(eltSalarie.getTable().getKey());
 
@SuppressWarnings("unchecked")
List<SQLRow> l = (List<SQLRow>) this.conf.getBase().getDataSource().execute(sel.asString(), new SQLRowListRSH(eltSalarie.getTable()));
 
for (SQLRow row : l) {
N4DSSalarie s = new N4DSSalarie(this);
s.write(row, rowSociete);
 
}
writeS80(stream, rowSociete);
writeS90(stream);
 
} catch (IOException e) {
e.printStackTrace();
} finally {
if (stream != null) {
stream.close();
}
}
}
 
private void writeS80(PrintStream stream, SQLRow rowSociete) throws IOException {
 
String siren = rowSociete.getString("NUM_SIRET").replaceAll(" ", "").substring(0, 9);
String nic = rowSociete.getString("NUM_SIRET").replaceAll(" ", "").substring(9);
 
// SIREN
write("S80.G01.00.001.001", siren);
 
// NIC
write("S80.G01.00.001.002", nic);
 
SQLRow rowAdr = rowSociete.getForeignRow("ID_ADRESSE_COMMON");
String voie = rowAdr.getString("RUE");
 
Ville ville = Ville.getVilleFromVilleEtCode(rowAdr.getString("VILLE"));
 
// Complement adresse
if (voie.contains("\n")) {
String[] sVoies = voie.split("\n");
if (sVoies.length > 0) {
voie = sVoies[0];
String complement = "";
for (int i = 1; i < sVoies.length; i++) {
complement += sVoies[i].trim() + " ";
}
if (complement.length() > 0) {
complement = complement.substring(0, complement.length() - 1);
}
 
write("S80.G01.00.003.001", complement);
}
}
 
// Voie
write("S80.G01.00.003.006", voie);
 
// TODO Code INSEE, facultatif
// stream.write("S80.G01.00.003.007",voie);
 
// FIXME Service de distribution
// stream.write("S80.G01.00.003.009",ville.getName());
 
// Code postal
write("S80.G01.00.003.010", ville.getCodepostal());
 
// Localité
write("S80.G01.00.003.012", ville.getName().toUpperCase());
 
// Code Pays, ne doit pas être renseigné pour une adresse en France
// TODO support des autres pays
// write("S80.G01.00.003.013", "");
 
// FIXME effectif déclaré
write("S80.G01.00.004.001", String.valueOf(getEffectifDeclare()));
 
// TODO Code établissement sans salarié
// write( "S80.G01.00.004.001", "2");
 
// TODO Code assujettis taxe sur salaires
write("S80.G01.00.005", "01");
 
// Code NAF etablissement
write("S80.G01.00.006", rowSociete.getString("NUM_APE"));
 
// FIXME Code section prud'homale
write("S80.G01.00.007.001", "04");
 
// TODO stecion principale dérogatoire
// write( "S80.G01.00.004.001", "2");
 
// TODO Institution Prevoyance sans salarie
// write( "S80.G01.01.001", "P0012");
// write( "S80.G01.01.002", "0003");
 
// TODO Institution retraite sans salarie
// write( "S80.G01.02.001", "G022");
 
// FIXME Code assujettissement taxe et contribution apprentissage
write("S80.G62.05.001", "01");
long totalApprentissage = Math.round(this.masseSalarialeBrute * 0.0068);
System.err.println(this.masseSalarialeBrute);
write("S80.G62.05.002.001", String.valueOf(totalApprentissage));
 
write("S80.G62.05.003", "02");
 
// FIXME Code assujettissement formation professionnelle continue
write("S80.G62.10.001", "01");
long totalFormation = Math.round(this.masseSalarialeBrute * 0.0055);
write("S80.G62.10.003.001", String.valueOf(totalFormation));
 
}
 
private void writeS90(PrintStream stream) throws IOException {
// Nombre total de rubrique + S90
write("S90.G01.00.001", String.valueOf(this.nbRubrique + 2));
 
// Nombre total de rubrique S20
write("S90.G01.00.002", String.valueOf(1));
}
 
private int nbRubrique = 0;
 
public void write(String rubriqueName, String value) throws IOException {
String tmp = rubriqueName + ",'";
stream.write(tmp.getBytes());
stream.write(value.getBytes());
stream.write(retour);
 
// if (rubriqueName.startsWith("S20")) {
// this.nbRubriqueS20++;
// }
this.nbRubrique++;
}
 
private int getEffectifDeclare() {
// FIXME ne pas inclure les intérimaires
SQLElement eltSalarie = this.conf.getDirectory().getElement("SALARIE");
SQLElement eltInfos = this.conf.getDirectory().getElement("INFOS_SALARIE_PAYE");
SQLSelect sel = new SQLSelect(eltSalarie.getTable().getBase());
sel.addSelect(eltSalarie.getTable().getKey());
Date d2 = new Date(110, 11, 31);
Where w = new Where(eltSalarie.getTable().getField("ID_INFOS_SALARIE_PAYE"), "=", eltInfos.getTable().getKey());
w = w.and(new Where(eltInfos.getTable().getField("DATE_SORTIE"), "=", (Date) null).or(new Where(eltInfos.getTable().getField("DATE_SORTIE"), ">", d2)));
 
sel.setWhere(w);
System.err.println(sel.asString());
List<SQLRow> l = (List<SQLRow>) this.conf.getBase().getDataSource().execute(sel.asString(), new SQLRowListRSH(eltSalarie.getTable()));
 
return (l == null ? 0 : l.size());
}
 
public static String normalizeString2(String s) {
s = s.toUpperCase();
String temp = Normalizer.normalize(s, Form.NFC);
temp = temp.replaceAll("-", " ");
return temp.replaceAll("[^\\p{ASCII}]", "");
}
 
private void writeS20(PrintStream stream, SQLRow rowSociete) throws IOException {
 
// Siren
 
String siren = rowSociete.getString("NUM_SIRET").replaceAll(" ", "").substring(0, 9);
String nic = rowSociete.getString("NUM_SIRET").replaceAll(" ", "").substring(9);
write("S20.G01.00.001", siren);
 
// Raison sociale
write("S20.G01.00.002", rowSociete.getString("NOM"));
 
// FIXME Debut periode
write("S20.G01.00.003.001", "01012011");
 
// FIXME Fin periode
write("S20.G01.00.003.002", "31122011");
 
// Code nature
write("S20.G01.00.004.001", "01");
 
// FIXME Code type (complement, Rectificatif, ...)
write("S20.G01.00.004.002", "51");
 
// fraction
write("S20.G01.00.005", "11");
 
// TODO debut periode rattachement
// stream.write("S20.G01.00.006.001,'","11");
 
// fin periode rattachement
// stream.write("S20.G01.00.006.002,'","11");
 
// Code devise de la déclaration
write("S20.G01.00.007", "01");
 
// NIC
write("S20.G01.00.008", nic);
 
SQLRow rowAdr = rowSociete.getForeignRow("ID_ADRESSE_COMMON");
String voie = rowAdr.getString("RUE");
 
Ville ville = Ville.getVilleFromVilleEtCode(rowAdr.getString("VILLE"));
 
// Complement adresse
if (voie.contains("\n")) {
String[] sVoies = voie.split("\n");
if (sVoies.length > 0) {
voie = sVoies[0];
String complement = "";
for (int i = 1; i < sVoies.length; i++) {
complement += sVoies[i].trim() + " ";
}
if (complement.length() > 0) {
complement = complement.substring(0, complement.length() - 1);
}
 
write("S20.G01.00.009.001", complement);
}
}
 
// Voie
write("S20.G01.00.009.006", voie);
 
// TODO Code INSEE
// stream.write("S20.G01.00.009.007",voie);
 
// FIXME Service de distribution
// stream.write("S20.G01.00.009.009",ville.getName());
 
// Code postal
write("S20.G01.00.009.010", ville.getCodepostal());
 
// Localité
write("S20.G01.00.009.012", ville.getName().toUpperCase());
 
// TODO Code Pays pour les autres pays que la France
// write("S20.G01.00.009.013","");
// write("S20.G01.00.009.016","");
 
write("S20.G01.00.013.002", "1");
// Code periodicite
// TODO déclaration autre que annuelle
write("S20.G01.00.018", "A00");
 
}
 
/**
* Strucuture S10, N4DS
* */
private void writeS10(PrintStream stream, SQLRow rowSociete) throws IOException {
 
// Siren
 
String siren = rowSociete.getString("NUM_SIRET").replaceAll(" ", "").substring(0, 9);
String nic = rowSociete.getString("NUM_SIRET").replaceAll(" ", "").substring(9);
write("S10.G01.00.001.001", siren);
 
// NIC
write("S10.G01.00.001.002", nic);
 
// Raison sociale
write("S10.G01.00.002", rowSociete.getString("NOM"));
 
SQLRow rowAdr = rowSociete.getForeignRow("ID_ADRESSE_COMMON");
String voie = rowAdr.getString("RUE");
 
Ville ville = Ville.getVilleFromVilleEtCode(rowAdr.getString("VILLE"));
 
// Complement adresse
if (voie.contains("\n")) {
String[] sVoies = voie.split("\n");
if (sVoies.length > 0) {
voie = sVoies[0];
String complement = "";
for (int i = 1; i < sVoies.length; i++) {
complement += sVoies[i] + " ";
}
if (complement.length() > 0) {
complement = complement.substring(0, complement.length() - 1);
}
 
write("S10.G01.00.003.001", complement);
}
}
 
// Voie
write("S10.G01.00.003.006", voie);
 
// TODO Code INSEE, facultatif
// stream.write("S10.G01.00.003.007",voie);
 
// TODO: Service de distribution
write("S10.G01.00.003.009", ville.getName());
 
// Code postal
write("S10.G01.00.003.010", ville.getCodepostal());
 
// Localité
write("S10.G01.00.003.012", ville.getName().toUpperCase());
 
// Code Pays, ne doit pas être renseigné pour une adresse en France
// TODO support des autres pays
// write("S10.G01.00.003.013", "");
 
// FIXME Référence de l'envoi
// Incrémenté le numéro
write("S10.G01.00.004,'", "1");
 
// Nom du logiciel
write("S10.G01.00.005", "OpenConcerto");
 
// Nom de l'éditeur
write("S10.G01.00.006", "ILM Informatique");
 
// Numéro version
// FIXME: utiliser le nuémro de version du logiciel
write("S10.G01.00.007", "1.2");
 
// Code service choisi
write("S10.G01.00.009", "40");
 
// Code envoi du fichier essai ou réel
write("S10.G01.00.010", "02");
 
// Norme utilisée
write("S10.G01.00.011", "V01X06");
 
// Code table char
write("S10.G01.00.012", "01");
 
// TODO Contact pour DADS
// Code civilite
write("S10.G01.01.001.001", "01");
// Nom Contact
// TODO Contact pour DADS
write("S10.G01.01.001.002", "MAILLARD GUILLAUME");
 
// Code domaine
// TODO Contact pour DADS
write("S10.G01.01.002", "03");
 
// Adresse mail
// TODO Contact pour DADS
write("S10.G01.01.005", "contact@ilm-informatique.fr");
 
// Tel
// TODO Contact pour DADS
write("S10.G01.01.006", "0322194472");
// TODO Contact pour DADS
 
// Fax
write("S10.G01.01.007", "0322194408");
}
 
private String getNumeroVoie(String voie) {
String numero = "";
voie = voie.trim();
for (int i = 0; i < voie.trim().length(); i++) {
char c = voie.charAt(i);
if (c >= '0' && c <= '9') {
numero += c;
} else {
break;
}
}
return numero;
}
 
private String getVoieWithoutNumber(String voie) {
voie = voie.trim();
String resultVoie = new String(voie);
 
for (int i = 0; i < voie.trim().length(); i++) {
char c = voie.charAt(i);
if (c >= '0' && c <= '9') {
resultVoie = resultVoie.substring(1);
} else {
break;
}
}
return resultVoie.trim();
}
 
public void addMasseSalarialeBrute(long baseBrute) {
this.masseSalarialeBrute += baseBrute;
 
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/erp/core/humanresources/employe/report/N4DSSalarie.java
New file
0,0 → 1,508
/*
* DO NOT ALTER OR