OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Compare Revisions

Regard whitespace Rev 181 → Rev 182

/trunk/OpenConcerto/lib/pdfbox-2.0.19.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/mysql-connector-java-5.1.40-bin.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/poi-3.17.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/poi-4.1.0.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/flatlaf-0.41.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/fontbox-2.0.19.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/jOpenDocument-1.4rc2.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/jOpenDocument-1.4rc2.badSecurity.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/jOpenDocument-1.4rc2.badSecurity.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/lib/pdfbox-2.0.22.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/pdfbox-2.0.22.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/lib/jOpenCalendar.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/flatlaf-1.2.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/flatlaf-1.2.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/lib/pdfbox2d.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/pdfbox2d.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/lib/mysql-connector-java-5.1.45-bin.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/mysql-connector-java-5.1.45-bin.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/lib/poi-5.0.0.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/poi-5.0.0.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/lib/fontbox-2.0.22.jar
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/lib/fontbox-2.0.22.jar
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/Configuration/Template/Default/SituationCompte.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/SituationCompte.ods
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/Configuration/Template/Default/Relance1.odt
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/EtatVentes.xml
124,4 → 124,28
</element>
</table3>
<element4 location="A4" type="Value" ValueName="DATE">
</element4>
<table4 firstLine="7" endLine="52" endPageLine="54" lastColumn="F" base="Societe" table="SAISIE_VENTE_FACTURE_ELEMENT">
<element location="A" type="fill">
<field base="Societe" name="CODE" />
</element>
 
<element location="B" type="fill">
<field base="Societe" name="NOM" />
</element>
 
<element location="D" type="fill">
<field base="Societe" name="QTE" />
</element>
 
<element location="E" type="fill">
<field base="Societe" name="QTE_REEL" />
</element>
<element location="F" type="fill">
<field base="Societe" name="QTE_MIN" />
</element>
</table4>
</contentDocument>
/trunk/OpenConcerto/Configuration/Template/Default/CommandeClient.xml
24,7 → 24,7
</element>
 
<element location="B6" type="fill">
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/>
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/>
</element>
 
<element location="B7" type="replace" replacePattern="_">
62,7 → 62,7
<field name="NOM" />
</element>
 
<element location="I10" type="fill">
<element location="H10" type="fill">
<field name="ID_CLIENT">
<field name="FORME_JURIDIQUE" />
<field name="NOM" />
69,7 → 69,7
</field>
</element>
 
<element location="I11" type="address.customer.full">
<element location="H11" type="address.customer.full">
 
</element>
 
/trunk/OpenConcerto/Configuration/Template/Default/Relance3.odt
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/Journaux.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/Avoir.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/BonLivraison.xml
24,7 → 24,7
</element>
 
<element location="B6" type="fill">
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/>
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/>
</element>
 
<element location="B7" type="replace" replacePattern="_">
/trunk/OpenConcerto/Configuration/Template/Default/EtatStocks.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/ReleveChequeEmis.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/DepotCheque.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/DepotCheque.ods
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/Configuration/Template/Default/BalanceAgee.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/Operations Report.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/EtatStockInventaire.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/EtatStockInventaire.ods
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/Configuration/Template/Default/GrandLivre.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/BonReception.xml
25,7 → 25,7
</element>
 
<element location="B6" type="fill">
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/>
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/>
</element>
 
<element location="B7" type="replace" replacePattern="_">
/trunk/OpenConcerto/Configuration/Template/Default/FichePayeSimplifiee.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/BonReception.odsp
1,6 → 1,6
<odsp>
<spliteveryrow>
<sheet number="0">57</sheet>
<sheet number="0">58</sheet>
</spliteveryrow>
<offset x="40" y ="20"/>
<resize percent="85"/>
/trunk/OpenConcerto/Configuration/Template/Default/ReleveCheque.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/DIPE.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/FichePayeSimplifiee.odsp
1,6 → 1,7
<odsp>
<spliteveryrow>
<sheet number="0">61</sheet>
<sheet number="0">65</sheet>
</spliteveryrow>
<offset x="20" y ="20"/>
<resize percent="92"/>
<offset x="23" y ="0"/>
</odsp>
/trunk/OpenConcerto/Configuration/Template/Default/Devis.xml
23,7 → 23,7
</element>
 
<element location="B6" type="fill">
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/>
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/>
</element>
 
<element location="B7" type="replace" replacePattern="_">
62,7 → 62,7
<field name="OBJET" />
</element>
 
<element location="I10" type="fill">
<element location="H10" type="fill">
<field name="ID_CLIENT">
<field name="FORME_JURIDIQUE" />
<field name="NOM" />
69,7 → 69,7
</field>
</element>
 
<element location="I11" type="address.customer.full">
<element location="H11" type="address.customer.full">
</element>
 
<element location="L63" type="fill">
/trunk/OpenConcerto/Configuration/Template/Default/VenteFactureSituation.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/FichePaye.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/VenteFactureTicket.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/VenteFactureTicket.ods
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/Configuration/Template/Default/DemandePrix.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/ReportingEcoContribution.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/AvoirF.xml
27,7 → 27,7
</element>
 
<element location="B6" type="fill">
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/>
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/>
</element>
 
<element location="B7" type="replace" replacePattern="_">
/trunk/OpenConcerto/Configuration/Template/Default/SituationCompte.xml
New file
0,0 → 1,57
<?xml version="1.0" encoding="UTF-8" ?>
<contentDocument>
 
<element0 location="A1" type="Value" ValueName="SOCIETE_NOM">
</element0>
<element0 location="A2" type="Value" ValueName="SOCIETE_ADRESSE">
</element0>
<element0 location="A4" type="Value" ValueName="SOCIETE_VILLE">
</element0>
<element0 location="A5" type="Value" prefix="Tél:" ValueName="SOCIETE_TEL">
</element0>
 
<element0 location="D6" type="Value" ValueName="NOM_CLIENT">
</element0>
<element0 location="F8" type="Value" ValueName="DATE">
</element0>
<element0 location="G51" type="Value" ValueName="TOTAL_ECHEANCE">
</element0>
<element0 location="G52" type="Value" ValueName="TOTAL_FUTUR">
</element0>
<element0 location="G53" type="Value" ValueName="TOTAL_PASSE">
</element0>
 
<table0 firstLine="17" endLine="50" endPageLine="58" lastColumn="G" base="Societe" table="ECHEANCE_CLIENT" pageRef="F1">
<element location="A" type="fill" useOOFormat="false">
<field base="Societe" name="DATE"/>
</element>
 
<element location="B" type="fill" useOOFormat="false">
<field base="Societe" name="PIECE"/>
</element>
 
 
<element location="C" type="fill" useOOFormat="false" maxChar="25">
<field base="Societe" name="LIBELLE"/>
</element>
 
<element location="D" type="fill" useOOFormat="false">
<field base="Societe" name="DATE_ECHEANCE"/>
</element>
 
<element location="E" type="fill" >
<field base="Societe" name="DU"/>
</element>
 
<element location="F" type="fill" >
<field base="Societe" name="REGLE"/>
</element>
<element location="G" type="fill" >
<field base="Societe" name="SOLDE"/>
</element>
</table0>
</contentDocument>
/trunk/OpenConcerto/Configuration/Template/Default/CommandeClient.odsp
1,6 → 1,6
<odsp>
<spliteveryrow>
<sheet number="0">65</sheet>
<sheet number="0">66</sheet>
</spliteveryrow>
<offset x="40" y ="20"/>
<resize percent="85"/>
/trunk/OpenConcerto/Configuration/Template/Default/JournauxMois.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/Commande.odsp
1,6 → 1,6
<odsp>
<spliteveryrow>
<sheet number="0">57</sheet>
<sheet number="0">58</sheet>
</spliteveryrow>
<offset x="40" y ="20"/>
<resize percent="85"/>
/trunk/OpenConcerto/Configuration/Template/Default/SituationCompte.odsp
New file
0,0 → 1,9
<odsp>
<spliteveryrow>
<sheet number="0">58</sheet>
</spliteveryrow>
<offset x="0" y ="0"/>
<resize percent="100"/>
 
 
</odsp>
/trunk/OpenConcerto/Configuration/Template/Default/FicheArticle.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/Commande.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/DepotCheque.odsp
New file
0,0 → 1,9
<odsp>
<spliteveryrow>
<sheet number="0">56</sheet>
</spliteveryrow>
<offset x="40" y ="20"/>
<resize percent="85"/>
 
 
</odsp>
/trunk/OpenConcerto/Configuration/Template/Default/VenteFacture.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/Avoir.xml
26,7 → 26,7
</element>
 
<element location="B6" type="fill">
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/>
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/>
</element>
 
 
/trunk/OpenConcerto/Configuration/Template/Default/Relance2.odt
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/FactureFournisseur.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/ReportingVentes.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/LivrePaye.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/EtatStockInventaire.odsp
New file
0,0 → 1,9
<odsp>
<spliteveryrow>
<sheet number="0">57</sheet>
</spliteveryrow>
<offset x="40" y ="20"/>
<resize percent="85"/>
 
 
</odsp>
/trunk/OpenConcerto/Configuration/Template/Default/JournauxAnalytique.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/ReportingClient.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/ReportingClient.ods
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/Configuration/Template/Default/DepotCheque.xml
New file
0,0 → 1,60
<?xml version="1.0" encoding="UTF-8" ?>
<contentDocument>
 
<element location="H1" type="fill">
<field base="Societe" name="DATE">
</field>
</element>
<element location="K56" type="fill">
<field base="Societe" name="MONTANT" type="devise">
</field>
</element>
<element location="A55" type="fill">
<field base="Societe" name="NOM">
</field>
</element>
<element location="A56" type="fill">
<field base="Societe" name="ID_BANQUE">
<field base="Societe" name="NOM" prefix ="Dépôt sur la banque "/>
</field>
</element>
 
<table endPageLine="56" firstLine="5" endLine="53" lastColumn="L" base="Societe" table="DEPOT_CHEQUE_ELEMENT">
 
<element location="A" type="fill">
<field name="BANQUE" />
</element>
<element location="B" type="fill">
<field name="DATE" />
</element>
 
<element location="C" type="fill">
<field name="NUMERO" />
</element>
 
<element location="E" type="fill">
<field name="ID_CLIENT" >
<field name="NOM" />
</field>
</element>
 
<element location="J" type="fill">
<field name="PIECE" />
</element>
 
<element location="K" type="fill">
<field name="MONTANT" type="devise"/>
</element>
<element location="L" type="LineReference">
<field name="MONTANT" type="LineReference"/>
</element>
</table>
</contentDocument>
/trunk/OpenConcerto/Configuration/Template/Default/EtatVentes.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/CommandeClient.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/EtatStockInventaire.xml
New file
0,0 → 1,33
<?xml version="1.0" encoding="UTF-8" ?>
<contentDocument>
<!-- <element location="A3" type="Value" ValueName="DATE">
</element>
-->
<table firstLine="2" endLine="17000" endPageLine="25000" lastColumn="E" table="SAISIE_VENTE_FACTURE">
<element location="A" type="fill">
<field name="FAMILLE" />
</element>
<element location="B" type="fill">
<field name="CODE" />
</element>
 
<element location="C" type="fill">
<field name="NOM" />
</element>
<element location="D" type="fill">
<field name="TAILLE" />
</element>
<element location="E" type="fill">
<field name="COULEUR" />
</element>
<element location="F" type="fill">
<field name="QTE" />
</element>
 
</table>
</contentDocument>
/trunk/OpenConcerto/Configuration/Template/Default/ListeDebiteur.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/BonLivraison.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/FicheRelance.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/FichePayeSimplifiee.xml
47,7 → 47,7
<field name="AU" prefix=" au " type="date" datePattern="dd/MM/yy" />
</element>
 
<element location="D54" type="fill">
<element location="D58" type="fill">
<field name="DU" type="datePaye" />
</element>
 
89,7 → 89,7
</element>
<element location="F12" type="fill">
<field name="ID_CUMULS_CONGES">
<field name="ACQUIS" />
<field name="ACQUIS" type="cumulConges" />
</field>
</element>
<element location="B6" type="replace" replacePattern="_">
122,79 → 122,159
</element>
 
 
<element location="I54" type="fill">
<field name="NET_A_PAYER" />
<element location="H54" type="fill">
<field name="NET_AVANT_PAS" />
</element>
 
<element location="I55" type="fill">
<field name="REDUCTION_GVT" />
</element>
 
<element location="D58" type="fill">
<element location="E57" type="fill">
<field name="NET_IMP" />
</element>
<element location="F57" type="fill">
<field name="ID_PAS">
<field name="TAUX_PAS" />
</field>
</element>
<element location="I57" type="fill">
<field name="TOTAL_PAS" />
</element>
 
 
 
 
<element location="I58" type="fill">
<field name="NET_A_PAYER" />
</element>
 
<element location="D62" type="fill">
<field name="SAL_BRUT" />
</element>
<element location="E58" type="fill">
<element location="E62" type="fill">
<field name="COT_SAL" />
</element>
<element location="F58" type="fill">
<element location="F62" type="fill">
<field name="COT_PAT" />
</element>
 
<element location="G58" type="fill">
<element location="G62" type="fill">
<field name="AVANTAGE_NATURE" />
</element>
 
<element location="H58" type="fill">
<element location="H62" type="fill">
<field name="NET_IMP" />
</element>
 
<element location="I58" type="fill">
<element location="I62" type="fill">
<field name="ALLEGEMENT_COTISATION" />
</element>
 
<element location="D59" type="fill">
<element location="D63" type="fill">
<field name="SAL_BRUT" type="cumulPaye" />
</element>
<element location="E59" type="fill">
<element location="E63" type="fill">
<field name="COT_SAL" type="cumulPaye" />
</element>
<element location="F59" type="fill">
<element location="F63" type="fill">
<field name="COT_PAT" type="cumulPaye" />
</element>
<element location="G59" type="fill">
<element location="G63" type="fill">
<field name="AVANTAGE_NATURE" type="cumulPaye" />
</element>
 
<element location="H59" type="fill">
<element location="H63" type="fill">
<field name="NET_IMP" type="cumulPaye" />
</element>
 
<element location="I59" type="fill">
<element location="I63" type="fill">
<field name="ALLEGEMENT_COTISATION" type="cumulPaye" />
</element>
 
<table endPageLine="61" firstLine="15" endLine="51" lastColumn="J" base="Societe" table="FICHE_PAYE_ELEMENT"
<element location="J16" type="fill">
<field name="ID_SALARIE">
<field name="CODE" />
</field>
</element>
<element location="J18" type="fill">
<field name="ID_SALARIE">
<field name="ID_INFOS_SALARIE_PAYE">
<field name="DATE_ARRIVE">
</field>
</field>
</field>
</element>
<element location="J21" type="fichepaye.smic" />
<element location="J23" type="fichepaye.plafond" />
 
<element location="J28" type="fill">
<field name="ID_VARIABLE_SALARIE">
<field name="HEURE_TRAV" />
</field>
</element>
 
<element location="J30" type="fichepaye.heure.sup.total">
</element>
<element location="J32" type="fichepaye.heure.total">
</element>
 
<element location="J35" type="fill">
<field name="ID_VARIABLE_SALARIE">
<field name="HEURE_TRAV_CUMUL_VAL" />
</field>
</element>
<element location="J37" type="fichepaye.heure.sup.cumul.total">
</element>
 
<element location="J39" type="fichepaye.heure.cumul.total">
</element>
 
 
<table endPageLine="65" firstLine="15" endLine="51" lastColumn="J" base="Societe" table="FICHE_PAYE_ELEMENT"
blankLineBeforeStyle="Titre 1,Titre 2" fieldWhere="IMPRESSION" orderBy="POSITION">
<element location="B" type="fill" cellSize="52">
<field name="NOM" />
</element>
<element location="E" type="fill">
<element location="D" type="fill">
<field name="NB_BASE" />
</element>
<element location="E" type="fill">
<field name="TAUX_SAL">
<exclude value="0.00" />
<exclude value="0.00%" />
<exclude value="0.000000" />
</field>
</element>
<element location="F" type="fill">
<field name="TAUX_SAL" />
<field name="MONTANT_SAL_AJ">
<exclude value="0.00" />
<exclude value="0.00%" />
<exclude value="0.000000" />
</field>
</element>
<element location="G" type="fill">
<field name="MONTANT_SAL_AJ" />
<field name="MONTANT_SAL_DED">
<exclude value="0.00" />
<exclude value="0.00%" />
<exclude value="0.000000" />
</field>
</element>
<element location="H" type="fill">
<field name="MONTANT_SAL_DED" />
<field name="TAUX_PAT">
<exclude value="0.00" />
<exclude value="0.00%" />
<exclude value="0.000000" />
</field>
</element>
<!-- <element location="I" type="fill">
<field name="TAUX_PAT" type="Devise" />
</element> -->
<element location="J" type="fill">
<field name="MONTANT_PAT" />
<element location="I" type="fill">
<field name="MONTANT_PAT">
<exclude value="0.00" />
<exclude value="0.00%" />
<exclude value="0.000000" />
</field>
</element>
 
 
/trunk/OpenConcerto/Configuration/Template/Default/ExportArticle.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/VentilationAnalytique.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/VenteFactureSituation.xml
24,7 → 24,7
</element>
 
<element location="B6" type="fill">
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/>
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/>
</element>
 
<element location="B7" type="replace" replacePattern="_">
/trunk/OpenConcerto/Configuration/Template/Default/EtatChargesPaye.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/VenteFactureTicket.xml
New file
0,0 → 1,90
<?xml version="1.0" encoding="UTF-8" ?>
<contentDocument>
 
<element0 location="B1" type="Value" ValueName="SOCIETE_NOM">
</element0>
 
<element0 location="B2" type="Value" ValueName="SOCIETE_RUE">
</element0>
 
<element0 location="B3" type="Value" ValueName="SOCIETE_CODE_POSTAL_VILLE">
</element0>
 
<element0 location="B5" type="Value" ValueName="SOCIETE_TYPE_CAPITAL">
</element0>
 
<element0 location="B6" type="Value" ValueName="SOCIETE_SIRET" prefix="N° de SIREN ">
</element0>
 
<element0 location="B7" type="Value" ValueName="SOCIETE_TVA" prefix="N° de TVA ">
</element0>
 
<element0 location="B8" type="Value" ValueName="SOCIETE_TEL" prefix="N° de Téléphone ">
</element0>
 
<element0 location="B10" type="Value" ValueName="SOCIETE_MAIL" prefix="Email ">
</element0>
 
<element0 location="B13" type="Value" ValueName="NUMERO">
</element0>
 
<element0 location="C13" type="Value" ValueName="DATE">
</element0>
 
<element0 location="H10" type="Value" ValueName="CLIENT">
</element0>
 
<element0 location="H11" type="Value" ValueName="ADRESSE">
</element0>
 
<element0 location="L62" type="Value" ValueName="T_HT">
</element0>
<element0 location="L65" type="Value" ValueName="T_TTC">
</element0>
 
<element0 location="L63" type="Value" ValueName="T_TVA_1">
</element0>
<element0 location="I63" type="Value" ValueName="TVA_1">
</element0>
 
<element0 location="L64" type="Value" ValueName="T_TVA_2">
</element0>
<element0 location="I64" type="Value" ValueName="TVA_2">
</element0>
 
<table0 endPageLine="66" firstLine="20" endLine="60" blankLineBeforeStyle="Titre 1,Titre 2" lastColumn="K"
base="Societe" table="SAISIE_VENTE_FACTURE_ELEMENT" pageRef="L16">
<element location="B" type="fill" cellSize="45">
<field name="CODE" />
</element>
<element location="C" type="fill" cellSize="45">
<field name="NOM" />
</element>
<element location="H" type="fill">
<field name="POURCENT_REMISE">
<exclude value="0.000000" />
</field>
</element>
<element location="I" type="fill">
<field name="PV_HT" type="devise">
<exclude value="0.000000" />
</field>
</element>
<element location="J" type="fill">
<field name="QTE" />
</element>
<element location="K" type="fill">
<field name="TVA">
</field>
</element>
<element location="L" type="fill">
<field name="T_HT_REMISE" type="devise">
<exclude value="0.000000" />
</field>
</element>
 
</table0>
</contentDocument>
/trunk/OpenConcerto/Configuration/Template/Default/DemandePrix.xml
25,7 → 25,7
</element>
 
<element location="B6" type="fill">
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/>
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/>
</element>
 
<element location="B7" type="replace" replacePattern="_">
/trunk/OpenConcerto/Configuration/Template/Default/FicheClient.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/FactureFournisseur.odsp
1,6 → 1,6
<odsp>
<spliteveryrow>
<sheet number="0">57</sheet>
<sheet number="0">58</sheet>
</spliteveryrow>
<offset x="40" y ="20"/>
<resize percent="85"/>
/trunk/OpenConcerto/Configuration/Template/Default/VenteFactureTicket.odsp
New file
0,0 → 1,9
<odsp>
<spliteveryrow>
<sheet number="0">66</sheet>
</spliteveryrow>
<offset x="40" y ="20"/>
<resize percent="85"/>
 
 
</odsp>
/trunk/OpenConcerto/Configuration/Template/Default/BonReception.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/ListeFacture.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/Courrier.odt
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/RepartitionAnalytique.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/Commande.xml
25,7 → 25,7
</element>
 
<element location="B6" type="fill">
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/>
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/>
</element>
 
<element location="B7" type="replace" replacePattern="_">
/trunk/OpenConcerto/Configuration/Template/Default/VenteFacture.xml
25,7 → 25,7
</element>
 
<element location="B6" type="fill">
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/>
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/>
</element>
 
<element location="B7" type="replace" replacePattern="_">
/trunk/OpenConcerto/Configuration/Template/Default/FactureFournisseur.xml
25,7 → 25,7
</element>
 
<element location="B6" type="fill">
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN " />
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET " />
</element>
 
<element location="B7" type="replace" replacePattern="_">
/trunk/OpenConcerto/Configuration/Template/Default/Devis.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/ReportingTaxeComplementaire.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/DemandePrix.odsp
2,8 → 2,4
<spliteveryrow>
<sheet number="0">58</sheet>
</spliteveryrow>
<offset x="40" y ="20"/>
<resize percent="85"/>
 
 
</odsp>
/trunk/OpenConcerto/Configuration/Template/Default/ReportingClient.xml
New file
0,0 → 1,36
<?xml version="1.0" encoding="UTF-8" ?>
<contentDocument>
<element location="C2" type="Value" ValueName="DATE">
</element>
 
<element location="C5" type="Value" ValueName="CLIENT" >
</element>
<table firstLine="8" endLine="305" endPageLine="305" lastColumn="H" >
<element location="B" type="fill">
<field base="Societe" name="NUMERO_FACTURE"/>
</element>
<element location="C" type="fill">
<field base="Societe" name="DATE"/>
</element>
<element location="D" type="fill" cellSize="24">
<field base="Societe" name="NOM"/>
</element>
<element location="E" type="fill">
<field base="Societe" name="ECHEANCE"/>
</element>
<element location="F" type="fill">
<field base="Societe" name="T_TTC"/>
</element>
<element location="G" type="fill">
<field base="Societe" name="REGLE"/>
</element>
 
<element location="H" type="fill">
<field base="Societe" name="DU"/>
</element>
</table>
</contentDocument>
/trunk/OpenConcerto/Configuration/Template/Default/Balance.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Default/AvoirF.ods
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Labels/test.graphicspl
New file
0,0 → 1,30
<?xml version="1.0" encoding="UTF-8" ?>
<graphicspl width="600" height="400" dpi="300"
printratio="0.5">
<rectangle fill="true" x="0" y="380" width="600" height="20"
color="#FF000" />
 
<image x="420" y="20" width="160" height="160" file="icon.png" />
 
 
 
<text x="10" y="10" fontsize="20" font="Verdana">OpenConcerto</text>
 
<text x="390" y="60" fontsize="40" font="Verdana" align="right"
wrap="true" maxwidth="200">Atome Premium</text>
 
<text x="390" y="140" fontsize="40" font="Verdana" align="right"
color="#12000">56.00 € HT</text>
 
 
 
<barcode x="10" y="120" modulewidth="4" type="datamatrix"
width="200">OpenConcerto</barcode>
<barcode x="10" y="220" type="gs1" height="100" modulewidth="6"
fontsize="25">1234566546
</barcode>
<text x="300" y="200" fontsize="10" font="Verdana" align="center"
color="#666666">ARTICLE EN PROMOTION - NON ECHANGEABLE</text>
<rectangle fill="false" x="0" y="0" width="599" height="399" />
 
</graphicspl>
/trunk/OpenConcerto/Configuration/Template/Labels/icon.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/Configuration/Template/Labels/icon.png
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/src/com/zimbra/common/util/BEncoding.java
New file
0,0 → 1,140
/*
*
*/
 
/*
* Created on Jul 7, 2005
*/
package com.zimbra.common.util;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
 
/**
* @author dkarp
*/
public class BEncoding {
 
public static final class BEncodingException extends Exception {
BEncodingException(String msg) { super(msg); }
BEncodingException(Exception e) { super(e); }
}
 
public static String encode(Map object) {
return encode(new StringBuffer(), object).toString();
}
public static String encode(List object) {
return encode(new StringBuffer(), object).toString();
}
 
public static Object decode(String data) throws BEncodingException {
if (data == null)
return null;
try {
Offset offset = new Offset();
Object result = decode(data.toCharArray(), offset);
if (offset.offset != data.length())
throw new BEncodingException("extra characters at end of encoded string");
return result;
} catch (BEncodingException e) {
throw e;
} catch (Exception e) {
throw new BEncodingException(e);
}
}
 
 
private static StringBuffer encode(StringBuffer sb, Object object) {
if (object instanceof Map) {
SortedMap tree = (object instanceof SortedMap ? (SortedMap) object : new TreeMap((Map) object));
sb.append('d');
if (!tree.isEmpty())
for (Iterator it = tree.entrySet().iterator(); it.hasNext(); ) {
Map.Entry entry = (Map.Entry) it.next();
if (entry.getKey() != null && entry.getValue() != null) {
encode(sb, entry.getKey().toString());
encode(sb, entry.getValue());
}
}
sb.append('e');
} else if (object instanceof List) {
Object value;
sb.append('l');
for (Iterator it = ((List) object).iterator(); it.hasNext(); )
if ((value = it.next()) != null)
encode(sb, value);
sb.append('e');
} else if (object instanceof Long || object instanceof Integer || object instanceof Short || object instanceof Byte) {
sb.append('i').append(object).append('e');
} else if (object != null) {
String value = object.toString();
sb.append(value.length()).append(':').append(value);
}
return sb;
}
 
private static final class Offset {
int offset;
}
 
private static Object decode(char[] buffer, Offset offset) throws BEncodingException {
Object key, value;
char c = buffer[offset.offset++];
switch (c) {
case 'd':
Map map = new HashMap();
while ((key = decode(buffer, offset)) != null) {
if ((value = decode(buffer, offset)) == null)
throw new BEncodingException("missing dictionary value for key " + key.toString());
map.put(key.toString(), value);
}
return map;
 
case 'l':
List list = new ArrayList();
while ((key = decode(buffer, offset)) != null)
list.add(key);
return list;
 
case 'e':
return null;
 
case 'i':
return new Long(readLong(buffer, offset, 'e'));
 
default:
offset.offset--;
long length = readLong(buffer, offset, ':');
int start = offset.offset;
offset.offset += length;
return new String(buffer, start, (int) length);
}
}
 
private static long readLong(char[] buffer, Offset offset, char terminator) {
int start = offset.offset;
while (buffer[offset.offset++] != terminator) ;
return Long.parseLong(new String(buffer, start, offset.offset - start - 1));
}
 
public static void main(String[] args) throws BEncodingException {
List list = new ArrayList();
list.add(new Integer(654));
list.add("hwhergk");
list.add(new StringBuffer("74x"));
Map map = new HashMap();
map.put("testing", new Long(5));
map.put("foo2", "bar");
map.put("herp", list);
map.put("Foo", new Float(6.7));
map.put("yy", new TreeMap());
String encoded = encode(map);
System.out.println(encoded);
System.out.println(decode(encoded));
}
}
/trunk/OpenConcerto/src/org/openconcerto/ftp/updater/UpdateManager.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
212,7 → 212,9
private void update(int toVersion) {
 
try {
if (SystemTray.isSupported()) {
SystemTray.getSystemTray().add(new TrayIcon(new ImageIcon(UpdateManager.class.getResource("tray.png")).getImage()));
}
} catch (AWTException e2) {
e2.printStackTrace();
}
299,7 → 301,9
 
void displayMessage(String s) {
try {
if (SystemTray.isSupported()) {
SystemTray.getSystemTray().getTrayIcons()[0].displayMessage("Mise à jour en cours", s, TrayIcon.MessageType.INFO);
}
} catch (Throwable e) {
e.printStackTrace();
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/XMLVersion.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
18,12 → 18,12
import java.util.Map;
import java.util.Set;
 
import net.jcip.annotations.Immutable;
 
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
 
import net.jcip.annotations.Immutable;
 
/**
* Encapsulate all namespaces for a particular version of xml.
*
64,6 → 64,7
this.put("dc", "http://purl.org/dc/elements/1.1/");
this.put("ooo", "http://openoffice.org/2004/office");
this.put("of", "urn:oasis:names:tc:opendocument:xmlns:of:1.2");
this.put("loext", "urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0");
}
};
 
/trunk/OpenConcerto/src/org/openconcerto/openoffice/style/data/DataStyle.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
13,6 → 13,7
package org.openconcerto.openoffice.style.data;
 
import org.openconcerto.openoffice.Feature;
import org.openconcerto.openoffice.Log;
import org.openconcerto.openoffice.ODEpoch;
import org.openconcerto.openoffice.ODPackage;
264,13 → 265,38
decSeparatorAlwaysShown = false;
// see 19.343.2
final String decPlacesAttr = elem.getAttributeValue("decimal-places", numberNS, "");
final String minDecPlacesAttr = elem.getAttributeValue("min-decimal-places", numberNS, "");
final int forcedPlaces, nonZeroPlaces;
if (!decPlacesAttr.isEmpty()) {
final int decPlaces = parsePositive(decPlacesAttr, lenient);
 
final String officeVers = this.getPackage().getFormatVersion().getOfficeVersion();
final Feature minDecFeature;
if (officeVers.equals("1.2")) {
minDecFeature = Feature.EXTENSION;
} else if (officeVers.equals("1.0") || officeVers.equals("1.1")) {
minDecFeature = Feature.UNSUPPORTED;
} else {
minDecFeature = Feature.SUPPORTED;
}
 
if (minDecFeature == Feature.UNSUPPORTED) {
forcedPlaces = decPlaces;
nonZeroPlaces = 0;
} else {
final Namespace minDecNS = minDecFeature == Feature.EXTENSION ? this.getNS().getNS("loext") : numberNS;
final String minDecPlacesAttr = elem.getAttributeValue("min-decimal-places", minDecNS, "");
if (minDecPlacesAttr.isEmpty()) {
// - minDecFeature == Feature.SUPPORTED is from observation (and if a 1.2
// document w/o "min-decimal-places" is saved by LO, then the attribute is
// explicitly added with a value equal to "decimal-places")
// - for the rest of the condition, see 19.356.2 & 3 of v1.3
if (minDecFeature == Feature.SUPPORTED && decReplacement.isEmpty() && elem.getName().equals("number")) {
forcedPlaces = 0;
nonZeroPlaces = decPlaces;
} else {
forcedPlaces = decPlaces;
nonZeroPlaces = 0;
}
} else {
forcedPlaces = parsePositive(minDecPlacesAttr, lenient);
if (forcedPlaces > decPlaces) {
280,6 → 306,7
nonZeroPlaces = decPlaces - forcedPlaces;
}
}
}
} else {
// default style specifies the maximum
forcedPlaces = 0;
/trunk/OpenConcerto/src/org/openconcerto/openoffice/generation/view/BaseGenerationRapport.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
46,6 → 46,7
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
 
import javax.swing.AbstractAction;
325,7 → 326,8
}
}
} catch (final Throwable e) {
ExceptionHandler.handle(BaseGenerationRapport.this, "Impossible de générer le rapport", e);
// generate() always use a Future
this.handleReportException(e instanceof ExecutionException ? e.getCause() : e);
}
}
 
397,6 → 399,10
 
abstract protected R createGeneration(final ReportType type);
 
protected void handleReportException(Throwable t) {
ExceptionHandler.handle(this, "Impossible de générer le rapport", t);
}
 
abstract protected File getFile(final R rg, String docID);
 
protected String getEmailRecipient(final R rg) {
/trunk/OpenConcerto/src/org/openconcerto/openoffice/spreadsheet/Lines.java
58,7 → 58,7
}
 
// newline or line separator or paragraph separator
static private final Pattern SEP_PATTERN = Pattern.compile("(\r?\n)|" + TextNode.VERTICAL_TAB_CHAR + "|\\p{Zl}|\\p{Zp}");
static private final Pattern SEP_PATTERN = Pattern.compile("(\r?\n)|\f|" + TextNode.VERTICAL_TAB_CHAR + "|\\p{Zl}|\\p{Zp}");
 
static private enum Sep {
// allow to re-use exiting lines (be it a line-break or a new paragraph)
76,6 → 76,8
res = Sep.ANY;
} else if (codePoint == TextNode.VERTICAL_TAB_CHAR) {
res = Sep.LINE;
} else if (codePoint == '\f') {
res = Sep.PARAGRAPH;
} else {
final int cat = Character.getType(codePoint);
if (cat == Character.PARAGRAPH_SEPARATOR) {
/trunk/OpenConcerto/src/org/openconcerto/openoffice/Feature.java
New file
0,0 → 1,18
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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;
 
public enum Feature {
UNSUPPORTED, EXTENSION, SUPPORTED;
}
/trunk/OpenConcerto/src/org/openconcerto/openoffice/OOXML.java
49,6 → 49,7
import org.jdom.Namespace;
import org.jdom.Parent;
import org.jdom.Text;
import org.jdom.Verifier;
import org.jdom.xpath.XPath;
import org.xml.sax.SAXException;
 
344,7 → 345,30
return Span.createEmpty(getFormatVersion()).setContent(encodeWSasList(s));
}
 
public final List<Content> encodeWSasList(final String s) {
public final String replaceInvalidChars(final String s) {
return replaceInvalidChars(s, false);
}
 
public final String replaceInvalidChars(final String s, final boolean keepVT) {
final int len = s.length();
StringBuilder sb = null;
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
if (!Verifier.isXMLCharacter(c) && (!keepVT || c != TextNode.VERTICAL_TAB_CHAR)) {
if (sb == null) {
sb = new StringBuilder(len);
sb.append(s.substring(0, i));
}
c = '�';
}
if (sb != null)
sb.append(c);
}
return sb == null ? s : sb.toString();
}
 
public final List<Content> encodeWSasList(String s) {
s = replaceInvalidChars(s, true);
final List<Content> res = new ArrayList<Content>();
final Matcher m = WHITE_SPACE_TO_ENCODE.matcher(s);
int last = 0;
352,8 → 376,7
res.add(new Text(s.substring(last, m.start())));
switch (m.group().charAt(0)) {
case '\n':
// Vertical Tab, see TextNode#VERTICAL_TAB_CHAR
case '\u000B':
case TextNode.VERTICAL_TAB_CHAR:
res.add(getLineBreak());
break;
case '\t':
/trunk/OpenConcerto/src/org/openconcerto/ui/group/Item.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/SwingThrottle.java
New file
0,0 → 1,52
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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;
 
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
import javax.swing.SwingUtilities;
import javax.swing.Timer;
 
public class SwingThrottle {
private Timer timer;
private Runnable runnable;
 
public SwingThrottle(int delayInMs, final Runnable runnable) {
this.runnable = runnable;
timer = new Timer(delayInMs, new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
timer.stop();
SwingUtilities.invokeLater(runnable);
 
}
});
}
 
public synchronized void execute() {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalArgumentException("must be called in EDT");
}
timer.restart();
}
 
public synchronized void executeNow() {
if (timer.isRunning()) {
timer.stop();
runnable.run();
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/IWindowWithArrow.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/Splash.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/state/JTableStateManager.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
142,18 → 142,27
elem.appendChild(sortingColsElem);
for (final Directive d : sorter.getSortingColumns()) {
final Element colElem = doc.createElement("sortCol");
 
sortingColsElem.appendChild(colElem);
final TableColumn col;
if (visibilityModel != null) {
col = visibilityModel.getColumnByModelIndex(d.getColumn());
if (col == null) {
Log.get().warning("null column in visibilityModel column : " + d.getColumn());
}
} else {
col = model.getColumn(getSrc().convertColumnIndexToView(d.getColumn()));
if (col == null) {
Log.get().warning("null column in model for : " + getSrc().convertColumnIndexToView(d.getColumn()));
}
}
if (col != null) {
colElem.setAttribute(IDENTIFIER_ATTR, String.valueOf(col.getIdentifier()));
final int status = d.getDirection();
setSortAttribute(colElem, status);
}
}
}
 
// Use a Transformer for output
final TransformerFactory tFactory = TransformerFactory.newInstance();
/trunk/OpenConcerto/src/org/openconcerto/ui/state/UserWindowStateManager.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
20,6 → 20,8
 
/**
* Delete state if user close (windowClosing) but save state if the program dispose (windowClosed).
* Useful for browser-like behavior, i.e. only restore windows that were visible when the program
* quit.
*
* @author Sylvain CUAZ
*/
/trunk/OpenConcerto/src/org/openconcerto/ui/table/XTableColumnModel.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
261,4 → 261,13
public TableColumn getColumn(int columnIndex, boolean onlyVisible) {
return getColumnsFast(onlyVisible).get(columnIndex);
}
 
@Override
public TableColumn getColumn(int columnIndex) {
if (columnIndex < 0) {
// Fix OpenJDK with FlatUI
columnIndex = 0;
}
return super.getColumn(columnIndex);
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/RoundedMenuItem.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/IMenuWithArrows.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/MovableContainerComponent.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/login/AbstractProperties.java
New file
0,0 → 1,167
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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.login;
 
import org.openconcerto.utils.FileUtils;
 
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
 
public abstract class AbstractProperties {
 
protected final Properties props = new Properties();
 
protected AbstractProperties() {
 
}
 
public Properties getProps() {
return this.props;
}
 
protected abstract String getPropsFileName();
 
public final boolean contains(String name) {
return this.props.containsKey(name + getPropertySuffix());
}
 
public String getPropertySuffix() {
return "";
}
 
/**
* Return the property for the passed key.
*
* @param name the key.
* @return the matching string, never <code>null</code>.
* @see #getDefaultStringValue()
*/
public String getStringProperty(String name) {
final String property = getProperty(name);
if (property == null)
return getDefaultStringValue();
else
return property;
}
 
/**
* Return the property for the passed key.
*
* @param name the key.
* @return the matching property value or <code>null</code>.
*/
public final String getProperty(String name) {
final String key = name + getPropertySuffix();
final String property = this.props.getProperty(key);
return property;
}
 
/**
* Return Boolean.TRUE only if the property is defined and equals to "true". If the property is
* not defined or not equals to "true", return Boolean.FALSE
*/
public final Boolean getBooleanValue(String name) {
final String property = this.getProperty(name);
if (property == null) {
return Boolean.FALSE;
}
return Boolean.valueOf(property);
}
 
public final boolean getBooleanValue(String name, boolean defaultValue) {
final String property = this.getProperty(name);
if (property == null) {
return defaultValue;
}
return Boolean.valueOf(property);
}
 
public String getDefaultStringValue() {
return "";
}
 
public final int getIntProperty(String name) {
return getIntProperty(name, getDefautIntValue());
}
 
public final int getIntProperty(String name, int defaultVal) {
final String property = this.getProperty(name);
return property == null ? defaultVal : Integer.valueOf(property).intValue();
}
 
protected int getDefautIntValue() {
return -1;
}
 
/**
* Set or remove a property.
*
* @param key the key.
* @param value the value, <code>null</code> meaning remove.
*/
public void setProperty(String key, String value) {
final String fullKey = key + getPropertySuffix();
if (value == null)
this.props.remove(fullKey);
else
this.props.setProperty(fullKey, value);
}
 
public void load() throws IOException {
final File file = new File(getPropsFileName());
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);
bufferedInputStream = new BufferedInputStream(fileInputStream);
this.props.load(bufferedInputStream);
} finally {
if (bufferedInputStream != null) {
try {
bufferedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
 
public void store() throws IOException {
BufferedOutputStream bufferedOutputStream = null;
final File file = new File(getPropsFileName());
try {
FileUtils.mkdir_p(file.getParentFile());
final FileOutputStream fileOutputStream = new FileOutputStream(file);
bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
this.props.store(bufferedOutputStream, "");
} finally {
if (bufferedOutputStream != null) {
try {
bufferedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/login/LoginFrame.java
New file
0,0 → 1,572
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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.login;
 
import org.openconcerto.ui.DefaultGridBagConstraints;
import org.openconcerto.ui.ReloadPanel;
import org.openconcerto.ui.SwingThrottle;
import org.openconcerto.ui.component.combo.ISearchableCombo;
import org.openconcerto.ui.valuewrapper.EmptyValueWrapper;
import org.openconcerto.ui.valuewrapper.ValueWrapperFactory;
import org.openconcerto.utils.Base64;
import org.openconcerto.utils.JImage;
import org.openconcerto.utils.i18n.TM;
import org.openconcerto.utils.i18n.TranslationManager;
import org.openconcerto.utils.jsonrpc.JSONRPCClient;
import org.openconcerto.utils.text.SimpleDocumentListener;
 
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
 
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
 
public class LoginFrame extends JFrame implements ActionListener, ConnectionStateListener {
 
public static final int CONNECTION_NOT_INITIATED = -1;
public static final int CONNECTION_INPROGRESS = 0;
public static final int CONNECTION_OK = 1;
public static final int CONNECTION_TIMEOUT = 2;
public static final int CONNECTION_REFUSED = 3;
public static final int CONNECTION_BROKEN = 4;
private final LoginProperties loginProperties;
 
// Login
private EmptyValueWrapper<String> textLogin;
// Password
private JPasswordField textPassWord;
private String encryptedPassword;
private String clearPassword;
private boolean allowStoredPass = true;
 
// Company
private ISearchableCombo<Company> comboCompany;
 
private ReloadPanel reloadPanel;
 
private CompanyIListModel model;
 
// 1/ open an empty interface, rotate reloadpanel
// 2/ fill with stored login/password/ defaultcompany (id+name)
// 3a/ when connection to server is ready, stop reloadpanel
// 3b/ when login+pass are entered, retrieve allowed companies and enable
// connect when
// connection to server is ready
 
private final JCheckBox saveCheckBox = new JCheckBox(getTM().translate("loginPanel.storePass"));
private final JButton buttonConnect = new JButton(getTM().translate("loginPanel.loginAction"));
 
private final JLabel loginLabel = new JLabel(getTM().translate("loginPanel.loginLabel"));
private final JLabel passwordLabel = new JLabel(getTM().translate("loginPanel.passLabel"));
private final JLabel companyLabel = new JLabel(getTM().translate("loginPanel.companyLabel"));
private String localeBaseName = null;
private final List<Locale> localesToDisplay = new ArrayList<Locale>();
private final JButton langButton = new JButton(Locale.ROOT.getLanguage());
private String company;
private final JSONRPCClient client;
private int connectionState = CONNECTION_NOT_INITIATED;
private final ExecutorService executorService = Executors.newFixedThreadPool(1);
private final String serverUrl;
private String context;
private int idCompany;
private SwingThrottle tCheckValidity;
 
public LoginFrame(String serverUrl, final JSONRPCClient client, String context) throws IOException {
this.client = client;
this.serverUrl = serverUrl;
this.context = context;
this.setContentPane(createLoginPanel());
// load stored info
System.err.println("LoginFrame.LoginFrame()" + serverUrl);
this.loginProperties = new LoginProperties(serverUrl);
this.loginProperties.load();
 
final Company storedDefaultCompany = new Company(this.loginProperties.getLastCompanyId(), this.loginProperties.getLastCompanyName());
this.init(this.loginProperties.getLastLoginName(), this.loginProperties.getEncryptedStoredPassword(), storedDefaultCompany);
 
}
 
public synchronized int getConnectionState() {
return this.connectionState;
}
 
public synchronized void setConnectionState(int connectionState) {
this.connectionState = connectionState;
}
 
private TM getTM() {
return org.openconcerto.utils.i18n.TM.getInstance();
}
 
private void init(String storedLogin, String storedEncryptedPassword, Company storedDefaultCompany) {
this.textLogin.setValue(storedLogin);
 
if (this.saveCheckBox != null && storedEncryptedPassword != null && storedEncryptedPassword.length() > 0) {
this.saveCheckBox.setSelected(true);
}
// to show the user its password has been retrieved
if (storedEncryptedPassword != null) {
final char[] s = new char[8];
Arrays.fill(s, ' ');
this.textPassWord.setText(new String(s));
this.clearPassword = null;
} else
this.clearPassword = "";
 
this.model = new CompanyIListModel();
this.comboCompany.initCache(this.model);
 
this.tCheckValidity = new SwingThrottle(800, new Runnable() {
 
@Override
public void run() {
checkValidity();
}
 
});
}
 
private JPanel createLoginPanel() {
JPanel panel = new JPanel();
 
panel.setLayout(new GridBagLayout());
 
final GridBagConstraints c = new GridBagConstraints();
c.gridheight = 1;
c.gridwidth = 2;
c.gridx = 0;
c.gridy = 0;
c.weightx = 1;
c.weighty = 0;
c.insets = new Insets(0, 0, 0, 0);
c.fill = GridBagConstraints.HORIZONTAL;
 
// Logo
 
JImage imageLogo = new JImage(LoginFrame.class.getResource("OpenConcerto_login.png"));
 
imageLogo.setBackground(Color.WHITE);
imageLogo.check();
panel.add(imageLogo, c);
c.gridy++;
c.gridwidth = GridBagConstraints.REMAINDER;
panel.add(new JSeparator(SwingConstants.HORIZONTAL), c);
 
// Login
c.insets = new Insets(2, 2, 1, 2);
c.gridy++;
c.gridwidth = 1;
c.weightx = 0;
 
this.loginLabel.setHorizontalAlignment(SwingConstants.RIGHT);
panel.add(this.loginLabel, c);
 
this.textLogin = new EmptyValueWrapper<String>(ValueWrapperFactory.create(new JTextField(), String.class));
 
c.gridx++;
c.weightx = 1;
panel.add(this.textLogin.getComp(), c);
((JTextField) this.textLogin.getComp()).addActionListener(this);
this.textLogin.addValueListener(new PropertyChangeListener() {
public void propertyChange(final PropertyChangeEvent evt) {
LoginFrame.this.tCheckValidity.execute();
}
});
 
// Password
c.gridy++;
c.gridx = 0;
c.weightx = 0;
System.err.println("LoginFrame.createLoginPanel()22c");
this.passwordLabel.setHorizontalAlignment(SwingConstants.RIGHT);
panel.add(this.passwordLabel, c);
 
this.textPassWord = new JPasswordField();
 
c.gridx++;
c.weightx = 1;
panel.add(this.textPassWord, c);
this.textPassWord.getDocument().addDocumentListener(new SimpleDocumentListener() {
public void update(final DocumentEvent e) {
LoginFrame.this.clearPassword = String.valueOf(LoginFrame.this.textPassWord.getPassword());
LoginFrame.this.tCheckValidity.execute();
}
});
this.textPassWord.addActionListener(this);
System.err.println("LoginFrame.createLoginPanel()22d");
// Societe
 
c.gridy++;
c.gridx = 0;
c.weightx = 0;
 
this.companyLabel.setHorizontalAlignment(SwingConstants.RIGHT);
panel.add(this.companyLabel, c);
 
this.comboCompany = new ISearchableCombo<Company>(true);
this.comboCompany.setEnabled(false);
 
c.gridx++;
panel.add(this.comboCompany, c);
 
// Button
final JPanel panelButton = new JPanel();
panelButton.setOpaque(false);
panelButton.setLayout(new GridBagLayout());
final GridBagConstraints c2 = new DefaultGridBagConstraints();
c2.weightx = 1;
if (this.allowStoredPass) {
 
this.saveCheckBox.setOpaque(false);
panelButton.add(this.saveCheckBox, c2);
c2.weightx = 0;
 
}
c2.gridx++;
 
// Language selector
//
this.langButton.addActionListener(new ActionListener() {
 
@Override
public void actionPerformed(ActionEvent e) {
final JPopupMenu menu = new JPopupMenu();
final Locale locale = Locale.getDefault();
for (final Locale l : LoginFrame.this.localesToDisplay) {
final JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(l.getDisplayName(l));
if (l.equals(locale)) {
menuItem.setSelected(true);
}
menu.add(menuItem);
menuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setUILanguage(l);
}
});
}
 
menu.show(LoginFrame.this.langButton, 0, 0);
 
}
});
this.langButton.setOpaque(false);
this.langButton.setBorderPainted(false);
this.langButton.setContentAreaFilled(false);
this.langButton.setBorder(null);
this.langButton.setFocusable(false);
this.langButton.setVisible(false);
panelButton.add(this.langButton, c2);
c2.gridx++;
this.reloadPanel = new ReloadPanel();
this.reloadPanel.setOpaque(false);
 
this.reloadPanel.setMode(ReloadPanel.MODE_EMPTY);
panelButton.add(this.reloadPanel, c2);
c2.gridx++;
c2.weightx = 0;
this.buttonConnect.setEnabled(false);
this.buttonConnect.setOpaque(false);
panelButton.add(this.buttonConnect, c2);
 
c.gridy++;
c.gridx = 0;
c.weightx = 0;
c.gridwidth = GridBagConstraints.REMAINDER;
c.weighty = 1;
panel.add(panelButton, c);
 
this.buttonConnect.addActionListener(this);
 
initLocalization("org.openconcerto.ui.login.Messages",
Arrays.asList(Locale.FRANCE, Locale.CANADA_FRENCH, new Locale("fr", "CH"), new Locale("fr", "BE"), Locale.UK, Locale.CANADA, Locale.US, Locale.GERMANY, new Locale("de", "CH")));
 
setUILanguage(Locale.getDefault());
 
return panel;
}
 
private void checkValidity() {
String login = this.textLogin.getValue();
this.buttonConnect.setEnabled(login != null && !login.trim().isEmpty() && this.comboCompany.getSelectedItem() != null);
 
// this.buttonConnect.setEnabled(this.connectionAllowed == null &&
// this.areFieldsValidated());
// this.buttonConnect.setToolTipText(this.connectionAllowed);
System.err.println("LoginFrame.checkValidity()");
String ePassword = this.encryptedPassword;
if (ePassword == null) {
if (this.clearPassword == null) {
this.clearPassword = "";
}
ePassword = encodePassword(this.clearPassword);
}
// Empty login is not allowed
if (login == null || login.trim().isEmpty()) {
return;
}
this.reloadPanel.setMode(ReloadPanel.MODE_ROTATE);
try {
sendAllowedCompanyRequest(login, ePassword);
} catch (Exception e) {
// Connection to server impossible
this.reloadPanel.setMode(ReloadPanel.MODE_BLINK);
e.printStackTrace();
}
}
 
@Override
public void actionPerformed(ActionEvent e) {
if (this.comboCompany.getSelectedItem() == null) {
return;
}
 
// Connect
this.setEnabled(false);
this.reloadPanel.setMode(ReloadPanel.MODE_ROTATE);
String ePassword = this.encryptedPassword;
if (ePassword == null) {
if (this.clearPassword == null) {
this.clearPassword = "";
}
ePassword = encodePassword(this.clearPassword);
}
this.company = this.comboCompany.getSelectedItem().getName();
sendLoginRequest(this.textLogin.getValue(), ePassword, this.comboCompany.getSelectedItem().getId(), this.context);
 
}
 
@Override
public void stateChanged(final int oldState, final int newState) {
if (SwingUtilities.isEventDispatchThread()) {
throw new IllegalAccessError("Must not be called in EventDispatchThread");
}
 
SwingUtilities.invokeLater(new Runnable() {
 
@Override
public void run() {
if (newState == CONNECTION_REFUSED || newState == CONNECTION_TIMEOUT) {
LoginFrame.this.reloadPanel.setMode(ReloadPanel.MODE_BLINK);
} else if (newState == CONNECTION_OK) {
LoginFrame.this.reloadPanel.setMode(ReloadPanel.MODE_EMPTY);
} else {
LoginFrame.this.reloadPanel.setMode(ReloadPanel.MODE_BLINK);
// Connection to server lost
// FIXME: autoreconnect after 10s
}
 
}
});
 
}
 
@Override
public void setAllowedCompanies(final List<Company> list) {
if (SwingUtilities.isEventDispatchThread()) {
throw new IllegalAccessError("Must not be called in EventDispatchThread");
}
 
SwingUtilities.invokeLater(new Runnable() {
 
@Override
public void run() {
 
LoginFrame.this.model.updateFrom(list);
if (!list.isEmpty()) {
LoginFrame.this.comboCompany.setEnabled(true);
for (Company company : list) {
// Reselect from saved company
if (company.getId() == LoginFrame.this.loginProperties.getLastCompanyId()) {
LoginFrame.this.comboCompany.setValue(company);
break;
}
}
 
if (LoginFrame.this.comboCompany.getSelectedItem() == null) {
LoginFrame.this.comboCompany.setSelectedIndex(0);
}
LoginFrame.this.buttonConnect.setEnabled(!LoginFrame.this.textLogin.getValue().trim().isEmpty() && LoginFrame.this.comboCompany.getSelectedItem() != null);
} else {
LoginFrame.this.buttonConnect.setEnabled(false);
}
LoginFrame.this.reloadPanel.setMode(ReloadPanel.MODE_EMPTY);
}
});
}
 
@Override
public void setEnabled(boolean b) {
if (b) {
this.reloadPanel.setMode(ReloadPanel.MODE_EMPTY);
}
this.textLogin.getComp().setEnabled(b);
this.textPassWord.setEnabled(b);
this.comboCompany.setEnabled(b);
this.saveCheckBox.setEnabled(b);
this.buttonConnect.setEnabled(b);
}
 
private final void initLocalization(final String baseName, final List<Locale> toDisplay) {
if (baseName == null)
throw new NullPointerException("Null baseName");
if (this.localeBaseName != null)
throw new IllegalStateException("Already inited to " + this.localeBaseName);
this.localeBaseName = baseName;
this.localesToDisplay.addAll(toDisplay);
// this.setUILanguage(UserProps.getInstance().getLocale());
TM.getInstance();
}
 
private void setUILanguage(Locale locale) {
System.err.println("LoginFrame.setUILanguage():" + this.localeBaseName + " locale:" + locale);
final ResourceBundle bundle = ResourceBundle.getBundle(this.localeBaseName, locale, TranslationManager.getControl());
this.loginLabel.setText(bundle.getString("loginLabel"));
this.passwordLabel.setText(bundle.getString("passwordLabel"));
this.companyLabel.setText(bundle.getString("companyLabel"));
this.saveCheckBox.setText(bundle.getString("saveCheckBox"));
this.buttonConnect.setText(bundle.getString("buttonConnect"));
this.langButton.setText(locale.getLanguage());
this.langButton.setVisible(true);
Locale.setDefault(locale);
}
 
private static MessageDigest md;
 
private static synchronized MessageDigest getMessageDigest() {
if (md == null)
try {
md = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
// should be standard
throw new IllegalStateException("no SHA1", e);
}
return md;
}
 
public static String encodePassword(String clearPassword) {
final byte[] s;
synchronized (getMessageDigest()) {
getMessageDigest().reset();
getMessageDigest().update(clearPassword.getBytes());
s = getMessageDigest().digest();
}
return Base64.encodeBytes(s);
}
 
public String getCompany() {
return this.company;
}
 
public void saveAndDispose() throws IOException {
this.loginProperties.setLastCompanyId(this.comboCompany.getSelectedItem().getId());
this.loginProperties.setLastCompanyName(this.comboCompany.getSelectedItem().getName());
this.loginProperties.setLastLoginName(this.textLogin.getValue());
this.loginProperties.store();
dispose();
}
 
private void sendAllowedCompanyRequest(String login, String passwordHash) {
this.executorService.execute(new Runnable() {
 
@Override
public void run() {
LoginFrame.this.client.setCredentials(login, passwordHash);
final JSONObject params = new JSONObject();
params.put("login", login);
try {
JSONObject result = (JSONObject) LoginFrame.this.client.rpcCall(LoginFrame.this.serverUrl, "getAllowedCompanies", params);
JSONArray array = (JSONArray) result.get("companies");
List<Company> list = new ArrayList<>();
for (Object o : array) {
final Company c = new Company();
c.fromJSON((JSONObject) o);
list.add(c);
}
setAllowedCompanies(list);
 
} catch (IOException e) {
setAllowedCompanies(Collections.emptyList());
e.printStackTrace();
}
 
}
});
}
 
/**
* @param encodedPassword : Base64 encodded hash of the password
*/
private void sendLoginRequest(String login, String encodedPassword, int idCompany, final String context) {
this.executorService.execute(new Runnable() {
 
@Override
public void run() {
stateChanged(getConnectionState(), CONNECTION_INPROGRESS);
LoginFrame.this.client.setCredentials(login, encodedPassword);
final JSONObject params = new JSONObject();
params.put("company-id", idCompany);
params.put("context", context);
try {
JSONObject result = (JSONObject) LoginFrame.this.client.rpcCall(LoginFrame.this.serverUrl, "login", params);
if (result.getAsString("status").equals("granted")) {
LoginFrame.this.idCompany = idCompany;
stateChanged(getConnectionState(), CONNECTION_OK);
} else {
stateChanged(getConnectionState(), CONNECTION_REFUSED);
}
} catch (IOException e) {
stateChanged(getConnectionState(), CONNECTION_BROKEN);
}
 
}
});
 
}
 
public int getCompanyId() {
return this.idCompany;
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/login/Messages_en.properties
New file
0,0 → 1,9
saveCheckBox=Store password
buttonConnect=Log in
adminLogin=Administrator
loginLabel=Login
passwordLabel=Password
companyLabel=Company
unknownUser=Unknown user
multipleUser=Multiple users named "{0}"
wrongPass=Wrong password
/trunk/OpenConcerto/src/org/openconcerto/ui/login/LoginProperties.java
New file
0,0 → 1,70
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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.login;
 
import org.openconcerto.utils.FileUtils;
 
public class LoginProperties extends AbstractProperties {
private static final String PASSWORD = "password";
private static final String LAST_LOGIN = "login";
private static final String LAST_COMPANY_ID = "company_id";
private static final String LAST_COMPANY_NAME = "company_name";
private String serverUrl;
 
public LoginProperties(String serverUrl) {
this.serverUrl = serverUrl;
}
 
public String getLastLoginName() {
return getStringProperty(LoginProperties.LAST_LOGIN);
}
 
public void setLastLoginName(String login) {
setProperty(LoginProperties.LAST_LOGIN, login);
}
 
public String getEncryptedStoredPassword() {
return getProperty(LoginProperties.PASSWORD);
}
 
public void setEncryptedStoredPassword(String password) {
setProperty(LoginProperties.PASSWORD, password);
}
 
public int getLastCompanyId() {
return getIntProperty(LoginProperties.LAST_COMPANY_ID, -1);
}
 
public void setLastCompanyId(long id) {
setProperty(LoginProperties.LAST_COMPANY_ID, String.valueOf(id));
}
 
public String getLastCompanyName() {
return getProperty(LoginProperties.LAST_COMPANY_NAME);
}
 
public void setLastCompanyName(String name) {
setProperty(LoginProperties.LAST_COMPANY_NAME, name);
}
 
@Override
protected String getPropsFileName() {
if (this.serverUrl == null) {
throw new IllegalStateException("null server url");
}
String string = System.getProperty("user.home") + "/.openconcerto/" + FileUtils.FILENAME_ESCAPER.escape(this.serverUrl) + "/";
return string + "login.properties";
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/ui/login/Company.java
New file
0,0 → 1,97
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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.login;
 
import org.openconcerto.utils.io.JSONAble;
import org.openconcerto.utils.io.JSONConverter;
 
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
 
import net.minidev.json.JSONObject;
 
public class Company implements Externalizable, JSONAble {
private int id;
private String name;
 
public Company() {
}
 
public Company(int id, String name) {
this.id = id;
this.name = name;
}
 
public int getId() {
return this.id;
}
 
public void setId(int id) {
this.id = id;
}
 
public String getName() {
return this.name;
}
 
public void setName(String name) {
this.name = name;
}
 
@Override
public boolean equals(Object obj) {
if (obj instanceof Company) {
return ((Company) obj).id == this.id;
}
return super.equals(obj);
}
 
@Override
public int hashCode() {
return (int) this.id;
}
 
@Override
public String toString() {
return this.name;
}
 
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(this.id);
out.writeUTF(this.name);
}
 
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this.id = in.readInt();
this.name = in.readUTF();
}
 
@Override
public JSONObject toJSON() {
final JSONObject json = new JSONObject();
json.put("id", this.id);
json.put("name", this.name);
return json;
}
 
@Override
public void fromJSON(JSONObject json) {
this.id = JSONConverter.getParameterFromJSON(json, "id", Integer.class);
this.name = JSONConverter.getParameterFromJSON(json, "name", String.class);
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/login/ConnectionStateListener.java
New file
0,0 → 1,22
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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.login;
 
import java.util.List;
 
public interface ConnectionStateListener {
void stateChanged(int oldState, int newState);
 
void setAllowedCompanies(List<Company> list);
}
/trunk/OpenConcerto/src/org/openconcerto/ui/login/Messages_fr.properties
New file
0,0 → 1,9
saveCheckBox=Mémoriser le mot de passe
buttonConnect=Connexion
adminLogin=Administrateur
loginLabel=Identifiant
passwordLabel=Mot de passe
companyLabel=Société
unknownUser=Utilisateur inconnu
multipleUser=Plusieurs utilisateurs nommés "{0}"
wrongPass=Mot de passe erronné
/trunk/OpenConcerto/src/org/openconcerto/ui/login/OpenConcerto_login.png
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
/trunk/OpenConcerto/src/org/openconcerto/ui/login/OpenConcerto_login.png
New file
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/trunk/OpenConcerto/src/org/openconcerto/ui/login/CompanyIListModel.java
New file
0,0 → 1,37
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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.login;
 
import org.openconcerto.utils.model.DefaultIListModel;
 
import java.util.List;
 
import javax.swing.SwingUtilities;
 
public class CompanyIListModel extends DefaultIListModel<Company> {
 
public void updateFrom(List<Company> list) {
if (!SwingUtilities.isEventDispatchThread()) {
throw new IllegalAccessError("Must be called in EventDispatchThread");
}
if (this.objects.equals(list)) {
// Same list content...
return;
}
this.objects.clear();
this.objects.addAll(list);
fireContentsChanged(this, 0, list.size());
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/ui/VFlowLayout.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/list/HighLightableJLabel.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
16,16 → 16,33
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
 
import javax.swing.JLabel;
 
public class HighLightableJLabel extends JLabel {
public static Color DEFAULT_COLOR = new Color(252, 252, 180);
private String text;
public static final Color DEFAULT_COLOR = new Color(252, 252, 180);
private List<String> texts = new ArrayList<>(1);
private Color highLightColor = DEFAULT_COLOR;
private boolean ignoreCase = true;
 
public HighLightableJLabel() {
super();
}
 
public HighLightableJLabel(String label) {
super(label);
}
 
public void setHightlight(List<String> parts) {
this.texts.clear();
this.texts.addAll(parts);
}
 
public void setHightlight(String text) {
this.text = text;
this.texts.clear();
this.texts.add(text);
}
 
public void setHighLightColor(Color highLightColor) {
32,24 → 49,40
this.highLightColor = highLightColor;
}
 
public void setIgnoreCase(boolean ignoreCase) {
this.ignoreCase = ignoreCase;
}
 
@Override
public void paint(Graphics g) {
if (highLightColor != null && text != null) {
if (this.texts != null && this.highLightColor != null && !this.texts.isEmpty()) {
String currentText = this.getText();
int offset = currentText.indexOf(text);
if (offset >= 0) {
FontMetrics metrics = g.getFontMetrics();
String start = getText().substring(0, offset);
// You may also need to account for some offsets here:
int startX = metrics.stringWidth(start);
int startY = 0; // You probably have some vertical offset to add here.
int length = metrics.stringWidth(text);
int height = metrics.getHeight();
// Now you can draw the highlighted region before drawing the rest of the label:
g.setColor(highLightColor);
g.fillRect(startX, startY, length, height);
if (this.ignoreCase) {
currentText = currentText.toLowerCase();
}
for (String text : this.texts) {
String textToSearch = text;
if (this.ignoreCase) {
textToSearch = textToSearch.toLowerCase();
}
int currentIndex = 0;
int offset = currentText.indexOf(textToSearch, currentIndex);
while (offset >= 0) {
final FontMetrics metrics = g.getFontMetrics();
final String start = getText().substring(0, offset);
final int startX = metrics.stringWidth(start);
final int length = metrics.stringWidth(text);
final int height = metrics.getHeight();
// Draw the highlighted region before drawing the rest of the label:
g.setColor(this.highLightColor);
g.fillRect(startX, 0, length, height);
// Next!
currentIndex = offset + textToSearch.length();
offset = currentText.indexOf(textToSearch, currentIndex);
}
}
}
super.paint(g);
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/ui/grid/DecoratedGridPanel.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
59,7 → 59,12
c.gridx++;
c.weightx = 0;
JPanel spacerScroll = new JPanel();
int wScroll = ((Integer) UIManager.get("ScrollBar.width")).intValue();
Object width = UIManager.get("ScrollBar.width");
if (width == null) {
// Nimbus
width = Integer.valueOf(20);
}
int wScroll = ((Integer) width).intValue();
spacerScroll.setMinimumSize(new Dimension(wScroll, header.getHeight()));
spacerScroll.setPreferredSize(new Dimension(wScroll, header.getHeight()));
spacerScroll.setMaximumSize(new Dimension(wScroll, header.getHeight()));
/trunk/OpenConcerto/src/org/openconcerto/ui/grid/GridPanel.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/RoundedComponentMenu.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/effect/Spots.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
31,6 → 31,10
 
public class Spots extends JComponent implements Runnable {
 
// Allow to disable (more than 10% CPU on x2go VM (java itself + x2goagent))
static public final String START_SLEEPING_PROP_NAME = Spots.class.getSimpleName() + ".startSleeping";
static final boolean START_SLEEPING = Boolean.getBoolean(START_SLEEPING_PROP_NAME);
 
static int width = 256;
static int height = 48;
 
68,8 → 72,9
 
private Image onScreenImage;
private MemoryImageSource imageSource;
private long sleeptime = DEFAULT_SLEEPTIME;
private long sleeptime;
private static final int DEFAULT_SLEEPTIME = 20;
private static final int STOP_SLEEPTIME = 60 * 60 * 1000;
 
public static void main(String[] args) {
JFrame f = new JFrame();
85,7 → 90,11
}
 
public Spots() {
this(START_SLEEPING, true);
}
 
public Spots(final boolean startSleeping, final boolean addMouseListener) {
this.sleeptime = startSleeping ? STOP_SLEEPTIME : DEFAULT_SLEEPTIME;
ga = true;
 
pixles = new int[width * height];
94,21 → 103,21
setPreferredSize(new Dimension(width, height));
setMinimumSize(new Dimension(width, height));
setMaximumSize(new Dimension(width, height));
if (addMouseListener) {
this.addMouseListener(new MouseAdapter() {
 
@Override
public void mouseClicked(MouseEvent e) {
 
if (sleeptime == DEFAULT_SLEEPTIME) {
// Redessine un fois par heure
sleeptime = 60 * 60 * 1000;
sleeptime = STOP_SLEEPTIME;
} else {
sleeptime = DEFAULT_SLEEPTIME;
updateThread.interrupt();
}
}
 
});
}
}
 
public void init() {
setBackground(Color.black);
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUITable.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/UserSearchItem.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIListRow.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/JSONToLightUIConvertorManager.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIRadioButtons.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/UserSearch.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUILabel.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUITabbed.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIHourEditor.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIButtonLink.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIFrame.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/IntValueConvertor.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/InvalidClassException.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIDate.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/JSONToLightUIConvertor.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIPictureUpload.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUserControlContainer.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIText.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/TitleLine.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUITab.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/InformationLine.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/StringValueConvertor.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/SimpleTextLine.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUserControl.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUISlider.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIContainer.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUICheckBox.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIDropDownButton.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIAutoCompleteComboBox.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/ListIntValueConvertor.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/ComboValueConvertor.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIComboBox.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIList.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/LightUIColorPicker.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/light/ColumnSpec.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
288,12 → 288,16
this.editors = JSONToLightUIConvertorManager.getInstance().createUIElementFromJSON(jsonEditors);
}
 
final String sValueClass = JSONConverter.getParameterFromJSON(json, VALUE_CLASS, String.class);
String sValueClass = JSONConverter.getParameterFromJSON(json, VALUE_CLASS, String.class);
if (sValueClass != null) {
try {
if (sValueClass.equals("string")) {
this.valueClass = String.class;
} else {
this.valueClass = Class.forName(sValueClass);
}
} catch (Exception ex) {
throw new IllegalArgumentException("invalid value for 'value-class', " + ex.getMessage());
throw new IllegalArgumentException("invalid value for 'value-class':" + sValueClass, ex);
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/ui/FormLayouter.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/ui/ScrollablePanel.java
New file
0,0 → 1,310
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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;
 
import java.awt.*;
import javax.swing.*;
 
/**
* A panel that implements the Scrollable interface. This class allows you to customize the
* scrollable features by using newly provided setter methods so you don't have to extend this class
* every time.
*
* Scrollable amounts can be specifed as a percentage of the viewport size or as an actual pixel
* value. The amount can be changed for both unit and block scrolling for both horizontal and
* vertical scrollbars.
*
* The Scrollable interface only provides a boolean value for determining whether or not the
* viewport size (width or height) should be used by the scrollpane when determining if scrollbars
* should be made visible. This class supports the concept of dynamically changing this value based
* on the size of the viewport. In this case the viewport size will only be used when it is larger
* than the panels size. This has the effect of ensuring the viewport is always full as components
* added to the panel will be size to fill the area available, based on the rules of the applicable
* layout manager of course.
*/
public class ScrollablePanel extends JPanel implements Scrollable, SwingConstants {
public enum ScrollableSizeHint {
NONE, FIT, STRETCH;
}
 
public enum IncrementType {
PERCENT, PIXELS;
}
 
private ScrollableSizeHint scrollableHeight = ScrollableSizeHint.NONE;
private ScrollableSizeHint scrollableWidth = ScrollableSizeHint.NONE;
 
private IncrementInfo horizontalBlock;
private IncrementInfo horizontalUnit;
private IncrementInfo verticalBlock;
private IncrementInfo verticalUnit;
 
/**
* Default constructor that uses a FlowLayout
*/
public ScrollablePanel() {
this(new FlowLayout());
}
 
/**
* Constuctor for specifying the LayoutManager of the panel.
*
* @param layout the LayountManger for the panel
*/
public ScrollablePanel(LayoutManager layout) {
super(layout);
 
IncrementInfo block = new IncrementInfo(IncrementType.PERCENT, 100);
IncrementInfo unit = new IncrementInfo(IncrementType.PERCENT, 10);
 
setScrollableBlockIncrement(HORIZONTAL, block);
setScrollableBlockIncrement(VERTICAL, block);
setScrollableUnitIncrement(HORIZONTAL, unit);
setScrollableUnitIncrement(VERTICAL, unit);
}
 
/**
* Get the height ScrollableSizeHint enum
*
* @return the ScrollableSizeHint enum for the height
*/
public ScrollableSizeHint getScrollableHeight() {
return scrollableHeight;
}
 
/**
* Set the ScrollableSizeHint enum for the height. The enum is used to determine the boolean
* value that is returned by the getScrollableTracksViewportHeight() method. The valid values
* are:
*
* ScrollableSizeHint.NONE - return "false", which causes the height of the panel to be used
* when laying out the children ScrollableSizeHint.FIT - return "true", which causes the height
* of the viewport to be used when laying out the children ScrollableSizeHint.STRETCH - return
* "true" when the viewport height is greater than the height of the panel, "false" otherwise.
*
* @param scrollableHeight as represented by the ScrollableSizeHint enum.
*/
public void setScrollableHeight(ScrollableSizeHint scrollableHeight) {
this.scrollableHeight = scrollableHeight;
revalidate();
}
 
/**
* Get the width ScrollableSizeHint enum
*
* @return the ScrollableSizeHint enum for the width
*/
public ScrollableSizeHint getScrollableWidth() {
return scrollableWidth;
}
 
/**
* Set the ScrollableSizeHint enum for the width. The enum is used to determine the boolean
* value that is returned by the getScrollableTracksViewportWidth() method. The valid values
* are:
*
* ScrollableSizeHint.NONE - return "false", which causes the width of the panel to be used when
* laying out the children ScrollableSizeHint.FIT - return "true", which causes the width of the
* viewport to be used when laying out the children ScrollableSizeHint.STRETCH - return "true"
* when the viewport width is greater than the width of the panel, "false" otherwise.
*
* @param scrollableWidth as represented by the ScrollableSizeHint enum.
*/
public void setScrollableWidth(ScrollableSizeHint scrollableWidth) {
this.scrollableWidth = scrollableWidth;
revalidate();
}
 
/**
* Get the block IncrementInfo for the specified orientation
*
* @return the block IncrementInfo for the specified orientation
*/
public IncrementInfo getScrollableBlockIncrement(int orientation) {
return orientation == SwingConstants.HORIZONTAL ? horizontalBlock : verticalBlock;
}
 
/**
* Specify the information needed to do block scrolling.
*
* @param orientation specify the scrolling orientation. Must be either:
* SwingContants.HORIZONTAL or SwingContants.VERTICAL.
* @paran type specify how the amount parameter in the calculation of the scrollable amount.
* Valid values are: IncrementType.PERCENT - treat the amount as a % of the viewport size
* IncrementType.PIXEL - treat the amount as the scrollable amount
* @param amount a value used with the IncrementType to determine the scrollable amount
*/
public void setScrollableBlockIncrement(int orientation, IncrementType type, int amount) {
IncrementInfo info = new IncrementInfo(type, amount);
setScrollableBlockIncrement(orientation, info);
}
 
/**
* Specify the information needed to do block scrolling.
*
* @param orientation specify the scrolling orientation. Must be either:
* SwingContants.HORIZONTAL or SwingContants.VERTICAL.
* @param info An IncrementInfo object containing information of how to calculate the scrollable
* amount.
*/
public void setScrollableBlockIncrement(int orientation, IncrementInfo info) {
switch (orientation) {
case SwingConstants.HORIZONTAL:
horizontalBlock = info;
break;
case SwingConstants.VERTICAL:
verticalBlock = info;
break;
default:
throw new IllegalArgumentException("Invalid orientation: " + orientation);
}
}
 
/**
* Get the unit IncrementInfo for the specified orientation
*
* @return the unit IncrementInfo for the specified orientation
*/
public IncrementInfo getScrollableUnitIncrement(int orientation) {
return orientation == SwingConstants.HORIZONTAL ? horizontalUnit : verticalUnit;
}
 
/**
* Specify the information needed to do unit scrolling.
*
* @param orientation specify the scrolling orientation. Must be either:
* SwingContants.HORIZONTAL or SwingContants.VERTICAL.
* @paran type specify how the amount parameter in the calculation of the scrollable amount.
* Valid values are: IncrementType.PERCENT - treat the amount as a % of the viewport size
* IncrementType.PIXEL - treat the amount as the scrollable amount
* @param amount a value used with the IncrementType to determine the scrollable amount
*/
public void setScrollableUnitIncrement(int orientation, IncrementType type, int amount) {
IncrementInfo info = new IncrementInfo(type, amount);
setScrollableUnitIncrement(orientation, info);
}
 
/**
* Specify the information needed to do unit scrolling.
*
* @param orientation specify the scrolling orientation. Must be either:
* SwingContants.HORIZONTAL or SwingContants.VERTICAL.
* @param info An IncrementInfo object containing information of how to calculate the scrollable
* amount.
*/
public void setScrollableUnitIncrement(int orientation, IncrementInfo info) {
switch (orientation) {
case SwingConstants.HORIZONTAL:
horizontalUnit = info;
break;
case SwingConstants.VERTICAL:
verticalUnit = info;
break;
default:
throw new IllegalArgumentException("Invalid orientation: " + orientation);
}
}
 
// Implement Scrollable interface
 
public Dimension getPreferredScrollableViewportSize() {
return super.getPreferredSize();
}
 
public int getScrollableUnitIncrement(Rectangle visible, int orientation, int direction) {
switch (orientation) {
case SwingConstants.HORIZONTAL:
return getScrollableIncrement(horizontalUnit, visible.width);
case SwingConstants.VERTICAL:
return getScrollableIncrement(verticalUnit, visible.height);
default:
throw new IllegalArgumentException("Invalid orientation: " + orientation);
}
}
 
public int getScrollableBlockIncrement(Rectangle visible, int orientation, int direction) {
switch (orientation) {
case SwingConstants.HORIZONTAL:
return getScrollableIncrement(horizontalBlock, visible.width);
case SwingConstants.VERTICAL:
return getScrollableIncrement(verticalBlock, visible.height);
default:
throw new IllegalArgumentException("Invalid orientation: " + orientation);
}
}
 
protected int getScrollableIncrement(IncrementInfo info, int distance) {
if (info.getIncrement() == IncrementType.PIXELS)
return info.getAmount();
else
return distance * info.getAmount() / 100;
}
 
public boolean getScrollableTracksViewportWidth() {
if (scrollableWidth == ScrollableSizeHint.NONE)
return false;
 
if (scrollableWidth == ScrollableSizeHint.FIT)
return true;
 
// STRETCH sizing, use the greater of the panel or viewport width
 
if (getParent() instanceof JViewport) {
return (((JViewport) getParent()).getWidth() > getPreferredSize().width);
}
 
return false;
}
 
public boolean getScrollableTracksViewportHeight() {
if (scrollableHeight == ScrollableSizeHint.NONE)
return false;
 
if (scrollableHeight == ScrollableSizeHint.FIT)
return true;
 
// STRETCH sizing, use the greater of the panel or viewport height
 
if (getParent() instanceof JViewport) {
return (((JViewport) getParent()).getHeight() > getPreferredSize().height);
}
 
return false;
}
 
/**
* Helper class to hold the information required to calculate the scroll amount.
*/
static class IncrementInfo {
private IncrementType type;
private int amount;
 
public IncrementInfo(IncrementType type, int amount) {
this.type = type;
this.amount = amount;
}
 
public IncrementType getIncrement() {
return type;
}
 
public int getAmount() {
return amount;
}
 
public String toString() {
return "ScrollablePanel[" + type + ", " + amount + "]";
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/request/ListSQLRequest.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
23,11 → 23,14
 
import java.util.List;
 
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;
 
@ThreadSafe
public class ListSQLRequest extends FilteredFillSQLRequest {
 
static private final boolean FETCH_METADATA = Boolean.getBoolean("listRequest.fetchMD");
 
private static final FieldExpander getExpander(final FieldExpander showAs) {
final FieldExpander res;
if (showAs != null) {
43,6 → 46,9
return res;
}
 
@GuardedBy("this")
private boolean fetchMD = FETCH_METADATA;
 
public ListSQLRequest(SQLTable table, List<String> fieldss) {
this(table, fieldss, null);
}
80,14 → 86,24
return new ListSQLRequest(this, forFreeze);
}
 
public synchronized final void setMetadataFetched(boolean fetchMD) {
this.fetchMD = fetchMD;
}
 
public synchronized final boolean isMetadataFetched() {
return this.fetchMD;
}
 
// MAYBE use changeGraphToFetch()
@Override
protected final void customizeToFetch(SQLRowValues graphToFetch) {
super.customizeToFetch(graphToFetch);
if (this.isMetadataFetched()) {
addField(graphToFetch, getPrimaryTable().getCreationDateField());
addField(graphToFetch, getPrimaryTable().getCreationUserField());
addField(graphToFetch, getPrimaryTable().getModifDateField());
addField(graphToFetch, getPrimaryTable().getModifUserField());
}
addField(graphToFetch, getPrimaryTable().getFieldRaw(SQLComponent.READ_ONLY_FIELD));
addField(graphToFetch, getPrimaryTable().getFieldRaw(SQLComponent.READ_ONLY_USER_FIELD));
}
/trunk/OpenConcerto/src/org/openconcerto/sql/request/BaseFillSQLRequest.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
18,6 → 18,7
import org.openconcerto.sql.model.IFieldPath;
import org.openconcerto.sql.model.OrderComparator;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValues.CreateMode;
109,6 → 110,8
@GuardedBy("this")
private List<Path> order;
@GuardedBy("this")
private Map<Object, Where> wheres;
@GuardedBy("this")
private Where where;
@GuardedBy("this")
private Map<IFieldPath, SearchField> searchFields;
139,7 → 142,7
throw new NullPointerException();
this.primaryTable = graph.getTable();
this.setOrder(null);
this.where = w;
this.setWhere(w);
this.searchFields = Collections.emptyMap();
this.searchLimit = 35;
this.selTransf = null;
153,6 → 156,7
this.primaryTable = req.getPrimaryTable();
synchronized (req) {
this.order = req.order;
this.wheres = req.wheres;
this.where = req.where;
this.searchFields = req.searchFields;
this.searchLimit = req.searchLimit;
381,6 → 385,48
return 0;
}
 
static private final VirtualFields FIELDS_FOR_ORDER = VirtualFields.PRIMARY_KEY.union(VirtualFields.ORDER);
 
// allow to save memory by only keeping trimmed SQLRow
public final OrderValue createOrderValue(final SQLRowValues r) {
if (!r.isFrozen())
throw new IllegalArgumentException("Row not frozen : " + r);
final List<Path> order = getOrder();
final List<SQLRow> rows = new ArrayList<>(order.size());
for (final Path p : order) {
rows.add(r.followPath(p).trimmedRow(FIELDS_FOR_ORDER));
}
return new OrderValue(Collections.unmodifiableList(rows));
}
 
static public final class OrderValue implements Comparable<OrderValue> {
private final List<SQLRow> rows;
 
OrderValue(List<SQLRow> rows) {
super();
this.rows = rows;
}
 
@Override
public int compareTo(OrderValue o2) {
if (this == o2)
return 0;
final int size = this.rows.size();
if (size != o2.rows.size())
throw new IllegalArgumentException("Not same state");
// same behaviour as SQLSelect
final Comparator<SQLRowAccessor> comp = OrderComparator.getFallbackToPKInstance();
for (int i = 0; i < size; i++) {
final SQLRow r1 = this.rows.get(i);
final SQLRow r2 = o2.rows.get(i);
final int res = comp.compare(r1, r2);
if (res != 0)
return res;
}
return 0;
}
}
 
protected List<Path> getDefaultOrder() {
return getTableOrder();
}
400,9 → 446,35
this.order = l == null ? null : Collections.unmodifiableList(new ArrayList<Path>(l));
}
 
/**
* Set a where to be AND'd.
*
* @param o a key, <code>null</code> to change the where set by {@link #setWhere(Where)}.
* @param w the new value, <code>null</code> to remove.
*/
public final void putWhere(final Object o, final Where w) {
synchronized (this) {
checkFrozen();
final Map<Object, Where> newValue = new HashMap<>(this.wheres);
if (w == null)
newValue.remove(o);
else
newValue.put(o, w);
this.wheres = Collections.unmodifiableMap(newValue);
this.where = Where.and(this.wheres.values());
}
fireWhereChange();
}
 
/**
* Set the where, replacing any other set by {@link #putWhere(Object, Where)}.
*
* @param w the new value, <code>null</code> to remove.
*/
public final void setWhere(final Where w) {
synchronized (this) {
checkFrozen();
this.wheres = w == null ? Collections.<Object, Where> emptyMap() : Collections.singletonMap(null, w);
this.where = w;
}
fireWhereChange();
/trunk/OpenConcerto/src/org/openconcerto/sql/request/UpdateBuilder.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
25,7 → 25,7
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.TableRef;
import org.openconcerto.sql.model.Where;
import org.openconcerto.utils.Tuple3.List3;
import org.openconcerto.sql.users.User;
 
import java.util.ArrayList;
import java.util.HashMap;
42,21 → 42,64
*/
public class UpdateBuilder {
 
public final class VirtualJoin {
private final String alias, definition, updateTableField, op, joinedTableField;
 
protected VirtualJoin(final String alias, final String definition, final String updateTableField, final String op, final String joinedTableField) {
super();
this.alias = alias;
this.definition = definition;
this.updateTableField = updateTableField;
this.op = op;
this.joinedTableField = joinedTableField;
}
 
public final String getAlias() {
return this.alias;
}
 
public final String getDefinition() {
return this.definition;
}
 
public final String getUpdateTableField() {
return this.updateTableField;
}
 
public final String getJoinedTableField() {
return this.joinedTableField;
}
 
protected final String getWhere() {
final SQLName joinedTableFieldName = new SQLName(getAlias(), this.getJoinedTableField());
return Where.comparison(getSyntax(), getTable().getField(this.getUpdateTableField()).getSQLNameUntilDBRoot(false).quote(), this.op, joinedTableFieldName.quote());
}
 
protected final String getSelect(final String value) {
return "( select " + value + " from " + this.getDefinition() + " where " + this.getWhere() + " )";
}
}
 
private final SQLTable t;
private final Map<String, String> fields;
private boolean addMetaData;
private User user;
private final List<String> tables;
private Where where;
// alias -> definition, t field, field of the joined table
private final Map<String, List3<String>> virtualJoins;
// alias -> VirtualJoin
private final Map<String, VirtualJoin> virtualJoins;
private final Map<String, Boolean> virtualJoinsOptimized;
 
public UpdateBuilder(SQLTable t) {
public UpdateBuilder(final SQLTable t) {
super();
this.t = t;
this.fields = new LinkedHashMap<String, String>();
this.tables = new ArrayList<String>();
this.virtualJoins = new HashMap<String, List3<String>>(4);
this.virtualJoinsOptimized = new HashMap<String, Boolean>(4);
this.fields = new LinkedHashMap<>();
// this class is low-level and callers don't expect it to automatically add fields
this.addMetaData = false;
this.user = null;
this.tables = new ArrayList<>();
this.virtualJoins = new HashMap<>(4);
this.virtualJoinsOptimized = new HashMap<>(4);
}
 
public final SQLTable getTable() {
63,6 → 106,17
return this.t;
}
 
public final UpdateBuilder setAddMetaData(final boolean addMetaData) {
this.addMetaData = addMetaData;
return this;
}
 
public final UpdateBuilder setUser(final User user) {
this.setAddMetaData(true);
this.user = user;
return this;
}
 
public final SQLSyntax getSyntax() {
return SQLSyntax.get(this.getTable());
}
130,8 → 184,8
public final UpdateBuilder setFromVirtualJoin(final String field, final String joinAlias, final String value) {
final String val;
if (this.isJoinVirtual(joinAlias)) {
final List3<String> virtualJoin = this.virtualJoins.get(joinAlias);
val = "( select " + value + " from " + virtualJoin.get0() + " where " + getWhere(joinAlias, virtualJoin) + " )";
final VirtualJoin virtualJoin = this.virtualJoins.get(joinAlias);
val = virtualJoin.getSelect(value);
} else {
val = value;
}
138,12 → 192,6
return this.set(field, val);
}
 
private final String getWhere(final String joinAlias, final List3<String> virtualJoin) {
assert this.virtualJoins.get(joinAlias) == virtualJoin;
final SQLName joinedTableFieldName = new SQLName(joinAlias, virtualJoin.get2());
return getTable().getField(virtualJoin.get1()).getSQLNameUntilDBRoot(false) + " = " + joinedTableFieldName.quote();
}
 
public final Set<String> getFieldsNames() {
return this.fields.keySet();
}
152,7 → 200,7
return this.fields.isEmpty();
}
 
public final void setWhere(Where where) {
public final void setWhere(final Where where) {
this.where = where;
}
 
179,6 → 227,7
*
* @param definition the table to add, ie either a table name or a sub-select.
* @param rawAlias the SQL alias, can be <code>null</code>, e.g. <code>"t"</code>.
* @see #addVirtualJoin(String, String, boolean, String, String, boolean)
*/
public final void addRawTable(final String definition, final String rawAlias) {
this.tables.add(definition + (rawAlias == null ? "" : " " + rawAlias));
199,9 → 248,17
}
 
public final void addVirtualJoin(final String definition, final String alias, final boolean aliasAlreadyDefined, final String joinedTableField, final String field) {
this.addVirtualJoin(definition, alias, aliasAlreadyDefined, joinedTableField, field, true);
this.addVirtualJoin(definition, alias, aliasAlreadyDefined, joinedTableField, "=", field, true);
}
 
public final VirtualJoin addVirtualJoin(final TableRef t, final String joinedTableField, final String op, final String field) {
return this.addVirtualJoin(t.getSQL(), t.getAlias(), true, joinedTableField, op, field, true);
}
 
public final VirtualJoin addVirtualJoin(final String definition, final String alias, final boolean aliasAlreadyDefined, final String joinedTableField, final String op, final String field) {
return this.addVirtualJoin(definition, alias, aliasAlreadyDefined, joinedTableField, op, field, true);
}
 
/**
* Add a virtual join to this UPDATE. Some systems don't support
* {@link #addRawTable(String, String) multiple tables}, this method is virtual in the sense
215,11 → 272,14
* is already inside the definition, e.g. ( VALUES ... ) as "constTable"(field1, ...) .
* @param joinedTableField the field in the joined table that will match <code>field</code> of
* the update {@link #getTable() table}.
* @param op the operator to compare <code>joinedTableField</code> and <code>field</code>.
* @param field the field in the update {@link #getTable() table}.
* @param optimize if <code>true</code> and if the system supports it, the virtual join will use
* the multiple table support.
* @return the new join.
*/
public final void addVirtualJoin(final String definition, final String alias, final boolean aliasAlreadyDefined, final String joinedTableField, final String field, final boolean optimize) {
public final VirtualJoin addVirtualJoin(final String definition, final String alias, final boolean aliasAlreadyDefined, final String joinedTableField, final String op, final String field,
final boolean optimize) {
if (alias == null)
throw new NullPointerException("No alias");
if (this.virtualJoins.containsKey(alias))
226,30 → 286,47
throw new IllegalStateException("Alias already exists : " + alias);
this.checkField(field);
final String completeDef = aliasAlreadyDefined ? definition : definition + ' ' + SQLBase.quoteIdentifier(alias);
this.virtualJoins.put(alias, new List3<String>(completeDef, field, joinedTableField));
final VirtualJoin res = new VirtualJoin(alias, completeDef, field, op, joinedTableField);
this.virtualJoins.put(alias, res);
this.virtualJoinsOptimized.put(alias, optimize);
return res;
}
 
public final String asString() {
// add tables and where for virtual joins
Where computedWhere = this.where;
final List<String> computedTables = new ArrayList<String>(this.tables);
for (final Entry<String, List3<String>> e : this.virtualJoins.entrySet()) {
final List<String> computedTables = new ArrayList<>(this.tables);
for (final Entry<String, VirtualJoin> e : this.virtualJoins.entrySet()) {
final String joinAlias = e.getKey();
final List3<String> virtualJoin = e.getValue();
final VirtualJoin virtualJoin = e.getValue();
final Where w;
if (this.isJoinVirtual(joinAlias)) {
w = Where.createRaw(SQLBase.quoteIdentifier(virtualJoin.get1()) + " in ( select " + SQLBase.quoteIdentifier(virtualJoin.get2()) + " from " + virtualJoin.get0() + " )");
// use same WHERE as setFromVirtualJoin()
w = Where.createRaw("EXISTS " + virtualJoin.getSelect("1"));
} else {
w = Where.createRaw(getWhere(joinAlias, virtualJoin));
computedTables.add(virtualJoin.get0());
w = Where.createRaw(virtualJoin.getWhere());
computedTables.add(virtualJoin.getDefinition());
}
computedWhere = w.and(computedWhere);
}
final String w = computedWhere == null ? "" : "\nWHERE " + computedWhere.getClause();
return "UPDATE " + this.getSyntax().getUpdate(this.getTable(), unmodifiableList(computedTables), unmodifiableMap(this.fields)) + w;
final Map<String, String> execFields;
if (this.addMetaData) {
execFields = new HashMap<>(this.fields);
setFieldValue(execFields, this.getTable().getModifUserField(), this.user == null ? null : this.user.getId());
setFieldValue(execFields, this.getTable().getModifDateField(), System.currentTimeMillis());
} else {
execFields = unmodifiableMap(this.fields);
}
return "UPDATE " + this.getSyntax().getUpdate(this.getTable(), unmodifiableList(computedTables), execFields) + w;
}
 
static private void setFieldValue(final Map<String, String> vals, final SQLField f, final Object val) {
if (f == null)
return;
vals.put(f.getName(), val == null ? "DEFAULT" : f.getType().toString(val));
}
 
@Override
public String toString() {
return this.getClass().getSimpleName() + ": " + this.asString();
/trunk/OpenConcerto/src/org/openconcerto/sql/request/FilteredFillSQLRequest.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
179,6 → 179,10
return getSole(res, id);
}
 
public final List<SQLRowValues> getValuesFromIDs(Collection<? extends Number> ids) {
return getValues(ids == null ? null : Where.inValues(this.getPrimaryTable().getKey(), ids));
}
 
protected final <T> T getSole(final List<T> res, int id) {
if (res.size() > 1)
throw new IllegalStateException("there's more than one line which has ID " + id + " for " + this + " : " + res);
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SQLElement.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
67,12 → 67,15
import org.openconcerto.sql.utils.SQLUtils.SQLFactory;
import org.openconcerto.sql.view.EditFrame;
import org.openconcerto.sql.view.EditPanel.EditMode;
import org.openconcerto.sql.view.list.IListe.ConvertedAction;
import org.openconcerto.sql.view.list.IListeAction;
import org.openconcerto.sql.view.list.RowAction;
import org.openconcerto.sql.view.list.SQLTableModelColumn;
import org.openconcerto.sql.view.list.SQLTableModelColumnPath;
import org.openconcerto.sql.view.list.SQLTableModelSource;
import org.openconcerto.sql.view.list.SQLTableModelSourceOffline;
import org.openconcerto.sql.view.list.SQLTableModelSourceOnline;
import org.openconcerto.sql.view.list.action.SQLRowValuesAction;
import org.openconcerto.ui.group.Group;
import org.openconcerto.ui.light.ComboValueConvertor;
import org.openconcerto.ui.light.IntValueConvertor;
79,7 → 82,6
import org.openconcerto.ui.light.LightUIComboBox;
import org.openconcerto.ui.light.LightUIElement;
import org.openconcerto.ui.light.LightUIFrame;
import org.openconcerto.ui.light.LightUIPanel;
import org.openconcerto.ui.light.StringValueConvertor;
import org.openconcerto.utils.CollectionMap2Itf.SetMapItf;
import org.openconcerto.utils.CollectionUtils;
127,8 → 129,8
import java.util.Set;
import java.util.SortedMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.function.Supplier;
 
import javax.swing.JComponent;
import javax.swing.JOptionPane;
192,7 → 194,9
private ComboSQLRequest combo;
private ListSQLRequest list;
private SQLTableModelSourceOnline tableSrc;
@Deprecated
private final ListChangeRecorder<IListeAction> rowActions;
private final ListChangeRecorder<SQLRowValuesAction> rowValuesActions;
private final LinkedListMap<String, ITransformer<Tuple2<SQLElement, String>, SQLComponent>> components;
// links
private SQLElementLinks ownedLinks;
236,7 → 240,38
this.code = code == null ? createCode() : code;
this.combo = null;
this.list = null;
this.rowActions = new ListChangeRecorder<IListeAction>(new ArrayList<IListeAction>());
this.rowActions = new ListChangeRecorder<>(Collections.synchronizedList(new ArrayList<>()));
this.rowActions.getRecipe().addListener(new IClosure<ListChangeIndex<IListeAction>>() {
private final Map<IListeAction, SQLRowValuesAction> map = new IdentityHashMap<>();
 
@Override
public void executeChecked(ListChangeIndex<IListeAction> listChange) {
final List<SQLRowValuesAction> oldActions = new ArrayList<>();
for (final IListeAction action : listChange.getItemsRemoved()) {
synchronized (this) {
final SQLRowValuesAction ra = this.map.remove(action);
if (ra != null)
oldActions.add(ra);
}
}
getRowValuesActions().removeAll(oldActions);
 
final List<SQLRowValuesAction> newActions = new ArrayList<>();
for (final IListeAction action : listChange.getItemsAdded()) {
if (action instanceof RowAction) {
synchronized (this) {
if (!this.map.containsKey(action)) {
final ConvertedAction converted = new ConvertedAction((RowAction) action);
this.map.put(action, converted);
newActions.add(converted);
}
}
}
}
getRowValuesActions().addAll(newActions);
}
});
this.rowValuesActions = new ListChangeRecorder<>(Collections.synchronizedList(new ArrayList<>()));
this.resetRelationships();
 
this.components = new LinkedListMap<String, ITransformer<Tuple2<SQLElement, String>, SQLComponent>>();
1945,18 → 1980,23
this.additionalListCols.add(col);
}
 
@Deprecated
public final Collection<IListeAction> getRowActions() {
return this.rowActions;
}
 
public final void addRowActionsListener(final IClosure<ListChangeIndex<IListeAction>> listener) {
this.rowActions.getRecipe().addListener(listener);
public final List<SQLRowValuesAction> getRowValuesActions() {
return this.rowValuesActions;
}
 
public final void removeRowActionsListener(final IClosure<ListChangeIndex<IListeAction>> listener) {
this.rowActions.getRecipe().rmListener(listener);
public final void addRowActionsListener(final IClosure<? super ListChangeIndex<SQLRowValuesAction>> listener) {
this.rowValuesActions.getRecipe().addListener(listener);
}
 
public final void removeRowActionsListener(final IClosure<? super ListChangeIndex<SQLRowValuesAction>> listener) {
this.rowValuesActions.getRecipe().rmListener(listener);
}
 
public String getDescription(SQLRow fromRow) {
return fromRow.toString();
}
2176,11 → 2216,11
* @return a copy ready to be inserted, or <code>null</code> if <code>row</code> cannot be
* copied.
*/
public SQLRowValues createCopy(SQLRowAccessor row, SQLRowAccessor parent) {
public final SQLRowValues createCopy(SQLRowAccessor row, SQLRowAccessor parent) {
return createCopy(row, false, parent);
}
 
public SQLRowValues createCopy(SQLRowAccessor row, final boolean full, SQLRowAccessor parent) {
public final SQLRowValues createCopy(SQLRowAccessor row, final boolean full, SQLRowAccessor parent) {
return this.createCopy(row, full, parent, null, null);
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SQLElementLink.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
15,13 → 15,35
 
import org.openconcerto.sql.element.SQLElement.ReferenceAction;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLFieldRowProcessor;
import org.openconcerto.sql.model.SQLResultSet;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLSyntax;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLType;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.model.graph.Link;
import org.openconcerto.sql.model.graph.Link.Direction;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.model.graph.Step;
import org.openconcerto.utils.CollectionUtils;
 
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
 
import org.apache.commons.dbutils.ResultSetHandler;
 
import net.jcip.annotations.Immutable;
 
/**
* A logical link between two elements. It can be a direct foreign {@link Link} or two links through
* a {@link JoinSQLElement join}. The {@link #getOwner()} needs the {@link #getOwned()}, i.e. if the
193,6 → 215,193
return this.getPathToChild().getStep(-1);
}
 
public final List<SQLRow> getRowsUntilRoot(final Number id) {
return this.getRowsUntilRoot(id, null).getRows();
}
 
/**
* Get all rows above the passed row (including it).
*
* @param id the first row.
* @param fields which fields to fetch, <code>null</code> to fetch all.
* @return all rows from the passed one to the root.
*/
public final RecursiveRows getRowsUntilRoot(final Number id, Set<String> fields) {
return getRecursiveRows(true, id, fields, -1, null);
}
 
public final List<SQLRow> getSubTreeRows(final Number id) {
return this.getSubTreeRows(id, null).getRows();
}
 
public final RecursiveRows getSubTreeRows(final Number id, final Set<String> fields) {
return getSubTreeRows(id, fields, -1);
}
 
/**
* Get all rows beneath the passed root (including it).
*
* @param id the root row.
* @param fields which fields to fetch, <code>null</code> to fetch all.
* @param maxLevel the max number of times to go through the link.
* @return all rows are deterministically ordered (by level, parent order, order ; i.e. root
* first).
*/
public final RecursiveRows getSubTreeRows(final Number id, final Set<String> fields, final int maxLevel) {
return getRecursiveRows(false, id, fields, maxLevel, getOwned().getTable().getOrderField());
}
 
static private final String findUnusedName(final Collection<String> usedNames, final String base) {
String res = base;
int i = 0;
while (usedNames.contains(res)) {
res = base + i++;
}
return res;
}
 
@Immutable
static public final class RecursiveRows {
 
static public final RecursiveRows ZERO_LEVEL = new RecursiveRows(0, Collections.emptyList(), Collections.emptyMap());
 
private final int maxLevel;
private final List<SQLRow> rows;
private final Map<SQLRow, List<Number>> cycles;
 
RecursiveRows(final int maxLevel, final List<SQLRow> rows, final Map<SQLRow, List<Number>> cycles) {
super();
this.maxLevel = maxLevel;
this.rows = Collections.unmodifiableList(rows);
// OK since List<Number> are already immutable
this.cycles = Collections.unmodifiableMap(cycles);
}
 
public final int getMaxLevelRequested() {
return this.maxLevel;
}
 
public final List<SQLRow> getRows() {
if (this.getCycles().isEmpty())
return this.getPartialRows();
else
throw new IllegalStateException("Cycle detected : " + this.getCycles());
}
 
public final List<SQLRow> getPartialRows() {
return this.rows;
}
 
public final Map<SQLRow, List<Number>> getCycles() {
return this.cycles;
}
}
 
private final RecursiveRows getRecursiveRows(final boolean foreign, final Number id, Set<String> fields, final int maxLevel, final SQLField orderField) {
if (this.getOwner() != this.getOwned() || this.isJoin())
throw new IllegalStateException("Not a recurive link : " + this);
final SQLTable t = getOwned().getTable();
final Link singleLink = this.getSingleLink();
final SQLField singleField = singleLink.getSingleField();
if (singleField == null)
throw new UnsupportedOperationException("Multiple fields not yet supported : " + singleLink);
Objects.requireNonNull(id, "id is null");
 
if (maxLevel == 0)
return RecursiveRows.ZERO_LEVEL;
 
final SQLSyntax syntax = t.getDBSystemRoot().getSyntax();
 
if (fields == null)
fields = t.getFieldsName();
final String recursiveT = "recT";
// use array to prevent infinite loop
final String visitedIDsF = findUnusedName(fields, "visitedIDs");
final String visitedIDsRef = recursiveT + '.' + visitedIDsF;
final String visitedIDsCount = syntax.getSQLArrayLength(visitedIDsRef);
// boolean to know about endless loops : we don't stop before visiting a row a second time,
// but just after
final String loopF = findUnusedName(fields, "loop");
 
// firstly visitedIDsF, secondly optional order, then the asked fields
final SQLSelect selNonRec = new SQLSelect();
selNonRec.addRawSelect(syntax.getSQLArray(Collections.singletonList(t.getKey().getFieldRef())), visitedIDsF);
selNonRec.addRawSelect(SQLType.getBoolean(syntax).toString(Boolean.FALSE), loopF);
final boolean useOrder = !foreign && orderField != null;
if (useOrder)
selNonRec.addRawSelect(syntax.cast("null", orderField.getType().getJavaType()), "parentOrder");
selNonRec.addAllSelect(t, fields);
if (!fields.contains(singleField.getName()))
selNonRec.addSelect(singleField);
// need PK for SQLRow
if (!fields.contains(t.getKey().getName()))
selNonRec.addSelect(t.getKey());
selNonRec.setWhere(new Where(t.getKey(), "=", id));
 
// recursive SELECT
final StringBuilder recSelect = new StringBuilder("SELECT ");
recSelect.append(syntax.getSQLArrayAppend(visitedIDsRef, t.getKey().getFieldRef())).append(", ");
recSelect.append(syntax.getSQLArrayContains(visitedIDsRef, t.getKey().getFieldRef())).append(", ");
final int index;
if (useOrder) {
recSelect.append(recursiveT).append('.').append(orderField.getName()).append(", ");
index = 3;
} else {
index = 2;
}
recSelect.append(CollectionUtils.join(selNonRec.getSelect().subList(index, selNonRec.getSelect().size()), ", "));
recSelect.append("\nFROM ").append(t.getSQLName().quote()).append(", ").append(recursiveT);
recSelect.append("\nWHERE ");
if (foreign) {
recSelect.append(t.getKey().getFieldRef()).append(" = ").append(recursiveT + '.' + singleField.getName());
} else {
recSelect.append(singleField.getFieldRef()).append(" = ").append(recursiveT + '.' + t.getKey().getName());
}
// avoid infinite loop
recSelect.append(" and not (").append(recursiveT).append('.').append(loopF).append(')');
if (t.getUndefinedIDNumber() != null) {
recSelect.append(" and ").append(new Where(t.getKey(), "!=", t.getUndefinedID()).getClause());
}
if (maxLevel > 0) {
recSelect.append(" and ").append(visitedIDsCount).append(" < ").append(maxLevel);
}
 
String cte = "with recursive " + recursiveT + "(" + CollectionUtils.join(selNonRec.getSelectNames(), ", ") + ") as (\n" + selNonRec.asString() + "\nunion all\n" + recSelect
+ ")\nSELECT * from " + recursiveT + " ORDER BY " + visitedIDsCount;
if (useOrder) {
cte += ", 2, " + recursiveT + '.' + orderField.getName();
}
 
final List<String> rsNames = new ArrayList<>(selNonRec.getSelectNames());
// int[] visited IDs
rsNames.set(0, null);
// boolean loop
rsNames.set(1, null);
if (useOrder)
rsNames.set(2, null);
 
final List<SQLRow> res = new ArrayList<>();
final Map<SQLRow, List<Number>> cycleRows = new HashMap<>();
final Class<? extends Number> keyType = t.getKey().getType().getJavaType().asSubclass(Number.class);
t.getDBSystemRoot().getDataSource().execute(cte, new ResultSetHandler() {
@Override
public Object handle(ResultSet rs) throws SQLException {
final SQLFieldRowProcessor rowProc = new SQLFieldRowProcessor(t, rsNames);
while (rs.next()) {
final SQLRow row = SQLRow.createFromRS(t, rs, rowProc, true);
final boolean looped = rs.getBoolean(2);
if (looped) {
cycleRows.put(row, SQLResultSet.getList(rs, 1, keyType));
} else {
res.add(row);
}
}
return null;
}
});
return new RecursiveRows(maxLevel, res, cycleRows);
}
 
public final LinkType getLinkType() {
return this.type;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/element/SQLElementDirectory.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
17,6 → 17,7
import org.openconcerto.sql.ShowAs;
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.model.DBStructureItemNotFound;
import org.openconcerto.sql.model.DBSystemRoot;
import org.openconcerto.sql.model.SQLName;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.request.SQLFieldTranslator;
23,6 → 24,7
import org.openconcerto.utils.CollectionMap2;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.SetMap;
import org.openconcerto.utils.cc.Cookies;
import org.openconcerto.utils.cc.ITransformer;
 
import java.lang.reflect.InvocationTargetException;
38,12 → 40,16
import net.jcip.annotations.GuardedBy;
 
/**
* Directory of SQLElement by table.
* Directory of SQLElement by table. <code>final</code> so that {@link SQLElement} can't depend on a
* subclass and can be shared between applications (e.g. using
* {@link #putAll(SQLElementDirectory)}). If some elements needs shared state, use
* {@link #getCookies()}.
*
* @author Sylvain CUAZ
*/
public final class SQLElementDirectory {
 
private final DBSystemRoot systemRoot;
private final Map<SQLTable, SQLElement> elements;
private final SetMap<String, SQLTable> tableNames;
private final SetMap<String, SQLTable> byCode;
56,7 → 62,10
private SQLFieldTranslator translator;
private final ShowAs showAs;
 
public SQLElementDirectory() {
private final Cookies cookies;
 
public SQLElementDirectory(DBSystemRoot systemRoot) {
this.systemRoot = systemRoot;
this.elements = new HashMap<SQLTable, SQLElement>();
// to mimic elements behaviour, if we add twice the same table
// the second one should replace the first one
69,6 → 78,7
this.phrasesPkgName = null;
 
this.showAs = new ShowAs((DBRoot) null);
this.cookies = new Cookies();
}
 
public synchronized final void destroy() {
91,6 → 101,10
return this.translator;
}
 
public final Cookies getCookies() {
return this.cookies;
}
 
private static <K> SQLTable getSoleTable(SetMap<K, SQLTable> m, K key) throws IllegalArgumentException {
final Collection<SQLTable> res = m.getNonNull(key);
if (res.size() > 1)
104,7 → 118,10
}
 
public synchronized final void putAll(SQLElementDirectory o) {
for (final SQLElement elem : o.getElements()) {
// Cookies are needed in addSQLElement() (e.g. by directoryChanged() and getShowAs())
this.cookies.putAll(o.getCookies());
// copy since addSQLElement() removes elements from their previous directory
for (final SQLElement elem : new ArrayList<>(o.getElements())) {
if (!this.contains(elem.getTable()))
this.addSQLElement(elem);
}
147,6 → 164,9
* @return the previously added element.
*/
public synchronized final SQLElement addSQLElement(SQLElement elem) {
if (elem.getTable().getDBSystemRoot() != this.systemRoot) {
System.err.println("SQLElementDirectory.addSQLElement() warning : Not in this system root : " + elem + " " + elem.getTable().getDBSystemRoot() + " != " + this.systemRoot);
}
final SQLElement res = this.removeSQLElement(elem.getTable());
this.elements.put(elem.getTable(), elem);
this.tableNames.add(elem.getTable().getName(), elem.getTable());
/trunk/OpenConcerto/src/org/openconcerto/sql/element/TreesOfSQLRows.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
342,7 → 342,7
public SQLSelect transformChecked(SQLSelect input) {
final FieldRef refField = input.getAlias(pathToTableWithFK.getLast()).getField(ffName);
// eg where RECEPTEUR.ID_LOCAL in (3,12)
return input.andWhere(new Where(refField, valsMap.keySet()));
return input.andWhere(Where.inValues(refField, valsMap.keySet()));
}
});
for (final SQLRowValues newVals : fetcher.fetch()) {
/trunk/OpenConcerto/src/org/openconcerto/sql/translation/messages_en.properties
97,6 → 97,8
ilist.lockRows=Lock rows
ilist.unlockRows=Unlock rows
ilist.metadata={0,choice,0#Modified|1#Created}{1,choice,0#|1# by {2} {3}}{4,choice,0#|1#, {5,date,long} at {5,time,medium}}
ilist.metadata.loading=Metadata loading
ilist.metadata.na=Metadata unvailable
 
sqlComp.stringValueTooLong=The value is {0, plural, one {# character too long} other {# characters too long}}
sqlComp.bdTooHigh=Number too high, {0, plural,\
/trunk/OpenConcerto/src/org/openconcerto/sql/translation/messages_fr.properties
97,6 → 97,8
ilist.lockRows=Verrouiller les lignes
ilist.unlockRows=Déverrouiller les lignes
ilist.metadata={0,choice,0#Modifiée|1#Créée}{1,choice,0#|1# par {2} {3}}{4,choice,0#|1# le {5,date,long} à {5,time,medium}}
ilist.metadata.loading=Métadonnées en chargement
ilist.metadata.na=Métadonnées non disponibles
 
sqlComp.stringValueTooLong=La valeur fait {0, plural, one {# caractère de trop} other {# caractères de trop}}
sqlComp.bdTooHigh=Nombre trop grand, {0, plural,\
/trunk/OpenConcerto/src/org/openconcerto/sql/utils/Diff.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
24,6 → 24,7
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLSchema;
import org.openconcerto.sql.model.SQLServer;
import org.openconcerto.sql.model.SQLSyntax;
import org.openconcerto.sql.model.SQLSyntax.ConstraintType;
import org.openconcerto.sql.model.SQLSystem;
import org.openconcerto.sql.model.SQLTable;
179,6 → 180,7
final AlterTable alterTable = new AlterTable(aT);
final SQLSystem aSystem = aT.getServer().getSQLSystem();
final SQLSystem bSystem = bT.getServer().getSQLSystem();
final SQLSyntax bSyntax = bT.getDBSystemRoot().getSyntax();
{
final Set<String> aFields = aT.getFieldsName();
final Set<String> bFields = bT.getFieldsName();
191,7 → 193,7
for (final String common : CollectionUtils.inter(aFields, bFields)) {
final SQLField aF = aT.getField(common);
final SQLField bF = bT.getField(common);
final Map<Properties, String> diff = aF.getDiffMap(bF, bSystem, true);
final Map<Properties, String> diff = aF.getDiffMap(bF, bSyntax, true);
alterTable.alterColumn(common, bF, diff.keySet());
}
}
259,7 → 261,7
if (alterTable.isEmpty()) {
final String exactDiff = aT.equalsDesc(bT, null, true);
assert exactDiff != null : "Why bother if exactly equals";
final String lenientDiff = aT.equalsDesc(bT, bT.getServer().getSQLSystem(), true);
final String lenientDiff = aT.equalsDesc(bT, bSyntax, true);
if (lenientDiff == null)
Log.get().info("Tables " + aT.getSQLName() + " and " + bT.getSQLName() + " are not exactly equal, but due to diferring DB system features can't be :\n" + exactDiff);
else
/trunk/OpenConcerto/src/org/openconcerto/sql/utils/ChangeTable.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
279,6 → 279,10
return cat(cts, new ChangeRootNameTransformer(r), true).get(0);
}
 
static public String getForeignColumDefaultValue(final SQLTable foreignTable) {
return foreignTable.getKey().getType().toString(foreignTable.getUndefinedIDNumber());
}
 
// allow to factor column name from table and FCSpec
public static final class ForeignColSpec {
 
296,7 → 300,7
static public ForeignColSpec fromTable(SQLTable foreignTable, final boolean absolute) {
if (foreignTable == null)
throw new NullPointerException("null table");
final String defaultVal = foreignTable.getKey().getType().toString(foreignTable.getUndefinedIDNumber());
final String defaultVal = getForeignColumDefaultValue(foreignTable);
final SQLName n = absolute ? foreignTable.getSQLName() : new SQLName(foreignTable.getName());
return new ForeignColSpec(null, n, foreignTable.getKey().getName(), defaultVal);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/utils/SQLUtils.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
22,6 → 22,7
import org.openconcerto.sql.model.SQLResultSet;
import org.openconcerto.sql.model.SQLSystem;
import org.openconcerto.utils.RTInterruptedException;
import org.openconcerto.utils.cc.ITransformerExn;
 
import java.sql.Connection;
import java.sql.ResultSet;
99,6 → 100,15
return executeAtomic(ds, h, false);
}
 
public static final <T, X extends Exception> T executeAtomic(final SQLDataSource ds, final ITransformerExn<? super SQLDataSource, T, X> run) throws SQLException, X {
return executeAtomic(ds, new ConnectionHandlerNoSetup<T, X>() {
@Override
public T handle(SQLDataSource ds) throws X {
return run.transformChecked(ds);
}
});
}
 
/**
* Execute <code>h</code> in a transaction. Only the outer most call to
* <code>executeAtomic()</code> commit or roll back a transaction, for recursive calls if
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowListRSH.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
13,7 → 13,6
package org.openconcerto.sql.model;
 
import org.openconcerto.sql.model.SQLSelect.LockStrength;
import org.openconcerto.utils.Tuple2;
 
import java.sql.ResultSet;
20,10 → 19,7
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
 
import org.apache.commons.dbutils.ResultSetHandler;
33,12 → 29,13
// hashCode()/equals() needed for data source cache
public static final class RSH implements ResultSetHandler {
private final Tuple2<SQLTable, List<String>> names;
private final boolean immutableRows;
 
// allow to create rows from arbitrary columns (and not just directly from actual fields of
// the same table)
// ATTN doesn't check that the types of columns are coherent with the types of the fields
public RSH(final SQLTable t, final List<String> names) {
this(Tuple2.create(t, names));
this(Tuple2.create(t, names), true);
// null are OK (they're ignored)
final List<String> unknown = names.stream().filter(n -> n != null && !t.getFieldsName().contains(n)).collect(Collectors.toList());
if (!unknown.isEmpty())
45,8 → 42,9
throw new IllegalArgumentException("Not all names are fields of " + t + " : " + unknown);
}
 
private RSH(final Tuple2<SQLTable, List<String>> names) {
private RSH(final Tuple2<SQLTable, List<String>> names, final boolean immutableRows) {
this.names = names;
this.immutableRows = immutableRows;
}
 
@Override
53,7 → 51,7
public List<SQLRow> handle(final ResultSet rs) throws SQLException {
// since the result will be cached, disallow its modification (e.g.avoid
// ConcurrentModificationException)
return Collections.unmodifiableList(SQLRow.createListFromRS(this.names.get0(), rs, this.names.get1()));
return SQLRow.createListFromRS(this.names.get0(), rs, this.names.get1(), this.immutableRows);
}
 
@Override
130,7 → 128,7
*/
@Deprecated
static public ResultSetHandler createFromSelect(final SQLSelect sel) {
return create(getIndexes(sel, null, true));
return create(getIndexes(sel, null, true), false);
}
 
/**
144,13 → 142,17
*/
@Deprecated
static public ResultSetHandler createFromSelect(final SQLSelect sel, final TableRef t) {
return create(getIndexes(sel, t, false));
return create(getIndexes(sel, t, false), false);
}
 
static ResultSetHandler create(final Tuple2<SQLTable, List<String>> names) {
return new RSH(names);
static ResultSetHandler create(final Tuple2<SQLTable, List<String>> names, final boolean immutableRows) {
return new RSH(names, immutableRows);
}
 
static public List<SQLRow> fetch(final SQLTable t) throws IllegalArgumentException {
return fetch(t, null);
}
 
static public List<SQLRow> fetch(final SQLTable t, final Collection<? extends Number> ids) throws IllegalArgumentException {
return fetch(t, ids, null);
}
161,6 → 163,9
sel.addSelectStar(t);
else
sel.addAllSelect(t, fields);
// deterministic
sel.addOrder(t, false);
if (ids != null)
sel.setWhere(new Where(t.getKey(), ids));
return execute(sel);
}
194,24 → 199,6
return new SQLSelectHandlerBuilder(sel).setTableRef(t).execute();
}
 
static IResultSetHandler createFromSelect(final SQLSelect sel, final Tuple2<SQLTable, List<String>> indexes, final boolean readCache, final boolean writeCache) {
final Set<SQLTable> tables = new HashSet<SQLTable>();
// not just tables of the fields of the SELECT clause since inner joins can change the rows
// returned
for (final TableRef ref : sel.getTableRefs().values()) {
tables.add(ref.getTable());
}
 
// the SELECT requesting a lock means the caller expects the DB to be accessed
final boolean acquireLock = sel.getLockStrength() != LockStrength.NONE;
return new IResultSetHandler(create(indexes), readCache && !acquireLock, writeCache && !acquireLock) {
@Override
public Set<? extends SQLData> getCacheModifiers() {
return tables;
}
};
}
 
private final SQLTable t;
private final boolean tableOnly;
 
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSelectHandlerBuilder.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
13,10 → 13,13
package org.openconcerto.sql.model;
 
import org.openconcerto.sql.model.SQLSelect.LockStrength;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.Value;
 
import java.util.HashSet;
import java.util.List;
import java.util.Set;
 
public final class SQLSelectHandlerBuilder {
 
23,6 → 26,7
private final SQLSelect sel;
private Value<TableRef> t;
private boolean readCache, writeCache;
private boolean immutableRows;
 
public SQLSelectHandlerBuilder(final SQLSelect sel) {
if (sel == null)
30,6 → 34,7
this.sel = sel;
this.t = Value.getNone();
this.setUseCache(true);
this.immutableRows = true;
}
 
public SQLSelectHandlerBuilder setUseCache(final boolean b) {
56,6 → 61,15
return this.writeCache;
}
 
public final SQLSelectHandlerBuilder setImmutableRows(boolean immutableRows) {
this.immutableRows = immutableRows;
return this;
}
 
public final boolean isImmutableRows() {
return this.immutableRows;
}
 
/**
* Set the table of the rows to be created. Must be used if the query has more than one table.
*
77,9 → 91,23
 
public IResultSetHandler createHandler() {
final Tuple2<SQLTable, List<String>> indexes = SQLRowListRSH.getIndexes(this.sel, this.t.toNonNull(), !this.t.hasValue());
return SQLRowListRSH.createFromSelect(this.sel, indexes, isReadCache(), isWriteCache());
final Set<SQLTable> tables = new HashSet<SQLTable>();
// not just tables of the fields of the SELECT clause since inner joins can change the rows
// returned
for (final TableRef ref : this.sel.getTableRefs().values()) {
tables.add(ref.getTable());
}
 
// the SELECT requesting a lock means the caller expects the DB to be accessed
final boolean acquireLock = this.sel.getLockStrength() != LockStrength.NONE;
return new IResultSetHandler(SQLRowListRSH.create(indexes, this.isImmutableRows()), isReadCache() && !acquireLock, isWriteCache() && !acquireLock) {
@Override
public Set<? extends SQLData> getCacheModifiers() {
return tables;
}
};
}
 
@SuppressWarnings("unchecked")
public List<SQLRow> execute() {
return (List<SQLRow>) this.sel.getSystemRoot().getDataSource().execute(this.sel.asString(), createHandler());
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSystem.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
16,6 → 16,7
import org.openconcerto.sql.utils.SQL_URL;
import org.openconcerto.utils.EnumOrderedSet;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.Tuple2.List2;
import org.openconcerto.utils.cc.ITransformer;
 
import java.sql.Statement;
23,8 → 24,10
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
 
import org.h2.engine.ConnectionInfo;
import org.h2.engine.Constants;
import org.h2.util.StringUtils;
 
135,7 → 138,7
H2("H2") {
 
private static final String TCP_PREFIX = "tcp://";
private static final String SSL_PREFIX = "ssl://";
private static final String ARBITRARY_BASE_NAME = "foo";
 
ITransformer<String, String> getURLTransf(final SQLServer s) {
if (s.getSQLSystem() != this)
144,21 → 147,10
return new ITransformer<String, String>() {
@Override
public String transformChecked(String base) {
final String sep;
// allow one to use mem for server name
// otherwise just cat name and base
// (eg "tcp://127.0.0.1/" + "sample", "file:~/" + "sample" or "" + "sample" )
if (s.getName().equals("mem"))
sep = ":";
else {
// for file, pass either file:, or file:/someDir/
// jdbc:h2:~/test
sep = "";
}
// by default h2 convert database name to upper case (we used to work around it
// with SQLSystem.getMDName() but in r2251 an equalsIgnoreCase() was replaced by
// equals()) see http://code.google.com/p/h2database/issues/detail?id=204
return s.getName() + sep + base + ";DATABASE_TO_UPPER=false";
return s.getName() + base + ";DATABASE_TO_UPPER=false";
}
};
}
187,26 → 179,33
}
 
@Override
public String getHostname(final String server) {
final String prefix;
if (server.startsWith(TCP_PREFIX))
prefix = TCP_PREFIX;
else if (server.startsWith(SSL_PREFIX))
prefix = SSL_PREFIX;
else
return null;
public List2<String> getHostnameAndPath(final String server) {
// append base name to server name to get a valid value
final ConnectionInfo info = new ConnectionInfo(server + ARBITRARY_BASE_NAME);
final String name = info.getName();
final String hostName;
final int pathIndex;
if (info.isRemote()) {
// tcp:// or ssl://server/path
assert name.startsWith("//");
final int slashIndex = name.indexOf('/', 2);
hostName = name.substring(2, slashIndex);
pathIndex = slashIndex + 1;
} else {
// mem: or file:/data/sample or ~/test
hostName = null;
pathIndex = 0;
}
 
// check that our name doesn't contain a path, otherwise we would loose it
// eg dbserv:8084/~/sample
final String hostAndPath = server.substring(prefix.length());
final int firstSlash = hostAndPath.indexOf('/');
if (firstSlash == hostAndPath.lastIndexOf('/')) {
return hostAndPath.substring(0, firstSlash);
} else
return null;
return new List2<>(hostName, name.substring(pathIndex, name.length() - ARBITRARY_BASE_NAME.length()));
}
 
@Override
public boolean isPermanent(final String server) {
return !server.startsWith(H2_IN_MEMORY);
}
 
@Override
public Map<String, String> getConnectionInfo(final String url) {
final Tuple2<String, Map<String, String>> settings = readSettingsFromURL(url);
final Map<String, String> res = new HashMap<String, String>();
316,6 → 315,8
}
}
 
public static final String H2_IN_MEMORY = "mem:";
 
private final String label;
private final EnumOrderedSet<HierarchyLevel> levels;
 
422,15 → 423,37
/**
* The host name for the passed server.
*
* @param server the name of an {@link SQLServer}.
* @return its host or <code>null</code> if <code>server</code> has no host or is too complex
* (eg tcp://127.0.0.1/a/b/c).
* @param server the name of an {@link SQLServer}, e.g. tcp://127.0.0.1/dir/.
* @return its host and its path, both can be <code>null</code> (but not at the same time), e.g.
* [127.0.0.1, dir].
*/
public String getHostname(String server) {
return server;
public List2<String> getHostnameAndPath(String server) {
Objects.requireNonNull(server, "Null server");
return new List2<>(server, null);
}
 
/**
* Whether the passed server runs inside the VM.
*
* @param server a server, e.g. {@link SQLServer#getName()}.
* @return <code>true</code> if <code>server</code> runs inside the VM.
*/
public final boolean isEmbedded(final String server) {
return getHostnameAndPath(server).get0() == null;
}
 
/**
* Whether the passed server survives when all connections to it are closed.
*
* @param server a server, e.g. {@link SQLServer#getName()}.
* @return <code>true</code> if <code>server</code> survives when all connections to it are
* closed.
*/
public boolean isPermanent(String server) {
return true;
}
 
/**
* Parse <code>url</code> to find info needed by {@link SQL_URL}.
*
* @param url a jdbc url, eg
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLTable.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
20,6 → 20,7
import org.openconcerto.sql.model.SQLSelect.LockStrength;
import org.openconcerto.sql.model.SQLSyntax.ConstraintType;
import org.openconcerto.sql.model.SQLTableEvent.Mode;
import org.openconcerto.sql.model.Where.RowComparison;
import org.openconcerto.sql.model.graph.DatabaseGraph;
import org.openconcerto.sql.model.graph.Link;
import org.openconcerto.sql.model.graph.Link.Rule;
30,6 → 31,7
import org.openconcerto.sql.utils.ChangeTable;
import org.openconcerto.sql.utils.PartialUniqueTrigger;
import org.openconcerto.sql.utils.SQLCreateMoveableTable;
import org.openconcerto.sql.utils.SQLUtils;
import org.openconcerto.sql.utils.UniqueConstraintCreatorHelper;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.CompareUtils;
48,6 → 50,8
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
63,6 → 67,7
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
122,7 → 127,7
};
 
@SuppressWarnings("unchecked")
public static final Map<String, Number> getUndefIDs(final SQLSchema schema) {
private static final Map<String, Number> getUndefIDs(final SQLSchema schema) {
assert Thread.holdsLock(UNDEFINED_IDs);
if (!UNDEFINED_IDs.containsKey(schema)) {
final Map<String, Number> r;
255,12 → 260,51
}
final int res = toInsert.size() + toUpdate.size();
if (res > 0) {
undefT.fireTableModified(SQLRow.NONEXISTANT_ID);
undefT.fireTableModified();
}
return res;
}
}
 
public static final boolean unsetUndefIDs(SQLSchema schema, Set<String> tableNames) throws SQLException {
final boolean tableLoaded = schema.getTable(undefTable) != null;
final boolean tableExists = unsetUndefIDs(schema.getDBSystemRoot(), schema.getDBRoot().getName(), tableNames);
if (tableLoaded != tableExists)
throw new IllegalStateException("Root not up to date, table loaded : " + tableLoaded + ", table exists : " + tableExists);
return tableExists;
}
 
public static final boolean unsetUndefIDs(final DBSystemRoot sysRoot, final String rootName, Set<String> tableNames) throws SQLException {
final SQLName undefSQLName = new SQLName(Objects.requireNonNull(rootName, "Missing root name"), undefTable);
final int deletedCount;
try {
// If already in a transaction, don't risk aborting it if a table doesn't exist.
// (it's not strictly required for H2 and MySQL, since the transaction is *not*
// aborted)
deletedCount = SQLUtils.executeAtomic(sysRoot.getDataSource(), (ds) -> {
try (final Statement stmt = ds.getConnection().createStatement()) {
final int res = stmt.executeUpdate("DELETE FROM " + undefSQLName + " WHERE "
+ Where.getCompareValuesClause(SQLBase.quoteIdentifier(UNDEF_TABLE_TABLENAME_FIELD), RowComparison.IN, tableNames, SQLType.getFromSyntax(sysRoot.getSyntax(), Types.VARCHAR, 250)));
assert res >= 0;
return res;
}
});
} catch (SQLException e) {
// nothing to unset
if (sysRoot.getSyntax().isTableNotFoundException(e))
return false;
throw e;
}
if (deletedCount > 0) {
// rootName might exist and thus the above query might succeed, but the root might not
// be loaded or up to date.
final SQLTable undefT = sysRoot.getDescLenient(undefSQLName, SQLTable.class);
if (undefT != null)
undefT.fireTableModified();
}
return true;
}
 
static private boolean AFTER_TX_DEFAULT = true;
 
static public void setDefaultAfterTransaction(final boolean val) {
799,14 → 843,42
}
 
/**
* Return the primary keys of this table.
* Return the fields of the primary key.
*
* @return the fields (SQLField) which are the keys of this table, can be empty.
* @return the fields of the primary key of this table, can be empty.
*/
public synchronized Set<SQLField> getPrimaryKeys() {
public synchronized Set<SQLField> getPrimaryKeyFields() {
return this.primaryKeys;
}
 
@Deprecated
public final Set<SQLField> getPrimaryKeys() {
return this.getPrimaryKeyFields();
}
 
public final List<String> getPKsNames() {
return this.getPKsNames(new ArrayList<String>());
}
 
public final <C extends Collection<String>> C getPKsNames(C pks) {
for (final SQLField f : this.getPrimaryKeys()) {
pks.add(f.getName());
}
return pks;
}
 
public final RowRef createRowRef(final Object... pk) {
return this.createRowRef(Arrays.asList(pk));
}
 
public final RowRef createRowRef(final List<?> pk) {
return new RowRef(this, pk);
}
 
public final RowRef createRowRef(final Number id) {
return new RowRef(this, id);
}
 
public final Set<Link> getForeignLinks() {
return this.getDBSystemRoot().getGraph().getForeignLinks(this);
}
1668,11 → 1740,14
this.removeTableModifiedListener(new BridgeListener(l));
}
 
public final void fireTableModified() {
this.fireTableModified(SQLRow.NONEXISTANT_ID);
}
 
/**
* Previent tous les listeners de la table qu'il y a eu une modification ou ajout si modif de
* d'une ligne particuliere.
* Previent tous les listeners de la table qu'il y a eu une modification.
*
* @param id -1 signifie tout est modifié.
* @param id which ID was modified, {@link SQLRow#NONEXISTANT_ID} meaning all rows.
*/
public void fireTableModified(final int id) {
this.fire(Mode.ROW_UPDATED, id);
1915,7 → 1990,7
// are the closest possible. NOTE that otherSystem is not required to be the system of the other
// table, it might be something else if the other table was loaded into a system different than
// the one which created the dump.
public synchronized String equalsDesc(SQLTable o, SQLSystem otherSystem, boolean compareName) {
public synchronized String equalsDesc(SQLTable o, SQLSyntax otherSyntax, boolean compareName) {
if (o == null)
return "other table is null";
final boolean name = !compareName || this.getName().equals(o.getName());
1930,17 → 2005,17
// if (!this.getTriggers().keySet().equals(o.getTriggers().keySet()))
// return "triggers names unequal : " + this.getTriggers() + " " + o.getTriggers();
// }
final boolean checkComment = otherSystem == null || this.getServer().getSQLSystem().isTablesCommentSupported() && otherSystem.isTablesCommentSupported();
final boolean checkComment = otherSyntax == null || this.getServer().getSQLSystem().isTablesCommentSupported() && otherSyntax.getSystem().isTablesCommentSupported();
if (checkComment && !CompareUtils.equals(this.getComment(), o.getComment()))
return "comment unequal : " + SQLBase.quoteStringStd(this.getComment()) + " != " + SQLBase.quoteStringStd(o.getComment());
return this.equalsChildren(o, otherSystem);
return this.equalsChildren(o, otherSyntax);
}
 
private synchronized String equalsChildren(SQLTable o, SQLSystem otherSystem) {
private synchronized String equalsChildren(SQLTable o, SQLSyntax otherSyntax) {
if (!this.getChildrenNames().equals(o.getChildrenNames()))
return "fields differences: " + this.getChildrenNames() + "\n" + o.getChildrenNames();
 
final String noLink = equalsChildrenNoLink(o, otherSystem);
final String noLink = equalsChildrenNoLink(o, otherSyntax);
if (noLink != null)
return noLink;
 
1950,6 → 2025,7
if (thisLinks.size() != oLinks.size())
return "different number of foreign keys " + thisLinks + " != " + oLinks;
final SQLSystem thisSystem = this.getServer().getSQLSystem();
final SQLSystem otherSystem = otherSyntax == null ? null : otherSyntax.getSystem();
for (final Link l : thisLinks) {
final Link ol = o.getDBSystemRoot().getGraph().getForeignLink(o, l.getCols());
if (ol == null)
2037,7 → 2113,7
* @param otherSystem the system <code>o</code> originates from, can be <code>null</code>.
* @return <code>null</code> if each fields of this exists in <code>o</code> and is equal to it.
*/
public synchronized final String equalsChildrenNoLink(SQLTable o, SQLSystem otherSystem) {
public synchronized final String equalsChildrenNoLink(SQLTable o, SQLSyntax otherSystem) {
for (final SQLField f : this.getFields()) {
final SQLField oField = o.getField(f.getName());
final boolean isPrimary = this.getPrimaryKeys().contains(f);
2106,21 → 2182,6
return res;
}
 
public final List<String> getPKsNames() {
return this.getPKsNames(new ArrayList<String>());
}
 
public synchronized final <C extends Collection<String>> C getPKsNames(C pks) {
for (final SQLField f : this.getPrimaryKeys()) {
pks.add(f.getName());
}
return pks;
}
 
public final String[] getPKsNamesArray() {
return getPKsNames().toArray(new String[0]);
}
 
/**
* Return the indexes mapped by column names. Ie a key will have as value every index that
* mentions it, and a multi-column index will be in several entries. Note: this is not robust
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowValuesCluster.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSyntaxPG.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
26,7 → 26,9
import org.openconcerto.utils.StringUtils;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.xml.XMLCodecUtils;
 
import java.beans.DefaultPersistenceDelegate;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
67,7 → 69,7
*
* @author Sylvain CUAZ
*/
class SQLSyntaxPG extends SQLSyntax {
public class SQLSyntaxPG extends SQLSyntax {
 
// From http://www.postgresql.org/docs/9.0/interactive/multibyte.html
static final short MAX_BYTES_PER_CHAR = 4;
91,9 → 93,11
DATE_SPECS.put(DateProp.MINUTE, "MI");
DATE_SPECS.put(DateProp.SECOND, "SS");
DATE_SPECS.put(DateProp.MICROSECOND, "US");
 
XMLCodecUtils.register(SQLSyntaxPG.class, new DefaultPersistenceDelegate(new String[] {}));
}
 
SQLSyntaxPG() {
public SQLSyntaxPG() {
super(SQLSystem.POSTGRESQL, DATE_SPECS);
this.typeNames.addAll(Boolean.class, "boolean", "bool", "bit");
this.typeNames.addAll(Short.class, "smallint", "int2");
295,6 → 299,22
return SQLUtils.findWithSQLState(exn).getSQLState().equals("40P01");
}
 
@Override
public boolean isTableNotFoundException(Exception exn) {
return SQLUtils.findWithSQLState(exn).getSQLState().equals("42P01");
}
 
@Override
public String getSetLockTimeoutQuery(int millis) {
return "SET lock_timeout to " + millis;
}
 
@Override
public String getShowLockTimeoutQuery() {
final String interval = cast("setting||unit", "INTERVAL");
return "select CAST( EXTRACT(milliseconds from " + interval + ") as int) from pg_settings where \"name\" = 'lock_timeout'";
}
 
private static final Pattern NOW_PTRN = Pattern.compile("\\(?'now'::text\\)?(::timestamp)");
 
@Override
564,4 → 584,74
public String getDropTrigger(Trigger t) {
return "DROP TRIGGER " + SQLBase.quoteIdentifier(t.getName()) + " on " + t.getTable().getSQLName().quote();
}
 
@Override
public String getSessionIDExpression() {
return "pg_backend_pid()";
}
 
@Override
public String getSessionsQuery(final DBSystemRoot sysRoot, final boolean includeSelf) {
final String allRows = "SELECT pid as \"ID\", query as \"QUERY\", usename as \"USER_NAME\" FROM pg_stat_activity WHERE datname=" + quoteString(sysRoot.getName());
if (includeSelf)
return allRows;
return allRows + " and pid != " + this.getSessionIDExpression();
}
 
@Override
public String getVersionFunction() {
// shorter than version() : "PostgreSQL 9.6.9 on x86_64-pc-linux-gnu (Ubuntu
// 9.6.9-2.pgdg16.04+1), compiled by gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609,
// 64-bit"
return "current_setting('server_version')";
}
 
@Override
public String getAllowConnectionsQuery(String sysRootName, boolean allow) {
// REVOKE CONNECT ON DATABASE "ControleKD" TO public : superusers can still connect
 
// to recover if all DB (even postgres and templates) run postgres --single
// (single user / standalone mode ignores datallowconn)
return "UPDATE pg_database SET datallowconn=" + SQLType.getBoolean(this).toString(allow) + " WHERE datname=" + quoteString(sysRootName);
// "ALTER DATABASE WITH ALLOW_CONNECTIONS" isn't allowed on our own base
}
 
@Override
public boolean isConnectionDisallowedException(SQLException exn) {
return "55000".equals(exn.getSQLState());
}
 
@Override
public String getSQLArray(final List<String> sqlExpressions, final String type) {
if (type == null)
return "ARRAY[" + CollectionUtils.join(sqlExpressions, ", ") + ']';
else
return this.quoteString('{' + CollectionUtils.join(sqlExpressions, ", ") + '}') + "::" + type + "[]";
}
 
@Override
public String getSQLArrayContains(String arrayExpression, String itemExpression) {
return itemExpression + " = ANY(" + arrayExpression + ")";
}
 
@Override
public String getSQLArrayLength(final String arrayExpression) {
return "ARRAY_LENGTH(" + arrayExpression + ", 1)";
}
 
@Override
public String getSQLArrayConcat(final String arrayExpression, final String array2Expression) {
return "array_cat(" + arrayExpression + ", " + array2Expression + ")";
}
 
@Override
public String getSQLArrayAppend(final String arrayExpression, final String itemExpression) {
// don't use || as it doesn't handle varchar literal
return "array_append(" + arrayExpression + ", " + itemExpression + ")";
}
 
@Override
public String getSQLArraySlice(final String arrayExpression, final String index1Expression, final String index2Expression) {
return '(' + arrayExpression + ")[" + index1Expression + ":" + index2Expression + "]";
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSelect.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
22,6 → 22,7
import org.openconcerto.utils.cc.ITransformer;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
47,18 → 48,6
public static final ArchiveMode ARCHIVED = ArchiveMode.ARCHIVED;
public static final ArchiveMode BOTH = ArchiveMode.BOTH;
 
/**
* Quote %-escaped parameters. %% : %, %s : quoteString, %i : quote, %f : quote(getFullName()),
* %n : quote(getName()).
*
* @param pattern a string with %, eg "SELECT * FROM %n where %f like '%%a%%'".
* @param params the parameters, eg [ /TENSION/, |TENSION.LABEL| ].
* @return pattern with % replaced, eg SELECT * FROM "TENSION" where "TENSION.LABEL" like '%a%'.
*/
public static final String quote(final String pattern, final Object... params) {
return SQLBase.quoteStd(pattern, params);
}
 
// [String], eg : [SITE.ID_SITE, AVG(AGE)]
private final List<String> select;
// names of columns (explicit aliases and field names), e.g. [ID_SITE, null]
511,6 → 500,10
return this.addSelect(f, null);
}
 
public final SQLSelect addAllSelect(final FieldRef... s) {
return this.addAllSelect(Arrays.asList(s));
}
 
/**
* Permet d'ajouter plusieurs champs.
*
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowAccessor.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
14,6 → 14,7
package org.openconcerto.sql.model;
 
import org.openconcerto.sql.Log;
import org.openconcerto.sql.model.SQLTable.VirtualFields;
import org.openconcerto.sql.model.graph.DatabaseGraph;
import org.openconcerto.sql.model.graph.Link;
import org.openconcerto.sql.model.graph.Link.Direction;
26,6 → 27,7
import java.math.BigDecimal;
import java.sql.Clob;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
196,6 → 198,25
public abstract Number getIDNumber();
 
/**
* Get the row reference.
*
* @return the row reference, <code>null</code> if some fields of the
* {@link SQLTable#getPrimaryKeyFields() primary key} are missing or <code>null</code>.
*/
public final RowRef getRowRef() {
final List<String> pkFields = this.getTable().getPKsNames();
final List<Object> pk = new ArrayList<>(pkFields.size());
for (final String f : pkFields) {
final Object o = this.getObject(f);
// Don't need contains() because a PK cannot contain NULL in the DB.
if (o == null)
return null;
pk.add(o);
}
return new RowRef(this.getTable(), Collections.unmodifiableList(pk), true);
}
 
/**
* Whether this row is the undefined row. Return <code>false</code> if both the
* {@link #getIDNumber() ID} and {@link SQLTable#getUndefinedIDNumber()} are <code>null</code>
* since no row can have <code>null</code> primary key in the database. IOW when
231,21 → 252,69
return ((Number) archiveVal).intValue() > 0;
}
 
public abstract SQLRowAccessor toImmutable();
 
public abstract boolean isFrozen();
 
/**
* Creates an SQLRow from these values, without any DB access.
*
* @return an SQLRow with the same values as this.
*/
public abstract SQLRow asRow();
public final SQLRow asRow() {
return this.asRow(null);
}
 
public abstract SQLRow asRow(final Boolean immutable);
 
/**
* Return an immutable SQLRow with only the passed fields retained. I.e. the returned instance
* can consume less memory.
*
* @param fields which fields to retain.
* @return a {@link #isFrozen() frozen} SQLRow.
*/
public final SQLRow trimmedRow(final VirtualFields fields) {
final Set<String> fieldsNames = this.getTable().getFieldsNames(fields);
if (this instanceof SQLRow && this.isFrozen() && fieldsNames.containsAll(this.getFields()))
return (SQLRow) this;
return SQLRow.trim(this, SQLRowAccessor::getValues, fieldsNames);
}
 
public final SQLRow fetchNewRow() {
return this.fetchNewRow(true);
}
 
/**
* Return a new instance with up-to-date values.
*
* @param useCache <code>true</code> to use the {@link SQLDataSource#isCacheEnabled() cache}.
* @return a new instance.
*/
public final SQLRow fetchNewRow(final boolean useCache) {
return new SQLRow(this.getTable(), this.getID()).fetchValues(useCache);
}
 
/**
* Creates an SQLRowValues from these values, without any DB access.
*
* @return an SQLRowValues with the same values as this.
*/
public abstract SQLRowValues asRowValues();
public final SQLRowValues asRowValues() {
return this.asRowValues(null);
}
 
/**
* Creates an SQLRowValues from these values, without any DB access.
*
* @param immutable <code>true</code> if the result must be
* {@link SQLRowValuesCluster#isFrozen() frozen}, <code>false</code> if it must not,
* <code>null</code> if the caller doesn't care and just wants the fastest result.
* @return an SQLRowValues with the same values as this.
*/
public abstract SQLRowValues asRowValues(final Boolean immutable);
 
/**
* Creates an SQLRowValues with just this ID, and no other values.
*
* @return an empty SQLRowValues.
267,6 → 336,8
 
public abstract Object getObject(String fieldName);
 
public abstract Object getObjectNoCheck(String fieldName);
 
/**
* Return the value for the passed field only if already present in this instance.
*
/trunk/OpenConcerto/src/org/openconcerto/sql/model/DBRoot.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
27,6 → 27,7
import org.openconcerto.sql.utils.SQLUtils;
import org.openconcerto.sql.utils.SQL_URL;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.Tuple2.List2;
 
import java.net.URISyntaxException;
import java.sql.SQLException;
335,7 → 336,7
return this.equalsDesc(o, null);
}
 
public final String equalsDesc(final DBRoot o, final SQLSystem otherSystem) {
public final String equalsDesc(final DBRoot o, final SQLSyntax otherSystem) {
if (this == o)
return null;
if (null == o)
364,8 → 365,12
* jdbc:h2:file:/a/b/c).
*/
public final SQL_URL getURL() {
final String hostname = this.getServer().getHostname();
if (hostname == null)
// check that our name doesn't contain a path, otherwise we would lose it
// e.g. dbserv:8084/~/sample
final List2<String> hostAndPath = this.getServer().getHostnameAndPath();
final String hostname = hostAndPath.get0();
// TODO return JDBCUrl
if (hostname == null || hostAndPath.get1() != null)
return null;
final SQLSystem system = this.getServer().getSQLSystem();
String url = system.name().toLowerCase() + "://" + this.getDBSystemRoot().getDataSource().getUsername() + "@" + hostname + "/";
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLFieldRowProcessor.java
New file
0,0 → 1,131
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.sql.model;
 
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
 
import org.apache.commons.dbutils.RowProcessor;
 
import net.jcip.annotations.Immutable;
 
@Immutable
public final class SQLFieldRowProcessor implements RowProcessor {
 
private final List<String> names;
private final int rsOffset;
private Boolean uniqueNames;
 
public SQLFieldRowProcessor(final SQLTable table, final List<String> fields) {
this(0, fields, table::getField);
}
 
public SQLFieldRowProcessor(final List<? extends FieldRef> fields) {
this(0, fields);
}
 
public SQLFieldRowProcessor(final int rsOffset, final List<? extends FieldRef> fields) {
this(rsOffset, fields, FieldRef::getField);
}
 
private <F> SQLFieldRowProcessor(final int rsOffset, final List<? extends F> fields, final Function<? super F, SQLField> func) {
final List<String> names = new ArrayList<>(fields.size());
for (final F ref : fields) {
if (ref == null) {
names.add(null);
} else {
final SQLField f = func.apply(ref);
names.add(f.getName());
}
}
this.names = Collections.unmodifiableList(names);
this.rsOffset = rsOffset;
this.uniqueNames = null;
}
 
public final List<String> getFieldNames() {
return this.names;
}
 
public final boolean areNamesUnique() {
if (this.uniqueNames == null) {
this.uniqueNames = new HashSet<>(this.names).size() == this.names.size();
}
return this.uniqueNames.booleanValue();
}
 
public final Object getObject(final ResultSet rs, final int rsIndex) throws SQLException {
return rs.getObject(rsIndex + this.rsOffset);
}
 
@Override
public Object[] toArray(ResultSet rs) throws SQLException {
final int count = this.names.size();
final Object[] result = new Object[count];
for (int i = 0; i < count; i++) {
result[i] = getObject(rs, i + 1);
}
return result;
}
 
@SuppressWarnings("rawtypes")
@Override
public Object toBean(ResultSet rs, Class type) throws SQLException {
throw new UnsupportedOperationException();
}
 
@SuppressWarnings("rawtypes")
@Override
public List toBeanList(ResultSet rs, Class type) throws SQLException {
throw new UnsupportedOperationException();
}
 
@Override
public Map<String, Object> toMap(ResultSet rs) throws SQLException {
final int count = this.names.size();
final Map<String, Object> result = new HashMap<>();
for (int i = 0; i < count; i++) {
final String name = this.names.get(i);
if (name != null) {
result.put(name, getObject(rs, i + 1));
}
}
return result;
}
 
@Override
public int hashCode() {
return Objects.hash(this.names, this.rsOffset);
}
 
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final SQLFieldRowProcessor other = (SQLFieldRowProcessor) obj;
return this.rsOffset == other.rsOffset && this.names.equals(other.names);
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/UndefinedRowValuesCache.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
57,6 → 57,10
return rv;
}
 
public void setDefaultRowValues(final SQLRowValues rowVals) {
this.map.put(rowVals.getTable(), rowVals);
}
 
public void preload(List<SQLTable> tablesToCache) {
if (tablesToCache.size() <= 0) {
throw new IllegalArgumentException("Empty list");
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLField.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
488,8 → 488,8
 
// compareDefault useful when fields' default are functions containing the name of the table (eg
// serial)
public String equalsDesc(SQLField o, SQLSystem otherSystem, boolean compareDefault) {
final Map<Properties, String> res = getDiffMap(o, otherSystem, compareDefault);
public String equalsDesc(SQLField o, SQLSyntax otherSyntax, boolean compareDefault) {
final Map<Properties, String> res = getDiffMap(o, otherSyntax, compareDefault);
if (res.size() == 0)
return null;
else
504,7 → 504,7
* @param compareDefault <code>true</code> if defaults should be compared.
* @return a map containing properties that differs and their values.
*/
public synchronized Map<Properties, String> getDiffMap(SQLField o, SQLSystem otherSystem, boolean compareDefault) {
public synchronized Map<Properties, String> getDiffMap(SQLField o, SQLSyntax otherSystem, boolean compareDefault) {
if (o == null)
return Collections.singletonMap(null, "other field is null");
final Map<Properties, String> res = new HashMap<Properties, String>();
/trunk/OpenConcerto/src/org/openconcerto/sql/model/Where.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
15,7 → 15,6
 
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.StringUtils;
import org.openconcerto.utils.cc.ITransformer;
 
import java.util.ArrayList;
import java.util.Arrays;
24,9 → 23,9
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.stream.Collectors;
 
import org.apache.commons.collections.functors.InstanceofPredicate;
 
import net.jcip.annotations.Immutable;
 
/**
127,6 → 126,25
return subqueries(ref, false, subQueries);
}
 
static public Where inValues(final FieldRef ref, final Collection<?> values) {
return compareValues(ref, RowComparison.IN, values);
}
 
static public Where notInValues(final FieldRef ref, final Collection<?> values) {
return compareValues(ref, RowComparison.NOT_IN, values);
}
 
static public Where compareValues(final FieldRef ref, final RowComparison cmp, final Collection<?> values) {
return compareValues(ref, cmp, NullValue.IS_FORBIDDEN, values);
}
 
static public Where compareValues(final FieldRef ref, final RowComparison cmp, final NullValue nullMode, final Collection<?> values) {
if (values.isEmpty()) {
return cmp == RowComparison.IN ? FALSE : TRUE;
}
return createRaw(getCompareValuesClause(ref.getFieldRef(), cmp, nullMode, values, ref.getField().getType()), ref);
}
 
/**
* Create a Where for a field value contained or not contained in sub-queries.
*
153,7 → 171,8
/**
* To create complex Where not possible with constructors.
*
* @param pattern a pattern to be passed to {@link SQLSelect#quote(String, Object...)}, eg
* @param s the syntax to use.
* @param pattern a pattern to be passed to {@link SQLBase#quoteStd(String, Object...)}, eg
* "EXTRACT(YEAR FROM %n) = 3007".
* @param params the params to be passed to <code>quote()</code>, eg [|MISSION.DATE_DBT|].
* @return a new Where with the result from <code>quote()</code> as its clause, and all
160,10 → 179,15
* <code>FieldRef</code> in params as its fields, eg {EXTRACT(YEAR FROM "DATE_DBT") =
* 3007 , |MISSION.DATE_DBT|}.
*/
@SuppressWarnings("unchecked")
static public Where quote(final String pattern, final Object... params) {
return new Where(SQLSelect.quote(pattern, params), org.apache.commons.collections.CollectionUtils.select(Arrays.asList(params), new InstanceofPredicate(FieldRef.class)));
static public Where quote(final SQLSyntax s, final String pattern, final Object... params) {
final List<FieldRef> fields = new ArrayList<>();
for (final Object param : params) {
if (param instanceof FieldRef) {
fields.add((FieldRef) param);
}
}
return new Where(SQLBase.quote(s, pattern, params), fields);
}
 
static private final String normalizeOperator(final String op) {
String res = op.trim();
172,19 → 196,70
return res;
}
 
static private final String comparison(final FieldRef ref, final String op, final String y) {
static public final String comparison(final FieldRef ref, final String op, final String y) {
return comparison(null, ref.getField(), ref.getFieldRef(), op, y);
}
 
static public final String comparison(final SQLSyntax s, final String x, final String op, final String y) {
return comparison(Objects.requireNonNull(s, "Missing syntax"), null, x, op, y);
}
 
static private final String comparison(SQLSyntax s, final DBStructureItem<?> syntaxSupplier, final String x, final String op, final String y) {
Objects.requireNonNull(op, "Missing operator");
if (op == NULL_IS_DATA_EQ || op == NULL_IS_DATA_NEQ) {
return ref.getField().getDBSystemRoot().getSyntax().getNullIsDataComparison(ref.getFieldRef(), op == NULL_IS_DATA_EQ, y);
if (s == null)
s = syntaxSupplier.getDBSystemRoot().getSyntax();
return s.getNullIsDataComparison(x, op == NULL_IS_DATA_EQ, y);
} else {
return ref.getFieldRef() + " " + op + " " + y;
return x + ' ' + op + ' ' + y;
}
}
 
static private final String getInClause(final FieldRef field1, final boolean in, final String inParens) {
final String op = in ? " in (" : " not in (";
return field1.getFieldRef() + op + inParens + ")";
return getInClause(field1.getFieldRef(), in ? RowComparison.IN : RowComparison.NOT_IN, inParens);
}
 
static private final String getInClause(final String expr, final RowComparison cmp, final String inParens) {
final String op = cmp == RowComparison.IN ? " in (" : " not in (";
return expr + op + inParens + ')';
}
 
static public enum RowComparison {
IN, NOT_IN
}
 
static public enum NullValue {
IS_DATA, IS_FORBIDDEN, IS_UNKNOWN
}
 
static public final String getCompareValuesClause(final String expr, final RowComparison cmp, final Collection<?> values, final SQLType type) {
return getCompareValuesClause(expr, cmp, NullValue.IS_FORBIDDEN, values, type);
}
 
static public final String getCompareValuesClause(final String expr, final RowComparison cmp, final NullValue nullMode, Collection<?> values, final SQLType type) {
final boolean addNull;
if (nullMode != NullValue.IS_UNKNOWN && values.contains(null)) {
if (nullMode == NullValue.IS_FORBIDDEN)
throw new IllegalArgumentException("Values contains a null value : " + values);
assert nullMode == NullValue.IS_DATA;
addNull = true;
} else {
addNull = false;
}
if (addNull) {
values = values.stream().filter((i) -> i != null).collect(Collectors.toList());
}
String res = getInClause(expr, cmp, CollectionUtils.join(values, ",", (input) -> type.toString(input)));
if (addNull) {
if (cmp == RowComparison.IN) {
res = expr + " is null or " + res;
} else {
res = expr + " is not null and " + res;
}
}
return res;
}
 
private final List<FieldRef> fields;
private final String clause;
 
226,6 → 301,7
*
* @param field1 le champs à tester.
* @param values les valeurs.
* @deprecated use {@link #inValues(FieldRef, Collection)}
*/
public Where(final FieldRef field1, final Collection<?> values) {
this(field1, true, values);
237,6 → 313,8
* @param field1 le champs à tester.
* @param in <code>true</code> for "in", <code>false</code> for "not in".
* @param values les valeurs.
* @deprecated use {@link #inValues(FieldRef, Collection)} or
* {@link #notInValues(FieldRef, Collection)}
*/
public Where(final FieldRef field1, final boolean in, final Collection<?> values) {
if (values.isEmpty()) {
244,14 → 322,9
this.clause = in ? FALSE.getClause() : TRUE.getClause();
} else {
this.fields = Collections.singletonList(field1);
this.clause = getInClause(field1, in, CollectionUtils.join(values, ",", new ITransformer<Object, String>() {
@Override
public String transformChecked(final Object input) {
return field1.getField().getType().toString(input);
this.clause = getCompareValuesClause(field1.getFieldRef(), in ? RowComparison.IN : RowComparison.NOT_IN, values, field1.getField().getType());
}
}));
}
}
 
public Where(final FieldRef field1, final boolean in, final SQLSelect subQuery) {
this.fields = Collections.singletonList(field1);
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLServer.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
20,6 → 20,8
import org.openconcerto.sql.model.graph.TablesMap;
import org.openconcerto.sql.utils.SQL_URL;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.NetUtils;
import org.openconcerto.utils.Tuple2.List2;
import org.openconcerto.utils.cc.CopyOnWriteMap;
import org.openconcerto.utils.cc.IClosure;
import org.openconcerto.utils.cc.ITransformer;
284,6 → 286,7
 
// fire once all the bases are loaded so that the graph is coherent
return this.getDBSystemRoot().getGraph().atomicRefresh(new Callable<Map<String, TablesMap>>() {
 
@Override
public Map<String, TablesMap> call() throws Exception {
final Map<String, TablesMap> res = new HashMap<String, TablesMap>();
573,7 → 576,41
return DBFileCache.create(this);
}
 
/**
* The host name of this server.
*
* @return the host name, <code>null</code> if not accessed through network.
*/
public final String getHostname() {
return this.getSQLSystem().getHostname(this.getName());
return this.getHostnameAndPath().get0();
}
 
public final List2<String> getHostnameAndPath() {
return this.getSQLSystem().getHostnameAndPath(this.getName());
}
 
public final boolean isPermanent() {
return this.getSQLSystem().isPermanent(this.getName());
}
 
/**
* Whether this server runs on the local machine.
*
* @return <code>true</code> if the JVM runs on the same machine than <code>this</code>.
*/
protected final boolean isLocalhost() {
// this method cannot be in SQLSyntax since it is used in the constructor of SQLDataSource
// (which is needed by SQLSyntax.create())
 
final String host = this.getHostname();
if (host == null)
return true;
final int colonIndex = host.indexOf(':');
final String hostWOPort = colonIndex < 0 ? host : host.substring(0, colonIndex);
return NetUtils.isSelfAddr(hostWOPort);
}
 
public final boolean isPersistent() {
return this.getSQLSystem() != SQLSystem.H2 || !this.getName().equals(SQLSystem.H2_IN_MEMORY);
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRow.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
23,6 → 23,7
import org.openconcerto.sql.model.graph.Link;
import org.openconcerto.sql.model.graph.Link.Direction;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.ListMap;
import org.openconcerto.utils.SetMap;
import org.openconcerto.utils.Tuple2.List2;
42,6 → 43,7
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.logging.Level;
 
import org.apache.commons.dbutils.ResultSetHandler;
102,15 → 104,17
* @see SQLRow#SQLRow(SQLTable, Map)
* @deprecated use {@link SQLRowListRSH} or {@link SQLRowValuesListFetcher} instead or if you
* must use a {@link ResultSet} call
* {@link #createFromRS(SQLTable, ResultSet, ResultSetMetaData, boolean)} thus
* avoiding the potentially costly {@link ResultSet#getMetaData()}
* {@link #createListFromRS(SQLTable, ResultSet, boolean)} thus avoiding the
* potentially costly {@link ResultSet#getMetaData()}
*/
public static final SQLRow createFromRS(SQLTable table, ResultSet rs, final boolean onlyTable) throws SQLException {
return createFromRS(table, rs, rs.getMetaData(), onlyTable);
}
 
// see createListFromRS()
@Deprecated
public static final SQLRow createFromRS(SQLTable table, ResultSet rs, final ResultSetMetaData rsmd, final boolean onlyTable) throws SQLException {
return createFromRS(table, rs, getFieldNames(table, rsmd, onlyTable));
return createFromRS(table, rs, new SQLFieldRowProcessor(table, getFieldNames(table, rsmd, onlyTable)), true);
}
 
private static final List<String> getFieldNames(SQLTable table, final ResultSetMetaData rsmd, final boolean tableOnly) throws SQLException {
130,18 → 134,10
return names;
}
 
// MAYBE create an opaque class holding names so that we can make this method, getFieldNames()
// and createListFromRS() public
static final SQLRow createFromRS(SQLTable table, ResultSet rs, final List<String> names) throws SQLException {
final int indexCount = names.size();
// ATTN doesn't check that names are fields of table
public static final SQLRow createFromRS(SQLTable table, ResultSet rs, final SQLFieldRowProcessor rowProc, final boolean immutable) throws SQLException {
final Map<String, Object> m = rowProc.toMap(rs);
 
final Map<String, Object> m = new HashMap<String, Object>(indexCount);
for (int i = 0; i < indexCount; i++) {
final String colName = names.get(i);
if (colName != null)
m.put(colName, rs.getObject(i + 1));
}
 
final Number id = getID(m, table, true);
// e.g. LEFT JOIN : missing values are null
if (id == null)
148,7 → 144,10
return null;
 
// pass already found ID
return new SQLRow(table, id, m);
final SQLRow res = new SQLRow(table, id, Collections.unmodifiableMap(m));
if (immutable)
res.freeze();
return res;
}
 
/**
162,7 → 161,7
* @throws SQLException if an error occurs while reading <code>rs</code>.
*/
public static final List<SQLRow> createListFromRS(SQLTable table, ResultSet rs, final boolean tableOnly) throws SQLException {
return createListFromRS(table, rs, getFieldNames(table, rs.getMetaData(), tableOnly));
return createListFromRS(table, rs, getFieldNames(table, rs.getMetaData(), tableOnly), true);
}
 
/**
172,17 → 171,19
* @param rs the result set.
* @param names the name of the field for each column, nulls are ignored, e.g. ["DESIGNATION",
* null, "ID"].
* @param immutable <code>true</code> if the result should be immutable.
* @return the data of the result set as SQLRows.
* @throws SQLException if an error occurs while reading <code>rs</code>.
*/
static final List<SQLRow> createListFromRS(SQLTable table, ResultSet rs, final List<String> names) throws SQLException {
static final List<SQLRow> createListFromRS(SQLTable table, ResultSet rs, final List<String> names, final boolean immutable) throws SQLException {
final List<SQLRow> res = new ArrayList<SQLRow>();
final SQLFieldRowProcessor rowProc = new SQLFieldRowProcessor(table, names);
while (rs.next()) {
final SQLRow row = createFromRS(table, rs, names);
final SQLRow row = createFromRS(table, rs, rowProc, immutable);
if (row != null)
res.add(row);
}
return res;
return immutable ? Collections.unmodifiableList(res) : res;
}
 
static final SQLRow createFromSelect(final SQLTable t, final VirtualFields vfs, final int id, final LockStrength l) {
190,8 → 191,9
sel.setLockStrength(l);
sel.setWhere(new Where(t.getKey(), "=", id));
@SuppressWarnings("unchecked")
final Map<String, ?> map = (Map<String, ?>) t.getDBSystemRoot().getDataSource().execute(sel.asString(), new IResultSetHandler(SQLDataSource.MAP_HANDLER, l.equals(LockStrength.NONE)));
return new SQLRow(t, id, map);
final Map<String, Object> map = (Map<String, Object>) t.getDBSystemRoot().getDataSource().execute(sel.asString(),
new IResultSetHandler(SQLDataSource.MAP_HANDLER, l.equals(LockStrength.NONE)));
return new SQLRow(t, id, map == null ? null : Collections.unmodifiableMap(map));
}
 
/**
199,17 → 201,26
*
* @param t the table.
* @param id the ID.
* @return a new {@link #exists() existing} {@link #isFilled() filled} {@link #getFields()
* empty} row.
* @return a new {@link #exists() existing} {@link #isFilled() filled} {@link #isFrozen()
* frozen} {@link #getFields() empty} row.
*/
static final SQLRow createEmpty(final SQLTable t, final int id) {
return new SQLRow(t, id, Collections.<String, Object> emptyMap());
public static final SQLRow createEmpty(final SQLTable t, final int id) {
final SQLRow res = new SQLRow(t, id, Collections.<String, Object> emptyMap());
res.freeze();
return res;
}
 
public static final <R extends SQLRowAccessor, A> SQLRow trim(final R r, final BiFunction<? super R, ? super A, ? extends Map<String, Object>> getVals, final A arg) {
final SQLRow res = new SQLRow(r.getTable(), null, CollectionUtils.toImmutableMap(getVals.apply(r, arg)));
res.freeze();
return res;
}
 
private final int ID;
private final Number idNumber;
private Map<String, Object> values;
private boolean fetched;
private boolean frozen = false;
 
private SQLRow(SQLTable table, Number id) {
super(table);
239,14 → 250,14
* @throws IllegalArgumentException si values ne contient pas la clef de la table.
*/
public SQLRow(SQLTable table, Map<String, ?> values) {
this(table, null, values);
this(table, null, values == null ? null : CollectionUtils.toImmutableMap(values));
}
 
// allow to call getID() only once
private SQLRow(SQLTable table, final Number id, Map<String, ?> values) {
// Private since it doesn't make sure the map is immutable.
private SQLRow(SQLTable table, final Number id, Map<String, Object> values) {
this(table, id == null ? getID(values, table, false) : id);
// faire une copie, sinon backdoor pour changer les valeurs sans qu'on s'en aperçoive
this.setValues(values == null ? null : new HashMap<String, Object>(values));
this.setValues(values);
}
 
// return ID, must always be present but may be null if <code>nullAllowed</code>
265,7 → 276,50
}
}
 
public final SQLRow copy(final boolean freeze) {
final SQLRow res = new SQLRow(this.getTable(), this.getIDNumber());
if (this.isFilled())
// safe to share map, since it is immutable
res.setValues(this.getValues());
assert res.isFilled() == this.isFilled();
if (freeze)
res.freeze();
assert res.isFrozen() == freeze;
return res;
}
 
@Override
public final boolean isFrozen() {
return this.frozen;
}
 
private void checkFrozen() {
if (this.isFrozen())
throw new IllegalStateException("SQLRow is not modifiable");
}
 
/**
* Freeze this instance so that no modification can be made. Once this method returns, this
* instance can be safely published (e.g. stored into a field that is properly guarded by a
* lock) to other threads without further synchronizations.
*
* @return <code>true</code> if this call changed the frozen status.
*/
public final boolean freeze() {
if (this.frozen)
return false;
this.frozen = true;
return true;
}
 
@Override
public final SQLRow toImmutable() {
if (this.isFrozen())
return this;
return this.copy(true);
}
 
/**
* Whether this contains values or just the {@link #getIDNumber() id}. NOTE that
* {@link #getObject(String)} (and thus any other methods that call it) will access the DB if
* the requested field is {@link #getFields() missing} even if this returns <code>true</code>.
313,11 → 367,13
* @return a new instance.
*/
public final SQLRow fetchNew(final boolean useCache) {
return new SQLRow(this.getTable(), this.getIDNumber()).fetchValues(useCache);
return this.fetchNewRow(useCache);
}
 
@SuppressWarnings("unchecked")
SQLRow fetchValues(final boolean readCache, final boolean writeCache) {
// not necessary here, but allows to fail early and avoid a request
// implique trop de regression dans la branche : checkFrozen();
final IResultSetHandler handler = new IResultSetHandler(SQLDataSource.MAP_HANDLER, readCache, writeCache) {
@Override
public Set<SQLRow> getCacheModifiers() {
324,12 → 380,15
return Collections.singleton(SQLRow.this);
}
};
this.setValues((Map<String, Object>) this.getTable().getBase().getDataSource().execute(this.getQuery(), handler, false));
final Map<String, Object> values = (Map<String, Object>) this.getTable().getBase().getDataSource().execute(this.getQuery(), handler, false);
this.setValues(values == null ? null : Collections.unmodifiableMap(values));
return this;
}
 
// attention ne vérifie pas que tous les champs soient présents
// Attention ne vérifie pas que tous les champs soient présents.
// Private since it doesn't make sure the map is immutable.
private final void setValues(Map<String, Object> values) {
// implique trop de regression dans la branche : checkFrozen();
this.values = values;
if (!this.fetched)
this.fetched = true;
342,13 → 401,13
*/
@Override
public Set<String> getFields() {
return this.fetched ? Collections.unmodifiableSet(this.getValues().keySet()) : Collections.<String> emptySet();
return this.isFilled() ? this.getValues().keySet() : Collections.<String> emptySet();
}
 
// avoid Collections.unmodifiableSet() allocation
@Override
public boolean contains(String fieldName) {
return this.fetched ? this.getValues().containsKey(fieldName) : false;
return this.isFilled() ? this.getValues().containsKey(fieldName) : false;
}
 
private String getQuery() {
411,6 → 470,11
return this.getValues().get(field);
}
 
@Override
public final Object getObjectNoCheck(String field) {
return this.values.get(field);
}
 
/**
* Fetch from the DB this row and the next/previous one. ATTN the rows are locked
* {@link LockStrength#UPDATE for update}, but if this method is not called from within a
1040,7 → 1104,7
*/
@Override
public Map<String, Object> getAbsolutelyAll() {
return Collections.unmodifiableMap(this.getValues());
return this.getValues();
}
 
private static final VirtualFields ALL_VALUES_FIELDS = VirtualFields.ALL.difference(VirtualFields.KEYS, VirtualFields.ARCHIVE, VirtualFields.ORDER);
1093,13 → 1157,19
}
 
@Override
public SQLRow asRow() {
public SQLRow asRow(final Boolean immutable) {
if (immutable == null || this.isFrozen() == immutable)
return this;
else
return this.copy(immutable);
}
 
@Override
public final SQLRowValues asRowValues() {
return this.createUpdateRow();
public final SQLRowValues asRowValues(final Boolean immutable) {
final SQLRowValues res = this.createUpdateRow();
if (Boolean.TRUE.equals(immutable))
res.getGraph().freeze();
return res;
}
 
/**
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLInjector.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
14,6 → 14,7
package org.openconcerto.sql.model;
 
import org.openconcerto.sql.Configuration;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.graph.SQLKey;
import org.openconcerto.sql.model.graph.TablesMap;
import org.openconcerto.sql.preferences.SQLPreferences;
224,7 → 225,13
SQLRowValues rowValsHeader = new SQLRowValues(UndefinedRowValuesCache.getInstance().getDefaultRowValues(tableElementDestination));
// FIXME Style forcé en titre 1 via l'ID
rowValsHeader.put("ID_STYLE", 3);
String elementName = StringUtils.firstUp(Configuration.getInstance().getDirectory().getElement(getSource()).getName().getVariant(org.openconcerto.utils.i18n.Grammar.SINGULAR));
SQLElement element = Configuration.getInstance().getDirectory().getElement(getSource());
//Utiliser pour les transfert du module commande interne
if (element == null) {
element = Configuration.getInstance().getDirectory().getElement(getSource().getName());
}
 
String elementName = StringUtils.firstUp(element.getName().getVariant(org.openconcerto.utils.i18n.Grammar.SINGULAR));
rowValsHeader.put("NOM", elementName + "\n N° " + srcRow.getString("NUMERO") + " du " + dateFormat.format(srcRow.getDate("DATE").getTime()));
rowValsHeader.put(refField, rowVals);
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSyntaxH2.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
17,10 → 17,14
import org.openconcerto.sql.model.graph.TablesMap;
import org.openconcerto.sql.utils.ChangeTable.ClauseType;
import org.openconcerto.sql.utils.SQLUtils;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.ListMap;
import org.openconcerto.utils.NetUtils;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.utils.cc.Transformer;
import org.openconcerto.xml.XMLCodecUtils;
 
import java.beans.DefaultPersistenceDelegate;
import java.io.File;
import java.math.BigDecimal;
import java.sql.Blob;
28,6 → 32,7
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
35,7 → 40,7
 
import org.h2.constant.ErrorCode;
 
class SQLSyntaxH2 extends SQLSyntax {
public class SQLSyntaxH2 extends SQLSyntax {
 
static protected final IdentityHashMap<String, String> DATE_SPECS;
 
42,10 → 47,30
static {
DATE_SPECS = new IdentityHashMap<String, String>(DateProp.JAVA_DATE_SPECS_PURE);
DATE_SPECS.put(DateProp.MICROSECOND, "SSS000");
 
XMLCodecUtils.register(SQLSyntaxH2.class, new DefaultPersistenceDelegate(new String[] {}));
}
 
SQLSyntaxH2() {
final static SQLSyntax create(DBSystemRoot sysRoot) {
boolean standardArray = true;
if (sysRoot != null) {
try {
standardArray = Arrays.equals(new String[] { "foo" }, (Object[]) sysRoot.getDataSource().executeScalar("SELECT ARRAY['foo']"));
} catch (RuntimeException e) {
if (SQLUtils.findWithSQLState(e).getErrorCode() == ErrorCode.COLUMN_NOT_FOUND_1)
standardArray = false;
else
throw e;
}
}
return new SQLSyntaxH2(standardArray);
}
 
private final boolean standardArray;
 
public SQLSyntaxH2(final boolean standardArray) {
super(SQLSystem.H2, DATE_SPECS);
this.standardArray = standardArray;
this.typeNames.addAll(Boolean.class, "boolean", "bool", "bit");
this.typeNames.addAll(Integer.class, "integer", "int", "int4", "mediumint");
this.typeNames.addAll(Byte.class, "tinyint");
189,20 → 214,6
}
 
@Override
protected boolean isServerLocalhost(SQLServer s) {
return s.getName().startsWith("mem") || s.getName().startsWith("file") || NetUtils.isSelfAddr(getAddr(s));
}
 
private String getAddr(SQLServer s) {
if (s.getName().startsWith("tcp") || s.getName().startsWith("ssl")) {
final int startIndex = "tcp://".length();
final int endIndex = s.getName().indexOf('/', startIndex);
return s.getName().substring(startIndex, endIndex < 0 ? s.getName().length() : endIndex);
} else
return null;
}
 
@Override
public String getCreateSynonym(SQLTable t, SQLName newName) {
return null;
}
324,4 → 335,78
// otherwise, the second one will timeout while waiting for the table lock
return stateExn.getErrorCode() == ErrorCode.DEADLOCK_1 || stateExn.getErrorCode() == ErrorCode.LOCK_TIMEOUT_1;
}
 
@Override
public boolean isTableNotFoundException(Exception exn) {
final SQLException stateExn = SQLUtils.findWithSQLState(exn);
return stateExn.getErrorCode() == ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1 || stateExn.getErrorCode() == ErrorCode.SCHEMA_NOT_FOUND_1;
}
 
@Override
public String getSessionIDExpression() {
return "SESSION_ID()";
}
 
@Override
public String getSessionsQuery(final DBSystemRoot sysRoot, final boolean includeSelf) {
final String allRows = "SELECT \"ID\", \"STATEMENT\" as \"QUERY\", \"USER_NAME\" FROM INFORMATION_SCHEMA.SESSIONS";
if (includeSelf)
return allRows;
return allRows + " WHERE \"ID\" != " + this.getSessionIDExpression();
}
 
@Override
public String getVersionFunction() {
return "H2VERSION()";
}
 
@Override
public String getAllowConnectionsQuery(String sysRootName, final boolean allow) {
return "SET EXCLUSIVE " + (allow ? "0" : "1");
}
 
@Override
public boolean isConnectionDisallowedException(SQLException exn) {
return exn.getErrorCode() == ErrorCode.DATABASE_IS_IN_EXCLUSIVE_MODE;
}
 
@Override
public String getSQLArray(final List<String> sqlExpressions, final String type) {
// H2 cannot specify type in SQL, so cast each item
final ITransformer<? super String, ?> tf = type == null ? Transformer.nopTransformer() : (s) -> {
return this.cast(s, type);
};
final String items = CollectionUtils.join(sqlExpressions, ", ", tf);
if (this.standardArray) {
return "ARRAY[" + items + ']';
} else {
// For the comma, see http://h2database.com/html/grammar.html#array
return '(' + items + (sqlExpressions.size() == 1 ? ",)" : ")");
}
}
 
@Override
public String getSQLArrayContains(String arrayExpression, String itemExpression) {
return "ARRAY_CONTAINS(" + arrayExpression + ", " + itemExpression + ")";
}
 
@Override
public String getSQLArrayLength(final String arrayExpression) {
return "ARRAY_LENGTH(" + arrayExpression + ")";
}
 
@Override
public String getSQLArrayConcat(final String arrayExpression, final String array2Expression) {
return "array_cat(" + arrayExpression + ", " + array2Expression + ")";
}
 
@Override
public String getSQLArrayAppend(final String arrayExpression, final String itemExpression) {
return "array_append(" + arrayExpression + ", " + itemExpression + ")";
}
 
@Override
public String getSQLArraySlice(final String arrayExpression, final String index1Expression, final String index2Expression) {
return "ARRAY_SLICE(" + arrayExpression + ", " + index1Expression + ", " + index2Expression + ")";
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLType.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
269,7 → 269,7
return equals(obj, null);
}
 
public boolean equals(Object obj, SQLSystem otherSystem) {
public boolean equals(Object obj, SQLSyntax otherSyntax) {
if (obj instanceof SQLType) {
final SQLType o = (SQLType) obj;
final boolean javaTypeOK = this.getJavaType().equals(o.getJavaType());
292,8 → 292,8
if (this.getType() == Types.DATE || this.getJavaType() == Float.class || this.getJavaType() == Double.class) {
sizeOK = true;
} else {
if (otherSystem == null && o.getSyntax() != null)
otherSystem = o.getSyntax().getSystem();
if (otherSyntax == null)
otherSyntax = o.getSyntax();
final SQLSystem thisSystem = this.getSyntax() == null ? null : this.getSyntax().getSystem();
final boolean isTime = this.getType() == Types.TIME || this.getType() == Types.TIMESTAMP;
final boolean decDigitsOK;
302,7 → 302,7
decDigitsOK = true;
} else if (this.isNumeric() ||
// isTime() : if we don't know the system, play it safe and compare
thisSystem == null || otherSystem == null || thisSystem.isFractionalSecondsSupported() && otherSystem.isFractionalSecondsSupported()) {
thisSystem == null || otherSyntax == null || thisSystem.isFractionalSecondsSupported() && otherSyntax.getSystem().isFractionalSecondsSupported()) {
decDigitsOK = CompareUtils.equals(this.getDecimalDigits(), o.getDecimalDigits());
} else {
decDigitsOK = true;
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowValuesListFetcher.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/sql/model/RowRef.java
New file
0,0 → 1,106
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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.CollectionUtils;
import org.openconcerto.utils.convertor.NumberConvertor;
 
import java.util.Collections;
import java.util.List;
import java.util.Objects;
 
import net.jcip.annotations.Immutable;
 
/**
* Allow to store a reference to a row with less memory than {@link SQLRow}.
*
* @author sylvain
* @see SQLTable#createRowRef(Number)
* @see SQLRowAccessor#getRowRef()
*/
@Immutable
public final class RowRef {
 
// need to convert so that equals() work (because Number.equals() doesn't exist)
static private Number convert(final SQLTable table, final Number n) {
// avoid method calls if null
return n == null ? null : NumberConvertor.convertExact(n, table.getKey().getType().getJavaType().asSubclass(Number.class));
}
 
private final SQLTable table;
private final List<?> pk;
private final Number id;
 
RowRef(final SQLTable table, final List<?> pk) {
this(table, pk, false);
}
 
RowRef(final SQLTable table, final List<?> pk, final boolean safe) {
this(table, Objects.requireNonNull(pk), null, safe);
}
 
RowRef(final SQLTable table, final Number id) {
this(table, null, Objects.requireNonNull(id), false);
}
 
private RowRef(final SQLTable table, final List<?> pk, final Number id, final boolean safe) {
assert (pk == null) != (id == null);
if (id != null && !table.isRowable())
throw new IllegalStateException("Not rowable : " + table.getSQLName());
else if (!safe && pk != null && pk.size() != table.getPrimaryKeyFields().size())
throw new IllegalArgumentException("Wrong number of items");
this.table = Objects.requireNonNull(table);
if (id != null || table.isRowable()) {
this.id = convert(table, id != null ? id : (Number) pk.get(0));
this.pk = Collections.singletonList(this.id);
} else {
this.id = null;
this.pk = safe ? pk : CollectionUtils.toImmutableList(pk);
}
}
 
public final SQLTable getTable() {
return this.table;
}
 
public final List<?> getPK() {
return this.pk;
}
 
public final Number getID() {
return this.id;
}
 
@Override
public int hashCode() {
return Objects.hash(this.pk, this.table);
}
 
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final RowRef other = (RowRef) obj;
return this.table.equals(other.table) && this.pk.equals(other.pk);
}
 
@Override
public String toString() {
return this.getClass().getSimpleName() + " " + this.getTable() + " " + this.getPK();
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSyntaxMS.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
27,7 → 27,9
import org.openconcerto.utils.StringUtils;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.xml.XMLCodecUtils;
 
import java.beans.DefaultPersistenceDelegate;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
51,7 → 53,7
import java.util.Set;
import java.util.regex.Pattern;
 
class SQLSyntaxMS extends SQLSyntax {
public class SQLSyntaxMS extends SQLSyntax {
 
static private final IdentityHashMap<String, String> DATE_SPECS;
 
66,9 → 68,11
DATE_SPECS.put(DateProp.MINUTE, "mm");
DATE_SPECS.put(DateProp.SECOND, "ss");
DATE_SPECS.put(DateProp.MICROSECOND, "ffffff");
 
XMLCodecUtils.register(SQLSyntaxMS.class, new DefaultPersistenceDelegate(new String[] {}));
}
 
SQLSyntaxMS() {
public SQLSyntaxMS() {
super(SQLSystem.MSSQL, DATE_SPECS);
this.typeNames.addAll(Boolean.class, "bit");
// tinyint is unsigned
240,6 → 244,22
}
 
@Override
public boolean isTableNotFoundException(Exception exn) {
// TODO
throw new UnsupportedOperationException();
}
 
@Override
public String getSetLockTimeoutQuery(int millis) {
return "SET LOCK_TIMEOUT " + millis;
}
 
@Override
public String getShowLockTimeoutQuery() {
return "SELECT @@LOCK_TIMEOUT";
}
 
@Override
public Map<ClauseType, List<String>> getAlterField(SQLField f, Set<Properties> toAlter, String type, String defaultVal, Boolean nullable) {
final ListMap<ClauseType, String> res = new ListMap<ClauseType, String>();
if (toAlter.contains(Properties.TYPE) || toAlter.contains(Properties.NULLABLE)) {
615,4 → 635,27
public String quoteForTimestampFormat(String text) {
return StringUtils.doubleQuote(text);
}
 
@Override
public String getSessionIDExpression() {
return "@@SPID";
}
 
@Override
public String getSessionsQuery(final DBSystemRoot sysRoot, final boolean includeSelf) {
final String allRows = "SELECT ps.spid as \"ID\", cmd as \"QUERY\", users.name as \"USER_NAME\" FROM sys.sysprocesses ps\n"
//
+ "JOIN sys.sysdatabases db on db.dbid = ps.dbid\nJOIN sys.sysusers users on users.uid = ps.uid\\n"
//
+ " WHERE db.name=" + quoteString(sysRoot.getName());
if (includeSelf)
return allRows;
return allRows + " and ps.spid != " + this.getSessionIDExpression();
}
 
@Override
public String getVersionFunction() {
// if this doesn't work, try SERVERPROPERTY('ProductVersion')
return "@@VERSION";
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSyntax.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
27,7 → 27,6
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.CompareUtils;
import org.openconcerto.utils.ListMap;
import org.openconcerto.utils.NetUtils;
import org.openconcerto.utils.StringUtils;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.ITransformer;
37,6 → 36,7
import java.io.FileFilter;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
319,7 → 319,7
if (sys == SQLSystem.POSTGRESQL)
res = new SQLSyntaxPG();
else if (sys == SQLSystem.H2)
res = new SQLSyntaxH2();
res = SQLSyntaxH2.create(sysRoot);
else if (sys == SQLSystem.MYSQL)
res = SQLSyntaxMySQL.create(sysRoot);
else if (sys == SQLSystem.MSSQL)
571,6 → 571,35
public abstract boolean isDeadLockException(final SQLException exn);
 
/**
* Whether the cause of the passed exception is a table not being found.
*
* @param exn an exception.
* @return <code>true</code> if the cause is a table not being found.
*/
public abstract boolean isTableNotFoundException(final Exception exn);
 
/**
* How to set the amount of time any statement waits while attempting to acquire a lock.
* <p>
* NOTE : some systems only support seconds precision, in that case the amount will be
* {@link RoundingMode#UP rounded up} so that the statement waits at least the passed amount.
* </p>
* <p>
* <strong>Warning</strong> : some systems will wait 2 or 3 times the passed amount.
* </p>
*
* @param millis the number of milliseconds.
* @return the SQL query.
*/
public String getSetLockTimeoutQuery(final int millis) {
return "SET lock_timeout " + millis;
}
 
public String getShowLockTimeoutQuery() {
return "SELECT lock_timeout()";
}
 
/**
* Something to be appended to CREATE TABLE statements, like "ENGINE = InnoDB".
*
* @return a String that need to be appended to CREATE TABLE statements.
710,9 → 739,8
sqlType = type + "(" + size + ")";
} else {
Log.get().warning("Unbounded varchar for " + f.getSQLName());
// if (this.getSystem() == SQLSystem.MYSQL)
// throw new IllegalStateException("MySQL doesn't support unbounded varchar and
// might truncate data if reducing size of " + f.getSQLName());
if (this.getSystem() == SQLSystem.MYSQL)
throw new IllegalStateException("MySQL doesn't support unbounded varchar and might truncate data if reducing size of " + f.getSQLName());
// don't specify size
sqlType = type;
}
1030,7 → 1058,7
if (delete)
t.getBase().getDataSource().execute("DELETE FROM " + t.getSQLName().quote());
_loadData(f, t);
t.fireTableModified(SQLRow.NONEXISTANT_ID);
t.fireTableModified();
if (level != null)
Log.get().log(level, "done loading " + f);
}
1066,18 → 1094,8
 
protected abstract void _storeData(SQLTable t, File f) throws IOException;
 
/**
* Whether the passed server runs on this machine.
*
* @param s the server to test.
* @return <code>true</code> if this jvm runs on the same machine than <code>s</code>.
*/
protected boolean isServerLocalhost(SQLServer s) {
return NetUtils.isSelfAddr(s.getName());
}
 
protected final void checkServerLocalhost(DBStructureItem<?> t) {
if (!this.isServerLocalhost(t.getServer()))
if (!t.getServer().isLocalhost())
throw new IllegalArgumentException("the server of " + t + " is not this computer: " + t.getServer());
}
 
1534,4 → 1552,98
}
};
}
 
/**
* The expression that returns the current session.
*
* @return the expression to know the current session ID.
* @see #getSessionsQuery(DBSystemRoot, boolean)
*/
public abstract String getSessionIDExpression();
 
public final String getSessionsQuery(final DBSystemRoot sysRoot) {
return this.getSessionsQuery(sysRoot, true);
}
 
/**
* Return a query to list the sessions in the passed system root.
*
* @param sysRoot the system root.
* @param includeSelf <code>true</code> if the session executing the query should be returned.
* @return a query returning ID, QUERY and USER_NAME columns.
*/
public abstract String getSessionsQuery(final DBSystemRoot sysRoot, final boolean includeSelf);
 
/**
* Return a query to grant privileges on a table.
*
* @param privileges which privileges, <code>null</code> for ALL, e.g. "SELECT".
* @param tableName which table.
* @param role which role, <code>null</code> for everyone, e.g. 'joe'.
* @return the query.
*/
public String getGrantQuery(final List<String> privileges, final SQLName tableName, final String role) {
final String priv = privileges == null ? "ALL" : CollectionUtils.join(privileges, ", ");
return "GRANT " + priv + " ON " + tableName + " TO " + (role == null ? getAllUsersForGrant() : role);
}
 
protected String getAllUsersForGrant() {
return "PUBLIC";
}
 
public String getVersionFunction() {
return "version()";
}
 
/**
* Return a query to allow/disallow connections to the passed database. I.e. allow exclusive
* access to the database.
*
* @param sysRootName the database name.
* @param allow <code>true</code> to allow connections, <code>false</code> to disallow.
* @return the query, <code>null</code> if not supported.
*/
public String getAllowConnectionsQuery(String sysRootName, final boolean allow) {
return null;
}
 
public boolean isConnectionDisallowedException(final SQLException exn) {
throw new UnsupportedOperationException();
}
 
public final String getSQLArray(final List<String> sqlExpressions) {
return this.getSQLArray(sqlExpressions, null);
}
 
/**
* Return an SQL expression of an array.
*
* @param sqlExpressions the items.
* @param type the component type, can be <code>null</code> to infer it from items (i.e. when
* <code>sqlExpressions</code> isn't empty).
* @return an SQL expression.
*/
public String getSQLArray(final List<String> sqlExpressions, final String type) {
throw new UnsupportedOperationException();
}
 
public String getSQLArrayContains(final String arrayExpression, final String itemExpression) {
throw new UnsupportedOperationException();
}
 
public String getSQLArrayLength(final String arrayExpression) {
throw new UnsupportedOperationException();
}
 
public String getSQLArrayConcat(final String arrayExpression, final String array2Expression) {
throw new UnsupportedOperationException();
}
 
public String getSQLArrayAppend(final String arrayExpression, final String itemExpression) {
throw new UnsupportedOperationException();
}
 
public String getSQLArraySlice(final String arrayExpression, final String index1Expression, final String index2Expression) {
throw new UnsupportedOperationException();
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowValues.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
36,6 → 36,7
import org.openconcerto.utils.RecursionType;
import org.openconcerto.utils.SetMap;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.Value;
import org.openconcerto.utils.cc.IClosure;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.utils.cc.IdentitySet;
109,6 → 110,10
COPY_ROW
}
 
public static enum SQLDefaultCopyMode {
COPY, REMOVE, PARSE_ELSE_REMOVE, PARSE_ELSE_ERROR;
}
 
static public enum CreateMode {
/**
* Never create rows.
150,6 → 155,24
}
};
 
public static final class SQLExpression {
private final String sql;
 
public SQLExpression(String sql) {
super();
this.sql = sql;
}
 
public final String getSQL() {
return this.sql;
}
 
@Override
public String toString() {
return this.getClass().getSimpleName() + " " + this.sql;
}
}
 
static public enum ValidityCheck {
/**
* The check is never performed.
341,6 → 364,7
*
* @return this if already frozen, otherwise a frozen copy of this.
*/
@Override
public final SQLRowValues toImmutable() {
if (this.isFrozen())
return this;
461,14 → 485,20
* @return the foreign row, eg FAMILLE[1].
*/
public final SQLRowValues grow(String fk) {
// growUndefined to make sure we can cast (otherwise if fk is undefined
// getForeign() returns a row)
return (SQLRowValues) this.grow(fk, true);
}
 
public final SQLRowAccessor grow(String fk, final boolean growUndefined) {
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);
this.grow(vals, true, growUndefined);
}
return (SQLRowValues) this.getForeign(fk);
return this.getForeign(fk);
}
 
public final SQLRowValues grow(SQLRowValues graph) {
681,17 → 711,48
}
 
@Override
public final Object getObjectNoCheck(String fieldName) {
return this.values.get(fieldName);
}
public final Value<Object> getNonDefaultObject(String fieldName) {
return getNonDefaultObject(fieldName, Object.class);
}
 
/**
* Never return {@link #SQL_DEFAULT}.
*
* @param fieldName the name of the field.
* @param clazz the type of value expected.
* @return {@link Value#getNone()} if the field is DEFAULT and the
* {@link SQLField#getParsedDefaultValue() default value} cannot be parsed, otherwise
* either the non DEFAULT field value or the parsed DEFAULT.
*/
public final <T> Value<T> getNonDefaultObject(String fieldName, Class<T> clazz) {
final Object res = this.getObject(fieldName);
if (res == SQL_DEFAULT) {
return this.getTable().getField(fieldName).getParsedDefaultValue().cast(clazz);
} else {
return Value.getSome(clazz.cast(res));
}
}
 
@Override
public Map<String, Object> getAbsolutelyAll() {
return getAllValues(ForeignCopyMode.COPY_ROW);
}
 
protected final Map<String, Object> getAllValues(ForeignCopyMode copyForeigns) {
return this.getAllValues(copyForeigns, false);
return this.getAllValues(copyForeigns, SQLDefaultCopyMode.COPY);
}
 
private final Map<String, Object> getAllValues(ForeignCopyMode copyForeigns, final boolean copy) {
public final Map<String, Object> getAllValues(ForeignCopyMode copyForeigns, SQLDefaultCopyMode copyDefaults) {
return this.getAllValues(copyForeigns, copyDefaults, false);
}
 
private final Map<String, Object> getAllValues(ForeignCopyMode copyForeigns, SQLDefaultCopyMode copyDefaults, final boolean copy) {
final Map<String, Object> toAdd;
if (copyForeigns == ForeignCopyMode.COPY_ROW || this.foreigns.size() == 0) {
if ((copyForeigns == ForeignCopyMode.COPY_ROW || this.foreigns.size() == 0) && (copyDefaults == SQLDefaultCopyMode.COPY || !this.values.values().contains(SQL_DEFAULT))) {
if (copy) {
toAdd = createLinkedHashMap(this.size());
toAdd.putAll(this.values);
702,19 → 763,46
final Set<Entry<String, Object>> entrySet = this.values.entrySet();
toAdd = createLinkedHashMap(entrySet.size());
for (final Map.Entry<String, Object> e : entrySet) {
if (!(e.getValue() instanceof SQLRowValues)) {
toAdd.put(e.getKey(), e.getValue());
final String fieldName = e.getKey();
boolean add = true;
Object val = e.getValue();
if (e.getValue() == SQL_DEFAULT) {
if (copyDefaults == SQLDefaultCopyMode.REMOVE) {
add = false;
} else if (copyDefaults == SQLDefaultCopyMode.COPY) {
add = true;
} else {
final Value<Object> parsedDefaultValue = this.getTable().getField(fieldName).getParsedDefaultValue();
if (parsedDefaultValue.hasValue()) {
val = parsedDefaultValue.getValue();
} else if (copyDefaults == SQLDefaultCopyMode.PARSE_ELSE_ERROR) {
throw new IllegalStateException("Couldn't parse default for " + fieldName);
} else {
assert copyDefaults == SQLDefaultCopyMode.PARSE_ELSE_REMOVE;
add = false;
}
}
} else if (e.getValue() instanceof SQLRowValues) {
if (copyForeigns == ForeignCopyMode.NO_COPY) {
add = false;
} else if (copyForeigns == ForeignCopyMode.COPY_ROW) {
add = true;
} else if (copyForeigns == ForeignCopyMode.COPY_NULL) {
toAdd.put(e.getKey(), null);
} else if (copyForeigns != ForeignCopyMode.NO_COPY) {
val = null;
} else {
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);
val = foreign.getIDNumber();
else if (copyForeigns == ForeignCopyMode.COPY_ID_OR_RM)
add = false;
else
assert copyForeigns == ForeignCopyMode.COPY_ID_OR_ROW;
}
}
if (add)
toAdd.put(fieldName, val);
}
}
return copy ? toAdd : Collections.unmodifiableMap(toAdd);
}
 
797,15 → 885,23
}
 
@Override
public final SQLRow asRow() {
public final SQLRow asRow(final Boolean immutable) {
if (!this.hasID())
throw new IllegalStateException(this + " has no ID");
return new SQLRow(this.getTable(), this.getAllValues(ForeignCopyMode.COPY_ID_OR_RM));
// keep the most information without throwing an exception
final SQLRow res = new SQLRow(this.getTable(), this.getAllValues(ForeignCopyMode.COPY_ID_OR_RM, SQLDefaultCopyMode.PARSE_ELSE_REMOVE, false));
// if the caller doesn't care about immutable, keep the same
if (Boolean.TRUE.equals(immutable) || (immutable == null && this.isFrozen()))
res.freeze();
return res;
}
 
@Override
public final SQLRowValues asRowValues() {
public final SQLRowValues asRowValues(final Boolean immutable) {
if (immutable == null || this.isFrozen() == immutable)
return this;
else
return this.getGraph().deepCopy(this, immutable);
}
 
// *** set
815,6 → 911,7
*
* @return <code>true</code> if this (and its graph) is not modifiable.
*/
@Override
public final boolean isFrozen() {
final SQLRowValuesCluster g = this.getGraph(false);
return g != null && g.isFrozen();
1010,10 → 1107,7
final ValueConvertor<T, U> conv = ValueConvertorFactory.find(source, dest);
if (conv == null)
throw new IllegalArgumentException("No convertor to " + dest + " from " + source);
assert source == value.getClass();
@SuppressWarnings("unchecked")
final T tVal = (T) value;
return conv.convert(tVal);
return conv.convert(source.cast(value));
}
 
private void _put(String fieldName, Object value, final boolean checkName, final ValueOperation checkValue) {
1023,7 → 1117,7
if (value == SQL_EMPTY_LINK) {
// keep getForeignTable since it does the check
value = this.getForeignTable(fieldName).getUndefinedIDNumber();
} else if (value != null && value != SQL_DEFAULT && checkValue != ValueOperation.PASS) {
} else if (value != null && value != SQL_DEFAULT && !(value instanceof SQLExpression) && checkValue != ValueOperation.PASS) {
final SQLField field = this.getTable().getField(fieldName);
if (value instanceof SQLRowValues) {
if (!field.isForeignKey())
1032,7 → 1126,11
final Class<?> javaType = field.getType().getJavaType();
if (!javaType.isInstance(value)) {
if (checkValue == ValueOperation.CONVERT) {
try {
value = convert(value.getClass(), value, javaType);
} catch (Exception e) {
throw new IllegalArgumentException("Couldn't convert " + SQLBase.quoteIdentifier(fieldName) + " " + value + getClassName(value.getClass()) + " to " + javaType, e);
}
} else {
throw new IllegalArgumentException("Wrong type for " + fieldName + ", expected " + javaType + " but got " + value.getClass());
}
1062,6 → 1160,17
}
 
/**
* Put an SQL expression into this.
*
* @param fieldName the field name.
* @param sqlExpression an SQL expression, e.g. "now()" or "'a'".
* @return this.
*/
public SQLRowValues putSQL(final String fieldName, final String sqlExpression) {
return this.put(fieldName, new SQLExpression(sqlExpression));
}
 
/**
* Set a new {@link SQLRowValues} as the value of <code>fieldName</code>. ATTN contrary to many
* methods this one do not return <code>this</code>.
*
1083,17 → 1192,23
/**
* Create or follow the passed path and put the passed row at the end.
*
* @param p the {@link Path#isSingleLink() single link} path.
* @param p the {@link Path#isSingleLink() single link} path, can only be {@link Path#length()
* empty} if <code>vals</code> is <code>null</code>.
* @param createPath <code>true</code> if new rows must {@link #createPathToOne(Path) always be
* created}, <code>false</code> if existing rows can be {@link #assurePath(Path) used}.
* @param vals the row to {@link #put(Step, SQLRowValues) put}, <code>null</code> to create a
* new one.
* @return the row that was added.
* @return the row at the end of the path.
* @throws IllegalArgumentException if the path is invalid.
*/
public final SQLRowValues put(final Path p, final boolean createPath, final SQLRowValues vals) throws IllegalArgumentException {
if (p.length() == 0)
throw new IllegalArgumentException("Empty path");
if (p.length() == 0) {
if (vals == null) {
return this;
} else {
throw new IllegalArgumentException("Empty path, won't merge " + vals + " into " + this);
}
}
if (!p.isSingleLink())
throw new IllegalArgumentException("Multi-link path " + p);
// checks first table
1277,6 → 1392,10
return this.putAll(m, null);
}
 
public final SQLRowValues putAllAbsent(Map<String, ?> m) {
return this.loadAll(m, FillMode.DONT_OVERWRITE);
}
 
public final SQLRowValues putAll(Map<String, ?> m, final Collection<String> keys) {
return this.putAll(m, keys, FillMode.OVERWRITE);
}
1839,6 → 1958,8
// 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 (fieldVal instanceof SQLExpression)
return new Object[] { fieldName, null };
if (fieldVal != null && fieldVal != SQL_DEFAULT && !(fieldVal instanceof SQLRowValues)) {
final SQLRow pb = foreignTable.checkValidity(((Number) fieldVal).intValue());
if (pb != null)
2030,11 → 2151,12
String result = this.getClass().getSimpleName() + " on " + this.getTable() + " : {";
result += CollectionUtils.join(this.values.entrySet(), ", ", new ITransformer<Entry<String, ?>, String>() {
public String transformChecked(final Entry<String, ?> e) {
final String className = e.getValue() == null ? "" : "(" + e.getValue().getClass() + ")";
final Object fieldVal = e.getValue();
final String className = getClassName(fieldVal);
final String value;
// avoid infinite loop (and overly verbose string)
if (e.getValue() instanceof SQLRowValues) {
final SQLRowValues foreignVals = (SQLRowValues) e.getValue();
if (fieldVal instanceof SQLRowValues) {
final SQLRowValues foreignVals = (SQLRowValues) fieldVal;
if (foreignVals == SQLRowValues.this) {
value = "this";
} else if (foreignVals.hasID()) {
2043,8 → 2165,9
// so that if the same vals is referenced multiple times, we can see it
value = "@" + System.identityHashCode(foreignVals);
}
} else
value = String.valueOf(e.getValue());
} else {
value = String.valueOf(fieldVal);
}
return e.getKey() + "=" + value + className;
}
});
2052,6 → 2175,11
return result;
}
 
protected String getClassName(final Object val) {
// SQL_DEFAULT already has our class name in toString()
return val == null || val == SQL_DEFAULT || val instanceof SQLExpression ? "" : "(" + val.getClass() + ")";
}
 
/**
* Return a graphical representation (akin to the result of a query) of the tree rooted at
* <code>this</code>.
2172,8 → 2300,8
}
// fields are already checked so if IDs are not wanted, just omit foreign rows
final ForeignCopyMode copyMode = useForeignID ? ForeignCopyMode.COPY_ID_OR_RM : ForeignCopyMode.NO_COPY;
final Map<String, Object> thisVals = this.getAllValues(copyMode, !usePK);
final Map<String, Object> oVals = o.getAllValues(copyMode, !usePK);
final Map<String, Object> thisVals = this.getAllValues(copyMode, SQLDefaultCopyMode.COPY, !usePK);
final Map<String, Object> oVals = o.getAllValues(copyMode, SQLDefaultCopyMode.COPY, !usePK);
if (!usePK) {
final List<String> pk = this.getTable().getPKsNames();
thisVals.keySet().removeAll(pk);
2243,6 → 2371,7
String req = (insert ? "INSERT INTO " : "UPDATE ") + tableQuoted + " ";
if (insert) {
assert fieldsNames.size() == values.size();
final List<String> insertValues = new ArrayList<>(values.size());
// remove DEFAULT since they are useless and prevent us from using
// INSERT INTO "TABLEAU_ELECTRIQUE" ("ID_OBSERVATION", ...) select DEFAULT, ?,
// MAX("ORDRE") + 1 FROM "TABLEAU_ELECTRIQUE"
2250,16 → 2379,18
if (values.get(i) == SQL_DEFAULT) {
fieldsNames.remove(i);
values.remove(i);
} else {
insertValues.add(getFieldValue(values.get(i)));
}
}
assert fieldsNames.size() == values.size();
 
// ajout de l'ordre
final SQLField order = table.getOrderField();
final SQLField orderF = table.getOrderField();
final boolean selectOrder;
if (order != null && !fieldsNames.contains(order.getName())) {
if (orderF != null && !fieldsNames.contains(orderF.getName())) {
// si l'ordre n'est pas spécifié, ajout à la fin
fieldsNames.add(order.getName());
fieldsNames.add(orderF.getName());
selectOrder = true;
} else {
selectOrder = false;
2274,8 → 2405,7
return SQLBase.quoteIdentifier(input);
}
}) + ")";
// no DEFAULT thus only ?
final String questionMarks = CollectionUtils.join(Collections.nCopies(values.size(), "?"), ", ");
final String questionMarks = CollectionUtils.join(insertValues, ", ");
if (selectOrder) {
// needed since VALUES ( (select MAX("ORDRE") from "LOCAL") ) on MySQL yield
// "You can't specify target table 'LOCAL' for update in FROM clause"
2284,7 → 2414,7
if (values.size() > 0)
req += ", ";
// COALESCE for empty tables, MIN_ORDER + 1 since MIN_ORDER cannot be moved
req += "COALESCE(MAX(" + SQLBase.quoteIdentifier(order.getName()) + "), " + ReOrder.MIN_ORDER + ") + 1 FROM " + tableQuoted;
req += "COALESCE(MAX(" + SQLBase.quoteIdentifier(orderF.getName()) + "), " + ReOrder.MIN_ORDER + ") + 1 FROM " + tableQuoted;
} else {
req += " VALUES (";
req += questionMarks;
2314,7 → 2444,7
int i = 0;
for (final Object value : values) {
// nothing to set if there's no corresponding '?'
if (value != SQL_DEFAULT) {
if (value != SQL_DEFAULT && !(value instanceof SQLExpression)) {
final Object toIns;
if (value instanceof SQLRowValues) {
// TODO if we already point to some row, archive it
2330,7 → 2460,12
}
 
private static String getFieldValue(final Object value) {
return value == SQL_DEFAULT ? "DEFAULT" : "?";
if (value == SQL_DEFAULT)
return "DEFAULT";
else if (value instanceof SQLExpression)
return ((SQLExpression) value).getSQL();
else
return "?";
}
 
@Override
2521,4 → 2656,14
public static final SQLRowValues trim(final SQLRowValues r) {
return new SQLRowValues(r, ForeignCopyMode.COPY_ID_OR_RM);
}
 
public static final List<SQLRowValues> toImmutableList(final Collection<SQLRowValues> rows) {
return toImmutableList(rows, new ArrayList<>(rows.size()));
}
 
public static final List<SQLRowValues> toImmutableList(final Collection<SQLRowValues> rows, final List<SQLRowValues> res) {
for (final SQLRowValues v : rows)
res.add(v.toImmutable());
return Collections.unmodifiableList(res);
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLDataSource.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
25,6 → 25,7
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cache.CacheResult;
import org.openconcerto.utils.cache.ICacheSupport;
import org.openconcerto.utils.cc.ITransformerExn;
 
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
285,7 → 286,7
 
// if TCP isn't used or is used to connect to localhost, TCP should be quite robust
private final Level getLogLevelForIgnoredTCPParam() {
return SQLSyntax.get(this.sysRoot).isServerLocalhost(this.sysRoot.getServer()) ? Level.CONFIG : Level.WARNING;
return this.sysRoot.getServer().isLocalhost() ? Level.CONFIG : Level.WARNING;
}
 
public final void setTCPKeepAlive(final boolean b) {
836,6 → 837,15
return this.handlers.get(Thread.currentThread());
}
 
public final <T, X extends Exception> T useConnection(final ITransformerExn<? super SQLDataSource, T, X> run) throws SQLException, X {
return this.useConnection(new ConnectionHandlerNoSetup<T, X>() {
@Override
public T handle(SQLDataSource ds) throws X {
return run.transformChecked(ds);
}
});
}
 
/**
* Use a single connection to execute <code>handler</code>.
*
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLResultSet.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
34,8 → 34,12
import java.sql.Statement;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
/**
46,6 → 50,9
*/
public class SQLResultSet implements ResultSet {
 
// useful since some JDBC drivers (e.g. PgResultSet) implement getObject(col, clazz) by the
// equivalent of clazz.cast(getObject(col)). I.e. getObject(col, Long.class) will fail if col
// is an int.
static public final <T> T getValue(final ResultSet rs, final Class<T> clz, final int columnIndex) throws SQLException {
final Object res;
if (clz == Object.class)
73,10 → 80,11
else if (Time.class.isAssignableFrom(clz))
res = rs.getTime(columnIndex);
else
res = rs.getObject(columnIndex);
res = rs.getObject(columnIndex, clz);
return clz.cast(res);
}
 
// see above comment
static public final <T> T getValue(final ResultSet rs, final Class<T> clz, final String columnLabel) throws SQLException {
final Object res;
if (clz == Object.class)
104,7 → 112,7
else if (Time.class.isAssignableFrom(clz))
res = rs.getTime(columnLabel);
else
res = rs.getObject(columnLabel);
res = rs.getObject(columnLabel, clz);
return clz.cast(res);
}
 
117,6 → 125,24
}
}
 
static public final <T> List<T> getList(final ResultSet rs, int column, final Class<? extends T> itemType) throws SQLException {
final Array array = rs.getArray(column);
try {
return Collections.unmodifiableList(readArray(array, itemType, new ArrayList<>()));
} finally {
array.free();
}
}
 
static public final <T, C extends Collection<T>> C readArray(final Array array, final Class<? extends T> itemType, final C res) throws SQLException {
final ResultSet rs = array.getResultSet();
while (rs.next()) {
// 1 is the index
res.add(getValue(rs, itemType, 2));
}
return res;
}
 
private final ResultSet delegate;
private final ResultSetFullnameHelper helper;
private final CachedTransformer<String, Integer, SQLException> indexes;
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLBase.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
46,6 → 46,7
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
769,19 → 770,13
* @return pattern with % replaced, eg SELECT * FROM "TENSION" where "TENSION.LABEL" like '%a%'.
*/
public final String quote(final String pattern, Object... params) {
return quote(this, pattern, params);
return quote(this.getDBSystemRoot().getSyntax(), pattern, params);
}
 
// since Strings might not be quoted correctly
@Deprecated
public final static String quoteStd(final String pattern, Object... params) {
return quote(null, pattern, params);
}
 
static private final Pattern percent = Pattern.compile("%.");
 
private final static String quote(final SQLBase b, final String pattern, Object... params) {
final SQLSyntax s = b == null ? null : SQLSyntax.get(b);
final static String quote(final SQLSyntax s, final String pattern, Object... params) {
Objects.requireNonNull(s, "Missing syntax");
final Matcher m = percent.matcher(pattern);
final StringBuffer sb = new StringBuffer();
int i = 0;
794,7 → 789,7
} else {
final Object param = params[i++];
if (modifier == 's') {
replacement = SQLSyntax.quoteString(s, param.toString());
replacement = s.quoteString(param.toString());
} else if (modifier == 'i') {
if (param instanceof SQLName)
replacement = ((SQLName) param).quote();
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSyntaxMySQL.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
24,10 → 24,13
import org.openconcerto.sql.utils.SQLUtils.SQLFactory;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.ListMap;
import org.openconcerto.utils.NumberUtils;
import org.openconcerto.utils.StringUtils;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.ITransformer;
import org.openconcerto.xml.XMLCodecUtils;
 
import java.beans.DefaultPersistenceDelegate;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
101,6 → 104,8
CAST_TYPES.put(java.sql.Time.class, "time");
CAST_TYPES.put(Blob.class, "binary");
CAST_TYPES.put(String.class, "char");
 
XMLCodecUtils.register(SQLSyntaxMySQL.class, new DefaultPersistenceDelegate(new String[] { "noBackslashEscapes" }));
}
 
private final boolean noBackslashEscapes;
123,6 → 128,10
this.typeNames.addAll(String.class, "varchar", "char");
}
 
public final boolean isNoBackslashEscapes() {
return this.noBackslashEscapes;
}
 
@Override
public final String quoteString(String s) {
final String res = super.quoteString(s);
282,6 → 291,23
}
 
@Override
public boolean isTableNotFoundException(Exception exn) {
return SQLUtils.findWithSQLState(exn).getErrorCode() == 1146;
}
 
@Override
public String getSetLockTimeoutQuery(int millis) {
// Wait at least the passed amount.
// 0ms -> 0s, 1ms -> 1s, 1000ms -> 1s, 1001ms -> 2s
return "set innodb_lock_wait_timeout = " + NumberUtils.divideRoundUp(millis, 1000);
}
 
@Override
public String getShowLockTimeoutQuery() {
return "select @@SESSION.innodb_lock_wait_timeout * 1000";
}
 
@Override
public Map<ClauseType, List<String>> getAlterField(SQLField f, Set<Properties> toAlter, String type, String defaultVal, Boolean nullable) {
final boolean newNullable = toAlter.contains(Properties.NULLABLE) ? nullable : getNullable(f);
final String newType = toAlter.contains(Properties.TYPE) ? type : getType(f);
608,4 → 634,30
}
};
}
 
@Override
public String getSessionIDExpression() {
return "CONNECTION_ID()";
}
 
@Override
public String getSessionsQuery(final DBSystemRoot sysRoot, final boolean includeSelf) {
final String allRows = "SELECT \"ID\", \"Info\" as \"QUERY\", \"USER\" as \"USER_NAME\" FROM INFORMATION_SCHEMA.PROCESSLIST";
if (includeSelf)
return allRows;
return allRows + " WHERE \"ID\" != " + this.getSessionIDExpression();
}
 
@Override
protected String getAllUsersForGrant() {
return "'%'@'%'";
}
 
@Override
public String getAllowConnectionsQuery(String sysRootName, final boolean allow) {
return null;
// MAYBE use this (but still allow super users)
// https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_offline_mode
// return "set GLOBAL offline_mode = " + (allow ? "off" : "on");
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/users/UserManager.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
67,6 → 67,10
 
public static final User getUser() {
final UserManager mngr = getInstance();
return getCurrentUser(mngr);
}
 
public static User getCurrentUser(final UserManager mngr) {
return mngr == null ? null : mngr.getCurrentUser();
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/users/rights/UserRightsManager.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
128,8 → 128,12
 
public static final UserRights getCurrentUserRights() {
final UserManager mngr = UserManager.getInstance();
final UserRightsManager rightsMngr = getInstance();
return getCurrentUserRights(rightsMngr, mngr);
}
 
public static final UserRights getCurrentUserRights(final UserRightsManager rightsMngr, final UserManager mngr) {
// if right table doesn't exist, give access to everything
final UserRightsManager rightsMngr = getInstance();
if (rightsMngr == null)
return UserRights.ALLOW_ALL;
// else if there are rights (and thus users) but no user is defined, use the default rights
/trunk/OpenConcerto/src/org/openconcerto/sql/users/rights/UserRightSQLElement.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
14,14 → 14,19
package org.openconcerto.sql.users.rights;
 
import static java.util.Arrays.asList;
 
import org.openconcerto.sql.element.GlobalMapper;
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.model.DBRoot;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.utils.SQLCreateTable;
import org.openconcerto.sql.view.list.action.ListEvent;
import org.openconcerto.sql.view.list.action.SQLRowValuesAction;
import org.openconcerto.sql.view.list.action.SQLRowValuesAction.PredicateRowAction;
 
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
55,11 → 60,19
return res;
}
 
static private final SQLRowValuesAction COPY_ACTION = new PredicateRowAction(true, (le) -> {
final Toolkit toolkit = Toolkit.getDefaultToolkit();
final Clipboard systemClipboard = toolkit.getSystemClipboard();
systemClipboard.setContents(new UserRightCopySelection(le.getSelectedRowAccessors()), null);
}).setPredicate(ListEvent.getNonEmptySelectionPredicate()).setName("Copier");
 
public UserRightSQLElement(final DBRoot r) {
super(r.findTable(TABLE_NAME), null, "sql.user-right");
 
final UserRightGroup group = new UserRightGroup();
GlobalMapper.getInstance().map(UserRightSQLComponent.ID, group);
setDefaultGroup(group);
getRowValuesActions().add(COPY_ACTION);
}
 
protected List<String> getListFields() {
/trunk/OpenConcerto/src/org/openconcerto/sql/users/rights/UserRightCopySelection.java
New file
0,0 → 1,64
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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.users.rights;
 
import org.openconcerto.sql.model.SQLRowAccessor;
 
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.util.List;
 
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
 
public class UserRightCopySelection implements Transferable {
 
// JSOnArray[ID Right, object,hasRight]
public final JSONObject rights;
public final static DataFlavor rightsFlavor = new DataFlavor(JSONObject.class, "RightsDataFlavor");
 
public UserRightCopySelection(List<? extends SQLRowAccessor> list) {
this.rights = new JSONObject();
 
for (SQLRowAccessor sqlRowValues : list) {
JSONArray array = new JSONArray();
array.add(sqlRowValues.getForeignIDNumber("ID_RIGHT"));
array.add(sqlRowValues.getString("OBJECT"));
array.add(sqlRowValues.getBoolean("HAVE_RIGHT"));
this.rights.put(String.valueOf(sqlRowValues.getID()), array);
}
 
}
 
@Override
public DataFlavor[] getTransferDataFlavors() {
DataFlavor[] ret = { rightsFlavor };
return ret;
}
 
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return rightsFlavor.equals(flavor);
}
 
@Override
public synchronized Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
if (isDataFlavorSupported(flavor)) {
return this.rights;
} else {
throw new UnsupportedFlavorException(rightsFlavor);
}
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/users/rights/UserRightsPanel.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
17,19 → 17,39
import org.openconcerto.sql.TM;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.model.SQLInsert;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTableEvent;
import org.openconcerto.sql.model.SQLTableEvent.Mode;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.ListSQLRequest;
import org.openconcerto.sql.sqlobject.IComboSelectionItem;
import org.openconcerto.sql.users.UserManager;
import org.openconcerto.sql.view.ListeModifyPanel;
import org.openconcerto.sql.view.list.RowAction;
import org.openconcerto.ui.DefaultGridBagConstraints;
import org.openconcerto.utils.ExceptionHandler;
import org.openconcerto.utils.Tuple2;
import org.openconcerto.utils.cc.IClosure;
 
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
38,6 → 58,9
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
 
import net.minidev.json.JSONArray;
import net.minidev.json.JSONObject;
 
public class UserRightsPanel extends JPanel {
 
// Liste des utilisateurs
130,8 → 153,40
c3.weighty = 1;
c3.fill = GridBagConstraints.BOTH;
this.add(pane, c3);
 
RowAction actionPaste = new RowAction(new AbstractAction("Coller") {
 
@Override
public void actionPerformed(ActionEvent e) {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable clipboardContent = clipboard.getContents(this);
 
if (clipboardContent.isDataFlavorSupported(UserRightCopySelection.rightsFlavor)) {
JSONObject rights;
try {
rights = (JSONObject) clipboardContent.getTransferData(UserRightCopySelection.rightsFlavor);
 
addRights(rights);
 
} catch (UnsupportedFlavorException | IOException e1) {
 
e1.printStackTrace();
}
}
}
 
}, true) {
@Override
public boolean enabledFor(List<SQLRowValues> selection) {
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable clipboardContent = clipboard.getContents(this);
 
return clipboardContent.isDataFlavorSupported(UserRightCopySelection.rightsFlavor);
}
};
this.modifPanel.getListe().addIListeAction(actionPaste);
}
 
private void updateListFromSelection() {
final int selectedIndex = this.list.getSelectedIndex();
 
153,6 → 208,64
((UserRightSQLComponent) this.modifPanel.getAddComp()).setUserID(userID);
}
 
private void addRights(JSONObject rights) {
// Récupération des droits courants
final ListSQLRequest req = this.modifPanel.getListe().getRequest();
List<SQLRowValues> vals = req.getValues();
Map<Tuple2<Number, String>, SQLRowValues> currentUserRight = new HashMap<>();
for (SQLRowValues sqlRowValues : vals) {
currentUserRight.put(Tuple2.create(sqlRowValues.getForeignIDNumber("ID_RIGHT"), sqlRowValues.getString("OBJECT")), sqlRowValues);
}
 
try {
List<SQLInsert> inserts = new ArrayList<SQLInsert>();
 
final int selectedIndex = this.list.getSelectedIndex();
final boolean b = selectedIndex >= 0;
 
final int userID;
if (b) {
userID = this.list.getModel().getRowAt(selectedIndex).getID();
} else {
// since we don't display user in the list (to avoid undef)
// we need to always display at most one user
userID = this.list.getModel().getTable().getUndefinedID();
}
 
BigDecimal order = getTable().getMaxOrder().add(BigDecimal.ONE);
for (Object a : rights.values()) {
JSONArray array = (JSONArray) a;
Number right = (Number) array.get(0);
String obj = (String) array.get(1);
Boolean hasRight = (Boolean) array.get(2);
 
final Tuple2<Number, String> key = Tuple2.create(right, obj);
if (currentUserRight.containsKey(key)) {
SQLRowValues rowVals = currentUserRight.get(key);
if (!rowVals.getBoolean("HAVE_RIGHT").equals(hasRight)) {
rowVals.createEmptyUpdateRow().put("HAVE_RIGHT", hasRight).commit();
}
} else {
SQLInsert insert = new SQLInsert();
insert.add(getTable().getField("ID_RIGHT"), right);
insert.add(getTable().getField("HAVE_RIGHT"), hasRight);
insert.add(getTable().getField("OBJECT"), obj);
insert.add(getTable().getField("ID_USER_COMMON"), userID);
insert.add(getTable().getOrderField(), order);
 
inserts.add(insert);
order = order.add(BigDecimal.ONE);
}
}
if (!inserts.isEmpty()) {
SQLInsert.executeMultiple(getTable().getDBSystemRoot(), inserts);
getTable().fire(new SQLTableEvent(getTable(), SQLRow.NONEXISTANT_ID, Mode.ROW_ADDED));
}
} catch (SQLException e1) {
ExceptionHandler.handle("Erreur lors de la duplication des droits", e1);
}
}
 
public final SQLTable getTable() {
return this.modifPanel.getElement().getTable();
}
/trunk/OpenConcerto/src/org/openconcerto/sql/users/UserCommonSQLElement.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
123,7 → 123,7
@Override
public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int column) {
final JLabel res = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
final boolean isCurrentUser = ITableModel.getLine(table.getModel(), row).getRow().getID() == UserManager.getUserID();
final boolean isCurrentUser = ITableModel.getLine(table.getModel(), row).getRowAccessor().getID() == UserManager.getUserID();
final int targetStyle = isCurrentUser ? Font.BOLD : Font.PLAIN;
if ((res.getFont().getStyle() & targetStyle) == 0) {
res.setFont(res.getFont().deriveFont(targetStyle));
519,6 → 519,7
final String pass = String.valueOf(this.getPassField().getPassword());
final String dbPass = Boolean.getBoolean(LEGACY_PASSWORDS) ? '"' + pass + '"' : pass;
this.encryptedPass.setText(Login.encodePassword(dbPass));
System.err.println("UserCommonSQLElement.UserSQLComponent.updateEncrypted() encode:"+dbPass+":"+this.encryptedPass.getText());
}
 
private boolean checkValidityPassword() {
/trunk/OpenConcerto/src/org/openconcerto/sql/changer/convert/TextDefault.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
17,7 → 17,6
import org.openconcerto.sql.model.DBSystemRoot;
import org.openconcerto.sql.model.SQLBase;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLSystem;
import org.openconcerto.sql.model.SQLTable;
 
49,8 → 48,8
@SuppressWarnings("unchecked")
@Override
protected void changeImpl(SQLTable t) throws SQLException {
final String infoSchema = SQLSelect.quote("SELECT TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE from information_schema.COLUMNS where TABLE_SCHEMA=%s and TABLE_NAME=%s", t.getBase()
.getName(), t.getName());
final String infoSchema = t.getBase().quote("SELECT TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT, IS_NULLABLE from information_schema.COLUMNS where TABLE_SCHEMA=%s and TABLE_NAME=%s",
t.getBase().getName(), t.getName());
final Map<String, Object> defaults = (Map<String, Object>) this.getDS().execute(infoSchema, new ResultSetHandler() {
public Object handle(ResultSet rs) throws SQLException {
final Map<String, Object> res = new HashMap<String, Object>();
/trunk/OpenConcerto/src/org/openconcerto/sql/changer/convert/RenamePK.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
14,11 → 14,11
package org.openconcerto.sql.changer.convert;
 
import static java.util.Collections.singletonList;
 
import org.openconcerto.sql.changer.Changer;
import org.openconcerto.sql.model.ConnectionHandlerNoSetup;
import org.openconcerto.sql.model.DBSystemRoot;
import org.openconcerto.sql.model.SQLDataSource;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLSystem;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.graph.Link;
67,7 → 67,7
getDS().execute(dropIndex);
}
 
final String alter = SQLSelect.quote("ALTER TABLE %n CHANGE COLUMN %n %i " + getSyntax().getPrimaryIDDefinitionShort(), t, t.getKey(), newName);
final String alter = t.getBase().quote("ALTER TABLE %n CHANGE COLUMN %n %i " + getSyntax().getPrimaryIDDefinitionShort(), t, t.getKey(), newName);
getStream().println(alter);
getDS().execute(alter);
t.fetchFields();
/trunk/OpenConcerto/src/org/openconcerto/sql/changer/convert/MergeTable.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
14,6 → 14,7
package org.openconcerto.sql.changer.convert;
 
import static org.openconcerto.utils.CollectionUtils.substract;
 
import org.openconcerto.sql.changer.Changer;
import org.openconcerto.sql.model.AliasedTable;
import org.openconcerto.sql.model.ConnectionHandlerNoSetup;
27,6 → 28,7
import org.openconcerto.sql.model.SQLSchema;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLSyntax;
import org.openconcerto.sql.model.SQLSystem;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.model.graph.DatabaseGraph;
34,6 → 36,7
import org.openconcerto.sql.model.graph.TablesMap;
import org.openconcerto.sql.request.UpdateBuilder;
import org.openconcerto.sql.utils.AlterTable;
import org.openconcerto.sql.utils.ChangeTable;
import org.openconcerto.sql.utils.ChangeTable.FCSpec;
import org.openconcerto.sql.utils.SQLCreateTable;
import org.openconcerto.sql.utils.SQLUtils;
134,7 → 137,8
// if we copy no rows, no need to check constraints
final boolean noRowsToMerge = oldIDs.size() == 0;
final DatabaseGraph graph = t.getDBSystemRoot().getGraph();
// check that transferred data from t still points to the same rows
// Check that transferred data from t still points to the same rows, i.e. that each foreign
// key of t exists in this.destTable and points to the same table.
final Set<Link> selfRefLinks = new HashSet<Link>();
for (final Link l : graph.getForeignLinks(t)) {
final Link destLink = graph.getForeignLink(this.destTable, l.getCols());
161,11 → 165,16
}
}
 
if (this.isDryRun())
return;
 
final SQLSyntax syntax = t.getDBSystemRoot().getSyntax();
final TablesMap tables = new TablesMap();
SQLUtils.executeAtomic(t.getDBSystemRoot().getDataSource(), new ConnectionHandlerNoSetup<Void, SQLException>() {
@Override
public Void handle(final SQLDataSource ds) throws SQLException {
final Set<SQLTable> toRefresh = new HashSet<SQLTable>();
SQLUtils.executeAtomic(t.getDBSystemRoot().getDataSource(), new ConnectionHandlerNoSetup<Object, SQLException>() {
@Override
public Object handle(final SQLDataSource ds) throws SQLException {
 
// drop self reference links before inserting
final AlterTable dropSelfFK = new AlterTable(MergeTable.this.destTable);
for (final Link selfRef : selfRefLinks) {
175,7 → 184,24
ds.execute(dropSelfFK.asString());
 
// copy all data of t into destTable
final List<Number> insertedIDs = SQLRowValues.insertIDs(MergeTable.this.destTable, fields + " " + sel.asString());
final List<Number> insertedIDs;
// In H2 v1.3 Statement.getGeneratedKeys() only returns the first ID
if (getSyntax().getSystem() == SQLSystem.H2) {
final SQLField destPK = MergeTable.this.destTable.getKey();
// null if table is empty
final Number maxID = (Number) getDS().executeScalar(new SQLSelect().addSelect(destPK, "max").asString());
final int insertedCount = SQLRowValues.insertCount(destPK.getTable(), fields + " " + sel.asString());
 
final SQLSelect selNewIDs = createSelect(destPK.getTable());
selNewIDs.addSelect(destPK);
if (maxID != null)
selNewIDs.setWhere(new Where(destPK, ">", maxID));
insertedIDs = getDS().executeCol(selNewIDs.asString());
if (insertedIDs.size() != insertedCount)
throw new IllegalStateException("Expected " + insertedCount + " new IDs but got " + insertedIDs.size());
} else {
insertedIDs = SQLRowValues.insertIDs(MergeTable.this.destTable, fields + " " + sel.asString());
}
// handle undefined
insertedIDs.add(0, MergeTable.this.destTable.getUndefinedIDNumber());
oldIDs.add(0, t.getUndefinedIDNumber());
217,9 → 243,20
ds.execute(t.getBase().quote("DROP TABLE %f", t));
ds.execute("DROP TABLE " + mapName.quote());
 
SQLTable.unsetUndefIDs(t.getSchema(), CollectionUtils.createSet(t.getName(), mapName.getName()));
 
toRefresh.add(t);
toRefresh.add(mapT);
 
final Set<SQLSchema> schemas = new HashSet<SQLSchema>();
for (final SQLTable table : toRefresh) {
tables.add(table.getDBRoot().getName(), table.getName());
schemas.add(table.getSchema());
}
for (final SQLSchema schema : schemas) {
schema.updateVersion();
}
 
return null;
}
 
245,18 → 282,19
// update the field using the map
final UpdateBuilder update = new UpdateBuilder(refTable);
final AliasedTable alias1 = new AliasedTable(mapT, "m");
update.addTable(alias1);
update.set(refKey.getName(), alias1.getField("NEW_ID").getFieldRef());
update.setWhere(new Where(refKey, Where.NULL_IS_DATA_EQ, alias1.getField("OLD_ID")));
// Where.NULL_IS_DATA_EQ since we want to be able to map a null undefined
// ID, i.e. rows pointing to null into 1.
update.addVirtualJoin(alias1, "OLD_ID", Where.NULL_IS_DATA_EQ, refKey.getName());
update.setFromVirtualJoinField(refKey.getName(), alias1.getAlias(), "NEW_ID");
if (selfLink) {
// only update new rows (old rows can have the same IDs but they point to old
// foreign rows, they must not be updated)
final AliasedTable onlyNew = new AliasedTable(mapT, "onlyNew");
update.addTable(onlyNew);
// we added the undefined to NEW_ID, but it wasn't copied from t so don't update
final Where w = new Where(refTable.getKey(), Where.NULL_IS_DATA_EQ, onlyNew.getField("NEW_ID")).and(new Where(refTable.getKey(), Where.NULL_IS_DATA_NEQ, refTable
.getUndefinedIDNumber()));
update.setWhere(update.getWhere().and(w));
final Where w = new Where(refTable.getKey(), true, new SQLSelect().addSelect(onlyNew.getField("NEW_ID")))
// we added the undefined to NEW_ID, but it wasn't copied from t so
// don't update
.and(new Where(refTable.getKey(), Where.NULL_IS_DATA_NEQ, refTable.getUndefinedIDNumber()));
update.setWhere(w.and(update.getWhere()));
}
ds.execute(update.asString());
 
265,21 → 303,14
// don't create an index : if there was one it's still there, if there wasn't
// don't alter the table silently (use AddFK if you want that)
addFK.addForeignConstraint(FCSpec.createFromLink(refLink, MergeTable.this.destTable), false);
// e.g. change from 'integer DEFAULT 1' to 'integer'
addFK.alterColumnDefault(refLink.getSingleField().getName(), ChangeTable.getForeignColumDefaultValue(MergeTable.this.destTable));
ds.execute(addFK.asString());
return refTable;
}
});
final TablesMap tables = new TablesMap();
final Set<SQLSchema> schemas = new HashSet<SQLSchema>();
for (final SQLTable table : toRefresh) {
tables.add(table.getDBRoot().getName(), table.getName());
schemas.add(table.getSchema());
}
t.getDBSystemRoot().refresh(tables, false);
for (final SQLSchema schema : schemas) {
schema.updateVersion();
}
}
 
private final SQLSelect createSelect(final SQLTable t) {
final SQLSelect sel = new SQLSelect(true);
/trunk/OpenConcerto/src/org/openconcerto/sql/replication/MemoryRep.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
28,11 → 28,11
import org.openconcerto.sql.model.graph.TablesMap;
import org.openconcerto.sql.utils.CSVHandler;
import org.openconcerto.sql.utils.ChangeTable;
import org.openconcerto.sql.utils.ChangeTable.FCSpec;
import org.openconcerto.sql.utils.SQLCreateMoveableTable;
import org.openconcerto.sql.utils.SQLCreateRoot;
import org.openconcerto.sql.utils.SQLCreateTableBase;
import org.openconcerto.sql.utils.SQLUtils;
import org.openconcerto.sql.utils.ChangeTable.FCSpec;
import org.openconcerto.utils.FileUtils;
import org.openconcerto.utils.RTInterruptedException;
import org.openconcerto.utils.ThreadFactory;
46,8 → 46,8
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
58,11 → 58,11
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
 
import org.apache.commons.dbutils.ResultSetHandler;
 
import net.jcip.annotations.GuardedBy;
import net.jcip.annotations.ThreadSafe;
 
import org.apache.commons.dbutils.ResultSetHandler;
 
/**
* Allow to replicate some tables in memory.
*
104,7 → 104,7
this.singleRootName = null;
}
// private in-memory database
this.slave = new SQLServer(SQLSystem.H2, "mem", null, null, null, new IClosure<DBSystemRoot>() {
this.slave = new SQLServer(SQLSystem.H2, SQLSystem.H2_IN_MEMORY, null, null, null, new IClosure<DBSystemRoot>() {
@Override
public void executeChecked(DBSystemRoot input) {
input.setRootsToMap(tables.keySet());
/trunk/OpenConcerto/src/org/openconcerto/sql/view/IListFrame.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
31,6 → 31,7
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.function.Function;
 
import javax.swing.JFrame;
import javax.swing.event.TableModelEvent;
47,6 → 48,21
public static final String SHORT_TITLE = "org.openconcerto.listframe.shortTitle";
private static final String FILE_STRUCT_VERSION = "20160923";
 
private static Function<? super SQLElement, File> CONF_DIR_FUNCTION = (elem) -> {
final Configuration conf = Configuration.getInstance();
return conf == null ? null : conf.getConfDir();
};
 
static synchronized public final void setConfDirFunction(final Function<? super SQLElement, File> f) {
CONF_DIR_FUNCTION = f;
}
 
static synchronized public final File getConfDir(final SQLElement elem) {
if (CONF_DIR_FUNCTION == null)
return null;
return CONF_DIR_FUNCTION.apply(elem);
}
 
// windowState-20160923/IListFrame/ARTICLE.xml
static public final File getConfigFile(final SQLElement elem, final Class<? extends JFrame> c) {
return getConfigFile(elem, null, c);
59,15 → 75,18
 
static private final File getConfigFile(final SQLElement elem, final SQLComponent comp, final Class<? extends Window> c) {
final String compName = comp == null ? "" : "-" + comp.getCode();
return getConfigFile(c, elem.getCode() + compName);
return getConfigFile(getConfDir(elem), c, elem.getCode() + compName);
}
 
// windowState-20160923/WindowClass/code.xml
static public final File getConfigFile(final Class<? extends Window> c, final String code) {
final Configuration conf = Configuration.getInstance();
if (conf == null)
return getConfigFile(getConfDir(null), c, code);
}
 
static public final File getConfigFile(final File rootDir, final Class<? extends Window> c, final String code) {
if (rootDir == null)
return null;
final File structFile = new File(conf.getConfDir(), "windowState-" + FILE_STRUCT_VERSION);
final File structFile = new File(rootDir, "windowState-" + FILE_STRUCT_VERSION);
return new File(structFile, c.getSimpleName() + File.separator + getConfigFileName(code));
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/view/IListPanel.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
35,6 → 35,7
import org.openconcerto.sql.view.list.ITableModel;
import org.openconcerto.sql.view.list.RowAction;
import org.openconcerto.sql.view.list.RowAction.PredicateRowAction;
import org.openconcerto.sql.view.list.action.SQLRowValuesAction;
import org.openconcerto.sql.view.search.SearchListComponent;
import org.openconcerto.ui.ContinuousButtonModel;
import org.openconcerto.ui.FrameUtil;
69,6 → 70,7
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
109,15 → 111,18
 
static public final File getConfigFile(final SQLElement elem, final Class<? extends Container> c, final String variant) {
final String suffix = StringUtils.isEmpty(variant, true) ? "" : "-" + variant;
return getConfigFile(c, elem.getCode() + suffix);
return getConfigFile(IListFrame.getConfDir(elem), c, elem.getCode() + suffix);
}
 
static public final File getConfigFile(final Class<? extends Container> c, String code) {
final Configuration conf = Configuration.getInstance();
if (conf == null)
return getConfigFile(IListFrame.getConfDir(null), c, code);
}
 
static public final File getConfigFile(final File rootDir, final Class<? extends Container> c, String code) {
if (rootDir == null)
return null;
 
final File structFile = new File(conf.getConfDir(), "jtableState-" + FILE_STRUCT_VERSION);
final File structFile = new File(rootDir, "jtableState-" + FILE_STRUCT_VERSION);
return new File(structFile, c.getSimpleName() + File.separator + IListFrame.getConfigFileName(code));
}
 
167,6 → 172,7
private JButton buttonPlus;
private JButton buttonMoins;
protected final JPanel searchPanel = new JPanel(new GridBagLayout());
 
private static final JButton createBtn(Icon i) {
final JButton res = new JButton(i);
res.setMargin(new Insets(1, 1, 1, 1));
219,23 → 225,33
list.setConfigFile(config);
}
this.liste = list;
final IClosure<ListChangeIndex<IListeAction>> l = new IClosure<ListChangeIndex<IListeAction>>() {
final Map<SQLRowValuesAction, IListeAction> actionsMap = new IdentityHashMap<>();
final IClosure<ListChangeIndex<SQLRowValuesAction>> l = new IClosure<ListChangeIndex<SQLRowValuesAction>>() {
@Override
public void executeChecked(ListChangeIndex<IListeAction> input) {
getListe().removeIListeActions(input.getItemsRemoved());
getListe().addIListeActions(input.getItemsAdded());
public void executeChecked(ListChangeIndex<SQLRowValuesAction> input) {
SwingThreadUtils.invoke(() -> {
for (final SQLRowValuesAction a : input.getItemsRemoved()) {
final IListeAction la = actionsMap.remove(a);
if (la != null)
getListe().removeIListeAction(la);
}
actionsMap.putAll(getListe().addRowValuesActions(getElement().getRowValuesActions()));
});
}
};
// remove listener if non displayable since getElement() never dies
this.addHierarchyListener(new HierarchyListener() {
@Override
public void hierarchyChanged(HierarchyEvent e) {
if ((e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0)
if (isDisplayable()) {
getListe().addIListeActions(getElement().getRowActions());
assert actionsMap.isEmpty();
actionsMap.putAll(getListe().addRowValuesActions(getElement().getRowValuesActions()));
getElement().addRowActionsListener(l);
} else {
getElement().removeRowActionsListener(l);
getListe().removeIListeActions(getElement().getRowActions());
getListe().removeIListeActions(actionsMap.values());
actionsMap.clear();
}
}
});
785,7 → 801,7
}
 
private boolean isRO() {
final SQLRowAccessor r = getListe().getSelectedRow();
final SQLRowAccessor r = getListe().getSelectedRowAccessor();
return r != null && SQLComponent.isReadOnly(r);
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/RowMetadata.java
New file
0,0 → 1,48
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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 java.util.Date;
 
import net.jcip.annotations.Immutable;
 
@Immutable
public final class RowMetadata {
private final Integer userCreate, userModify;
private final Date creation, modification;
 
public RowMetadata(Date creation, Integer userCreate, Date modification, Integer userModify) {
super();
this.creation = creation;
this.userCreate = userCreate;
this.modification = modification;
this.userModify = userModify;
}
 
public final Integer getUserCreate() {
return this.userCreate;
}
 
public final Integer getUserModify() {
return this.userModify;
}
 
public final Date getCreation() {
return this.creation;
}
 
public final Date getModification() {
return this.modification;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/search/SearchList.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
31,7 → 31,7
 
public SearchList() {
super();
this.items = new OrderedSet<SearchSpec>();
this.items = new OrderedSet<>();
}
 
public void addSearchItem(SearchSpec item) {
42,6 → 42,7
this.items.remove(item);
}
 
@Override
public boolean isEmpty() {
for (final SearchSpec s : this.items)
if (!s.isEmpty())
49,12 → 50,13
return true;
}
 
@Override
public boolean match(Object line) {
return this.match((List) line);
return this.match((List<?>) line);
}
 
// fait un AND de tous les éléments
private boolean match(List line) {
private boolean match(List<?> line) {
// List de String, cad 1 String par colonne
 
boolean result = false;
69,6 → 71,7
return result;
}
 
@Override
public String toString() {
final StringBuffer sb = new StringBuffer(this.items.size() * 32);
sb.append("SearchList:" + this.items.size() + " items");
/trunk/OpenConcerto/src/org/openconcerto/sql/view/search/SearchSpecUtils.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
23,7 → 23,7
if (search == null || search.isEmpty())
result = l;
else {
result = new ArrayList<T>(l.size());
result = new ArrayList<>(l.size());
for (final T item : l) {
if (search.match(item))
result.add(item);
/trunk/OpenConcerto/src/org/openconcerto/sql/view/search/SearchListComponent.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
43,8 → 43,8
public SearchListComponent(ITableModel model) {
super();
this.r = null;
this.items = new ArrayList<SearchItemComponent>();
this.formats = new HashMap<Class<?>, FormatGroup>();
this.items = new ArrayList<>();
this.formats = new HashMap<>();
 
this.setLayout(new GridBagLayout());
this.c = new GridBagConstraints();
175,7 → 175,7
this.checkEDT();
 
for (int i = 0; i < this.items.size(); i++) {
SearchItemComponent component = this.items.get(i);
final SearchItemComponent component = this.items.get(i);
component.setSearchFullMode(b);
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/search/TextSearchSpec.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
55,8 → 55,8
this.mode = mode;
this.filterString = filterString;
this.normalizedFilterString = normalize(filterString);
this.formats = new HashMap<Class<?>, FormatGroup>();
this.parsedFilter = new HashMap<Format, Object>();
this.formats = new HashMap<>();
this.parsedFilter = new HashMap<>();
}
 
private String normalize(String s) {
88,7 → 88,7
private final String format(final Format fmt, final Object cell) {
try {
return fmt.format(cell);
} catch (Exception e) {
} catch (final Exception e) {
throw new IllegalStateException("Couldn't format " + cell + '(' + getClass(cell) + ") with " + fmt, e);
}
}
101,7 → 101,7
if (!this.parsedFilterD_tried) {
try {
this.parsedFilterD = Double.valueOf(this.filterString);
} catch (NumberFormatException e) {
} catch (final NumberFormatException e) {
this.parsedFilterD = null;
}
this.parsedFilterD_tried = true;
/trunk/OpenConcerto/src/org/openconcerto/sql/view/search/SearchItemComponent.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
52,6 → 52,7
import javax.swing.event.DocumentEvent;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.TableModel;
import javax.swing.text.BadLocationException;
 
import net.jcip.annotations.Immutable;
89,30 → 90,38
}
}
 
private JTextField textFieldRecherche = new JTextField(10);
private final JComboBox comboColonnePourRecherche;
private final JComboBox searchMode;
private JCheckBox invertSearch = new JCheckBox(TM.tr("toReverse"));
private JButton buttonAdd = new JButton("+");
private JButton buttonRemove = new JButton();
private final JTextField textFieldRecherche = new JTextField(10);
private final JComboBox<Column> comboColonnePourRecherche;
private final JComboBox<String> searchMode;
private final JCheckBox invertSearch = new JCheckBox(TM.tr("toReverse"));
private final JButton buttonAdd = new JButton("+");
private final JButton buttonRemove = new JButton();
final SearchListComponent list;
// final to ease removing listener, SearchListComponent.reset() removes every item
// when the TableModel changes.
private final TableModel tableModel;
private String text = "";
 
public SearchItemComponent(final SearchListComponent list) {
super();
this.list = list;
this.tableModel = this.list.getTableModel();
this.setOpaque(false);
// Initialisation de l'interface graphique
this.searchMode = new JComboBox(new String[] { TM.tr("contains"), TM.tr("contains.exactly"), TM.tr("isLessThan"), TM.tr("isEqualTo"), TM.tr("isExactlyEqualTo"), TM.tr("isGreaterThan"),
TM.tr("isEmpty") });
final ListComboBoxModel comboModel = new ListComboBoxModel(Arrays.asList(TOUT));
this.searchMode = new JComboBox<>(
new String[] { TM.tr("contains"), TM.tr("contains.exactly"), TM.tr("isLessThan"), TM.tr("isEqualTo"), TM.tr("isExactlyEqualTo"), TM.tr("isGreaterThan"), TM.tr("isEmpty") });
final ListComboBoxModel<Column> comboModel = new ListComboBoxModel<>(Arrays.asList(TOUT));
comboModel.setSelectOnAdd(false);
// allow getColIndex() and thus getSearchItem() to work from now on
assert comboModel.getSelectedItem() != null;
this.comboColonnePourRecherche = new JComboBox(comboModel);
this.comboColonnePourRecherche = new JComboBox<>(comboModel);
uiInit();
}
 
final TableModel getTableModel() {
return this.tableModel;
}
 
private void uiInit() {
this.setLayout(new GridBagLayout());
final GridBagConstraints c = new GridBagConstraints();
123,11 → 132,12
 
// designation
// don't just use DefaultListCellRenderer, it fails on some l&f
final ListCellRenderer old = this.comboColonnePourRecherche.getRenderer();
this.comboColonnePourRecherche.setRenderer(new ListCellRenderer() {
@SuppressWarnings("unchecked")
final ListCellRenderer<Object> old = (ListCellRenderer<Object>) this.comboColonnePourRecherche.getRenderer();
this.comboColonnePourRecherche.setRenderer(new ListCellRenderer<Column>() {
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
return old.getListCellRendererComponent(list, ((Column) value).getLabel(), index, isSelected, cellHasFocus);
public Component getListCellRendererComponent(JList<? extends Column> list, Column value, int index, boolean isSelected, boolean cellHasFocus) {
return old.getListCellRendererComponent(list, value.getLabel(), index, isSelected, cellHasFocus);
}
});
// hand tuned for a IListPanel width of 1024px
171,11 → 181,13
initInvertSearch();
 
this.buttonAdd.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SearchItemComponent.this.list.addNewSearchItem();
}
});
this.buttonRemove.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SearchItemComponent.this.list.removeSearchItem(SearchItemComponent.this);
}
184,6 → 196,7
 
private void initInvertSearch() {
this.invertSearch.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
updateSearchList();
}
192,12 → 205,13
 
private void initSearchText() {
this.textFieldRecherche.getDocument().addDocumentListener(new SimpleDocumentListener() {
@Override
public void update(DocumentEvent e) {
try {
// One ne peut pas appeler chercher() car le texte n'est pas encore a jour
SearchItemComponent.this.text = e.getDocument().getText(0, e.getDocument().getLength()).trim();
updateSearchList();
} catch (BadLocationException exn) {
} catch (final BadLocationException exn) {
// impossible
exn.printStackTrace();
}
207,6 → 221,7
 
private void initCombo() {
final ItemListener listener = new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
updateSearchList();
}
223,13 → 238,14
};
// allow the TableModel to die
this.addHierarchyListener(new HierarchyListener() {
@Override
public void hierarchyChanged(HierarchyEvent e) {
if ((e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0) {
if (e.getChanged().isDisplayable()) {
columnsChanged(listener);
SearchItemComponent.this.list.getTableModel().addTableModelListener(tableModelL);
SearchItemComponent.this.getTableModel().addTableModelListener(tableModelL);
} else {
SearchItemComponent.this.list.getTableModel().removeTableModelListener(tableModelL);
SearchItemComponent.this.getTableModel().removeTableModelListener(tableModelL);
}
}
}
244,7 → 260,7
 
private void fillColumnCombo(ItemListener listener) {
// sort column names alphabetically
final int columnCount = this.list.getTableModel().getColumnCount();
final int columnCount = this.getTableModel().getColumnCount();
final String[][] names = new String[columnCount][];
final int[] indexes = new int[columnCount];
for (int i = 0; i < columnCount; i++) {
253,7 → 269,7
}
// use column index as columns names are not unique
final SortedMap<String, Integer> map = solve(names, indexes);
final List<Column> cols = new ArrayList<Column>(columnCount);
final List<Column> cols = new ArrayList<>(columnCount);
cols.add(TOUT);
for (final Entry<String, Integer> e : map.entrySet()) {
final int colIndex = e.getValue().intValue();
263,7 → 279,7
 
// don't fire when filling, we will fire when selecting
this.comboColonnePourRecherche.removeItemListener(listener);
final ListComboBoxModel comboModel = (ListComboBoxModel) this.comboColonnePourRecherche.getModel();
final ListComboBoxModel<Column> comboModel = (ListComboBoxModel<Column>) this.comboColonnePourRecherche.getModel();
assert !comboModel.isSelectOnAdd() : "Otherwise our following select might not fire";
comboModel.removeAllElements();
comboModel.addAll(cols);
273,7 → 289,7
private void columnsChanged(final ItemListener listener) {
final String currentID = ((Column) this.comboColonnePourRecherche.getSelectedItem()).getID();
fillColumnCombo(listener);
final ListComboBoxModel comboModel = (ListComboBoxModel) this.comboColonnePourRecherche.getModel();
final ListComboBoxModel<Column> comboModel = (ListComboBoxModel<Column>) this.comboColonnePourRecherche.getModel();
// no selection since the model was just emptied
assert this.comboColonnePourRecherche.getSelectedIndex() == -1 && this.comboColonnePourRecherche.getSelectedItem() == null;
// try to reselect the same column if it's still there
296,7 → 312,7
private SortedMap<String, Integer> solve(final String[][] names, final int[] indexes) {
final int columnCount = names.length;
// columns' index by name
final ListMap<String, Integer> collisions = new ListMap<String, Integer>(columnCount);
final ListMap<String, Integer> collisions = new ListMap<>(columnCount);
for (int i = 0; i < columnCount; i++) {
final int index = indexes[i];
if (index >= names[i].length)
304,8 → 320,8
final String columnName = names[i][index];
collisions.add(columnName, i);
}
final SortedMap<String, Integer> res = new TreeMap<String, Integer>();
for (Entry<String, ? extends Collection<Integer>> e : collisions.entrySet()) {
final SortedMap<String, Integer> res = new TreeMap<>();
for (final Entry<String, ? extends Collection<Integer>> e : collisions.entrySet()) {
final Collection<Integer> indexesWithCollision = e.getValue();
if (indexesWithCollision.size() > 1) {
// increment only the minimum indexes to try to solve the conflict with the lowest
/trunk/OpenConcerto/src/org/openconcerto/sql/view/search/DateSearchSpec.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
18,10 → 18,10
 
public class DateSearchSpec implements SearchSpec {
// include or exclude filterString
private boolean excludeFilterString;
private int columnIndex;
private Date fromDate;
private Date toDate;
private final boolean excludeFilterString;
private final int columnIndex;
private final Date fromDate;
private final Date toDate;
 
public DateSearchSpec(Date from, Date to, int columnIndex) {
this(false, from, to, columnIndex);
44,8 → 44,7
* @return <code>true</code> si la ligne contient.
*/
static private boolean contains(Object line, Date startDate, Date stopDate, int index) {
 
List list = (List) line;
final List<?> list = (List<?>) line;
final int start;
final int stop;
if (index < 0) {
62,7 → 61,7
final Object cell = list.get(i);
if (cell != null) {
if (cell instanceof Date) {
Date date = (Date) cell;
final Date date = (Date) cell;
if (date.after(startDate) && date.before(stopDate)) {
return true;
}
75,6 → 74,7
return false;
}
 
@Override
public boolean match(Object line) {
return this.excludeFilterString ^ contains(line, this.fromDate, this.toDate, this.columnIndex);
}
83,6 → 83,7
System.out.println(this.excludeFilterString + ":" + this.fromDate + "->" + this.toDate + " col:" + this.columnIndex);
}
 
@Override
public boolean isEmpty() {
return true;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelLinesSource.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
24,6 → 24,7
import java.beans.PropertyChangeListener;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Future;
69,9 → 70,20
return this.getModel().getUpdateQ().getState().getReq();
}
 
public abstract List<ListSQLLine> getAll();
public final List<ListSQLLine> getAll() {
return this.get(null);
}
 
/**
* Fetch up to date values from the DB.
*
* @param ids which rows to fetch, <code>null</code> meaning all.
* @return the new values from the DB, some changes in the DB might be ignored if there's
* pending changes in this.
*/
public abstract List<ListSQLLine> get(final Collection<? extends Number> ids);
 
/**
* A row in the DB has been changed, fetch its current value.
*
* @param id a valid ID of a database row.
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/RowAction.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
15,6 → 15,7
 
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.view.list.action.ListEvent;
import org.openconcerto.utils.cc.IClosure;
import org.openconcerto.utils.cc.IPredicate;
import org.openconcerto.utils.i18n.TranslationManager;
50,7 → 51,7
}
 
public static class PredicateRowAction extends RowAction {
private IPredicate<? super IListeEvent> pred = null;
private IPredicate<? super ListEvent> pred = null;
 
public PredicateRowAction(Action action, boolean header) {
super(action, header);
68,7 → 69,7
super(action, header, popupMenu, id);
}
 
public final PredicateRowAction setPredicate(IPredicate<? super IListeEvent> pred) {
public final PredicateRowAction setPredicate(IPredicate<? super ListEvent> pred) {
if (pred == null) {
throw new IllegalArgumentException("null predicate");
}
81,7 → 82,7
}
 
@Override
public boolean enabledFor(IListeEvent evt) {
public boolean enabledFor(ListEvent evt) {
if (this.pred == null) {
throw new IllegalStateException("No predicate for " + this);
}
148,7 → 149,7
}
 
public boolean enabledFor(List<SQLRowValues> selection) {
throw new UnsupportedOperationException("Should overload this method or enabledFor(IListeEvent)");
throw new UnsupportedOperationException("Should overload this method or enabledFor(IListeEvent) on : " + this);
}
 
/**
157,7 → 158,7
* @param evt the state of the IListe.
* @return <code>true</code> if the action can be performed.
*/
public boolean enabledFor(IListeEvent evt) {
public boolean enabledFor(ListEvent evt) {
return this.enabledFor(evt.getSelectedRows());
}
 
172,7 → 173,7
}
 
@Override
public Action getDefaultAction(IListeEvent evt) {
public Action getDefaultAction(ListEvent evt) {
return this.enabledFor(evt) ? this.getAction() : null;
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelSourceOnline.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
14,6 → 14,7
package org.openconcerto.sql.view.list;
 
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.request.ComboSQLRequest.KeepMode;
import org.openconcerto.sql.request.ListSQLRequest;
 
/**
24,15 → 25,30
*/
public class SQLTableModelSourceOnline extends SQLTableModelSource {
 
private final KeepMode keepMode;
 
public SQLTableModelSourceOnline(final ListSQLRequest req, final SQLElement elem) {
this(req, elem, KeepMode.GRAPH);
}
 
public SQLTableModelSourceOnline(final ListSQLRequest req, final SQLElement elem, final KeepMode keepMode) {
super(req, elem);
if (keepMode == null || keepMode == KeepMode.NONE)
throw new IllegalArgumentException("Invalid mode : " + keepMode);
this.keepMode = keepMode;
}
 
public SQLTableModelSourceOnline(SQLTableModelSourceOnline src) {
super(src);
this.keepMode = src.keepMode;
}
 
@Override
public final KeepMode getKeepMode() {
return this.keepMode;
}
 
@Override
protected boolean allowBiggerGraph() {
return true;
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/RowValuesTableModel.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
144,6 → 144,10
this.requiredFields.add(f);
}
 
public SQLField getValidationField() {
return this.validationField;
}
 
public SQLRowValues getDefaultRowValues() {
return this.defautRow;
}
233,8 → 237,10
}
rowVal.put(fieldName, value);
for (SQLTableElement sqlTableElem : this.list) {
if (sqlTableElem.getField().getName().equals(fieldName)) {
sqlTableElem.fireModification(rowVal);
}
}
fireTableModelModified(rowIndex);
}
 
707,7 → 713,7
checkEDT();
final List<SQLRowValues> rows = new ArrayList<SQLRowValues>(1);
rows.add(row);
addRows(rows, true);
addRows(rows, fireModified);
}
 
public void submit(Runnable r) {
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/ITableModel.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
20,7 → 20,6
import org.openconcerto.sql.Log;
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.users.rights.TableAllRights;
import org.openconcerto.sql.users.rights.UserRights;
492,7 → 491,7
final ListSQLLine line = getRow(rowIndex);
if (!line.getSrc().isCellEditable(line, columnIndex, col))
return true;
final SQLRowValues r = line.getRow();
final SQLRowAccessor r = line.getRowAccessor();
return r.getTable().contains(SQLComponent.READ_ONLY_FIELD) && SQLComponent.isReadOnly(r);
}
 
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/search/SearchQueue.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
65,9 → 65,14
* exist.
*/
public static String getLastReferentField(final Path p) {
final Step lastRefStep = getLastReferentStep(p);
return lastRefStep == null ? null : lastRefStep.getSingleField().getName();
}
 
public static Step getLastReferentStep(final Path p) {
final Step lastStep = p.length() == 0 ? null : p.getStep(-1);
final boolean lastIsForeign = lastStep == null || lastStep.getDirection() == Direction.FOREIGN;
return lastIsForeign ? null : lastStep.getSingleField().getName();
return lastIsForeign ? null : lastStep;
}
 
private final ITableModel model;
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/IListeAction.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
15,9 → 15,8
 
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.view.list.action.ListEvent;
import org.openconcerto.ui.list.selection.ListSelection;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.cc.IPredicate;
 
import java.util.Collections;
36,79 → 35,19
*/
public interface IListeAction {
 
static public class IListeEvent {
static public class IListeEvent extends ListEvent {
 
static private final IPredicate<IListeEvent> emptyTotalRowCountPredicate = createTotalRowCountPredicate(0, 0);
 
static public final IPredicate<IListeEvent> getEmptyListPredicate() {
return emptyTotalRowCountPredicate;
}
 
static public final IPredicate<IListeEvent> createTotalRowCountPredicate(final int min, final int max) {
return new IPredicate<IListeEvent>() {
@Override
public boolean evaluateChecked(IListeEvent e) {
return e.getTotalRowCount() >= min && e.getTotalRowCount() <= max;
}
};
}
 
static private final IPredicate<IListeEvent> singleSelectionPredicate = createSelectionCountPredicate(1, 1);
static private final IPredicate<IListeEvent> nonEmptySelectionPredicate = createNonEmptySelectionPredicate(Integer.MAX_VALUE);
 
static public final IPredicate<IListeEvent> getSingleSelectionPredicate() {
return singleSelectionPredicate;
}
 
static public final IPredicate<IListeEvent> getNonEmptySelectionPredicate() {
return nonEmptySelectionPredicate;
}
 
static public final IPredicate<IListeEvent> createNonEmptySelectionPredicate(final int max) {
return createSelectionCountPredicate(1, max);
}
 
static public final IPredicate<IListeEvent> createSelectionCountPredicate(final int min, final int max) {
return new IPredicate<IListeEvent>() {
@Override
public boolean evaluateChecked(IListeEvent e) {
// this is the fastest since it involves no object creation
final List<?> selectedIDs = e.getSelectedRows();
return selectedIDs.size() >= min && selectedIDs.size() <= max;
}
};
}
 
private final IListe list;
private final List<SQLRowValues> selection;
 
IListeEvent(final IListe list) {
super();
IListeEvent(final IListe list, final List<SQLRowValues> selection, final List<? extends SQLRowAccessor> selectionAccessor) {
super(list, list.getSource().getElem(), list.getTotalRowCount(), selection, selectionAccessor);
this.list = list;
// this create instances so cache it
this.selection = list.getSelectedRows();
}
 
public final SQLRowAccessor getSelectedRow() {
return CollectionUtils.getFirst(this.getSelectedRows());
}
 
public final List<SQLRowValues> getSelectedRows() {
return this.selection;
}
 
public final int getTotalRowCount() {
return this.list.getTotalRowCount();
}
 
public final ListSelection getSelection() {
return this.list.getSelection();
}
 
public final SQLTable getTable() {
return this.list.getSource().getPrimaryTable();
}
}
 
/**
* Allow to build a list of buttons and when to enable/disable them.
180,8 → 119,8
 
private final boolean clickOnRows;
 
PopupEvent(final IListe list, final boolean clickOnRows) {
super(list);
PopupEvent(final IListe list, final List<SQLRowValues> selection, final List<? extends SQLRowAccessor> selectionAccessor, final boolean clickOnRows) {
super(list, selection, selectionAccessor);
this.clickOnRows = clickOnRows;
}
 
285,7 → 224,7
* @param evt the state of the <code>IListe</code>.
* @return the default action to perform, can be <code>null</code>.
*/
Action getDefaultAction(IListeEvent evt);
Action getDefaultAction(ListEvent evt);
 
// never null
PopupBuilder getPopupContent(PopupEvent evt);
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/ListSQLLine.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
14,12 → 14,16
package org.openconcerto.sql.view.list;
 
import org.openconcerto.sql.model.FieldPath;
import org.openconcerto.sql.model.RowRef;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValues.CreateMode;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTable.VirtualFields;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.request.BaseFillSQLRequest.OrderValue;
import org.openconcerto.sql.request.ComboSQLRequest.KeepMode;
import org.openconcerto.sql.view.list.search.SearchQueue;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.NumberUtils;
68,7 → 72,10
private final SQLTableModelLinesSource src;
// unmodifiable
@GuardedBy("this")
private SQLRowValues row;
private SQLRowAccessor row;
// If the whole graph of this.row isn't kept then we need to store the primary keys so that when
// a SQLTableEvent is fired, we can know which lines is affected.
private final Set<RowRef> pks;
// Immutable
private final SQLTableModelSourceState state;
private final int id;
75,6 → 82,9
// allow to order by something not in the row
@GuardedBy("this")
private Number order;
// If the whole graph of this.row isn't kept then we need to store rows needed to order the
// lines. Otherwise don't allocate more memory just use this.rows.
private final OrderValue reqOrderVal;
// lists are accessed by Swing (model.getValueAt()) and
// by the search queue (SearchRunnable#matchFilter(ListSQLLine line))
// immutable
87,8 → 97,24
this.setRow(row);
this.id = id;
this.state = state;
final boolean isGraph = src.getParent().getKeepMode() == KeepMode.GRAPH;
this.reqOrderVal = isGraph ? null : state.getReq().createOrderValue(row);
if (isGraph) {
this.clearCache();
this.pks = null;
} else {
// compute all cells,
this.list = Collections.emptyList();
this.loadCache(state.getAllColumns().getAllColumns().size());
// then free memory by keeping a single SQLRow and only RowIDRef
this.setRow(row.asRow());
final Set<RowRef> tmpRefs = new HashSet<>(row.getGraphSize(), 1.0f);
for (final SQLRowValues v : row.getGraph().getItems()) {
tmpRefs.add(v.getRowRef());
}
this.pks = Collections.unmodifiableSet(tmpRefs);
}
}
 
// load at least columnCount values
// (to avoid loading debug columns, which took more time than the regular columns, ie more than
109,7 → 135,7
return this.src;
}
 
private final void setRow(SQLRowValues v) {
private final void setRow(SQLRowAccessor v) {
if (!v.isFrozen())
throw new IllegalArgumentException("Not frozen : " + v);
synchronized (this) {
117,10 → 143,18
}
}
 
public synchronized final SQLRowValues getRow() {
public final SQLRowValues getRow() {
return (SQLRowValues) this.getRowAccessor();
}
 
public synchronized final SQLRowAccessor getRowAccessor() {
return this.row;
}
 
public final Set<RowRef> getPKs() {
return this.pks;
}
 
@Override
public int compareTo(ListSQLLine o) {
if (this.src != o.src)
148,9 → 182,18
} else {
if (order2 != null)
throw new IllegalStateException("Order mismatch :\n" + order1 + " for " + l1 + " not coherent with\n" + order2 + " for " + l2);
 
final OrderValue orderVal1 = l1.getRequestOrderValue();
if (orderVal1 != null) {
final OrderValue orderVal2 = l2.getRequestOrderValue();
return orderVal1.compareTo(orderVal2);
} else {
// assert because we already checked same state above
assert l2.getRequestOrderValue() == null;
return l1.getState().getReq().order(l1.getRow(), l2.getRow());
}
}
}
 
public int getID() {
return this.id;
164,6 → 207,10
return this.order;
}
 
public final OrderValue getRequestOrderValue() {
return this.reqOrderVal;
}
 
public synchronized List<Object> getList(int columnCount) {
this.loadCache(columnCount);
return this.list;
220,6 → 267,8
}
 
public void clearCache() {
if (this.getSrc().getParent().getKeepMode() != KeepMode.GRAPH)
throw new IllegalStateException("Wouldn't be able to compute cell values");
synchronized (this) {
this.list = Collections.emptyList();
}
329,6 → 378,6
 
@Override
public String toString() {
return this.getClass().getSimpleName() + " on " + this.getRow();
return this.getClass().getSimpleName() + " on " + this.getRowAccessor();
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/ChangeAllRunnable.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelLinesSourceOffline.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
31,6 → 31,7
 
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
231,8 → 232,10
* @return the new lines.
*/
@Override
public List<ListSQLLine> getAll() {
public List<ListSQLLine> get(Collection<? extends Number> ids) {
assert isUpdateThread();
if (ids != null)
throw new UnsupportedOperationException("Refreshing a subset of rows is not yet supported");
final List<SQLRowValues> dbRows = this.fetch();
 
if (this.lines.isEmpty()) {
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelLinesSourceOnline.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
23,6 → 23,7
 
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
75,8 → 76,8
}
 
@Override
public List<ListSQLLine> getAll() {
final List<SQLRowValues> values = this.getUpdateQueueReq().getValues();
public List<ListSQLLine> get(Collection<? extends Number> ids) {
final List<SQLRowValues> values = this.getUpdateQueueReq().getValuesFromIDs(ids);
final List<ListSQLLine> res = new ArrayList<ListSQLLine>(values.size());
for (final SQLRowValues v : values) {
final ListSQLLine newLine = createLine(v);
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelSource.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
25,6 → 25,7
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.request.BaseFillSQLRequest;
import org.openconcerto.sql.request.ComboSQLRequest.KeepMode;
import org.openconcerto.sql.request.ListSQLRequest;
import org.openconcerto.utils.cc.IClosure;
import org.openconcerto.utils.change.ListChangeIndex;
118,6 → 119,8
return this.elem;
}
 
protected abstract KeepMode getKeepMode();
 
// lazy initialization since this method calls colsChanged() which subclasses overload and
// they need their own attribute that aren't set yet since super() must be the first statement.
public void init() {
127,6 → 130,7
 
final SQLRowValues graph = this.inited;
 
final boolean moreDebugCols = this.getKeepMode() == KeepMode.GRAPH;
graph.walkFields(new IClosure<FieldPath>() {
@Override
public void executeChecked(final FieldPath input) {
134,7 → 138,7
if (f.getTable().getLocalContentFields().contains(f)) {
final SQLTableModelColumnPath col = new SQLTableModelColumnPath(input, null, getElem().getDirectory());
SQLTableModelSource.this.cols.add(col);
} else
} else if (moreDebugCols) {
SQLTableModelSource.this.debugCols.add(new SQLTableModelColumnPath(input.getPath(), f.getName(), f.toString()) {
// don't show the rowValues since it's very verbose (and all content fields
// are already displayed as normal columns) and unsortable
145,8 → 149,10
}
});
}
}
}, true);
 
if (moreDebugCols)
this.debugCols.add(new DebugRow(getPrimaryTable()));
final SQLField orderField = getPrimaryTable().getOrderField();
if (orderField != null)
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/MoveQueue.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
82,7 → 82,7
SQLUtils.executeAtomic(getTable().getDBSystemRoot().getDataSource(), new ConnectionHandlerNoSetup<Object, Exception>() {
@Override
public Object handle(SQLDataSource ds) throws Exception {
moveQuick(rows, after, destID.get().getRow().asRow());
moveQuick(rows, after, destID.get().getRowAccessor().asRow());
return null;
}
});
107,7 → 107,7
final int rowCount = this.tableModel.getRowCount();
final boolean after = rowIndex >= rowCount;
final int index = after ? rowCount - 1 : rowIndex;
final SQLRowValues line = this.tableModel.getRow(index).getRow();
final SQLRowAccessor line = this.tableModel.getRow(index).getRowAccessor();
assert line.isFrozen() : "row could change by the time move() is called";
 
return this.put(new Runnable() {
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/UpdateOneRunnable.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
14,6 → 14,7
package org.openconcerto.sql.view.list;
 
import org.openconcerto.sql.model.SQLTableEvent;
import org.openconcerto.sql.request.ComboSQLRequest.KeepMode;
import org.openconcerto.utils.Value;
 
import java.util.Collection;
28,7 → 29,9
 
@Override
public void run() {
if (this.getTable() == this.getReq().getParent().getPrimaryTable()) {
final boolean isPrimaryTable = this.getTable() == this.getReq().getParent().getPrimaryTable();
if (this.getReq().getParent().getKeepMode() == KeepMode.GRAPH) {
if (isPrimaryTable) {
final Value<ListSQLLine> val = this.getReq().get(this.getID());
if (!val.hasValue())
return;
37,7 → 40,10
// eg CONTACT[3] has changed
updateLines(this.getAffectedPaths());
}
} else {
this.updateLines(this.getUpdateQ().getAffectedLines(this.getRow()), isPrimaryTable ? this.getID() : null);
}
}
 
@Override
protected Collection<String> getModifedFields() {
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/SQLTableModelSourceOffline.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
14,6 → 14,7
package org.openconcerto.sql.view.list;
 
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.request.ComboSQLRequest.KeepMode;
import org.openconcerto.sql.request.ListSQLRequest;
 
/**
37,4 → 38,9
protected SQLTableModelLinesSourceOffline _createLinesSource(final ITableModel model) {
return new SQLTableModelLinesSourceOffline(this, model);
}
 
@Override
public final KeepMode getKeepMode() {
return KeepMode.GRAPH;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/RowValuesTableControlPanel.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
14,6 → 14,7
package org.openconcerto.sql.view.list;
 
import org.openconcerto.sql.TM;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.view.IListFrame;
import org.openconcerto.ui.DefaultGridBagConstraints;
130,7 → 131,20
if (cellEditor != null) {
cellEditor.cancelCellEditing();
}
SQLField validationField = RowValuesTableControlPanel.this.model.getValidationField();
if (validationField != null) {
boolean canDelete = true;
for (int i : table.getSelectedRows()) {
SQLRowValues rowVals = RowValuesTableControlPanel.this.model.getRowValuesAt(i);
canDelete &= (!rowVals.contains(validationField.getName()) || rowVals.getObject(validationField.getName()) == null || !rowVals.getBoolean(validationField.getName()));
}
if (canDelete) {
RowValuesTableControlPanel.this.model.removeRowsAt(table.getSelectedRows());
}
// MAYBE show popup if can't delete
} else {
RowValuesTableControlPanel.this.model.removeRowsAt(table.getSelectedRows());
}
table.clearSelection();
}
});
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/UpdateQueue.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
14,9 → 14,10
package org.openconcerto.sql.view.list;
 
import org.openconcerto.sql.Log;
import org.openconcerto.sql.model.RowRef;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValues.CreateMode;
import org.openconcerto.sql.model.SQLRowValuesCluster.State;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.SQLTableEvent;
23,6 → 24,7
import org.openconcerto.sql.model.SQLTableModifiedListener;
import org.openconcerto.sql.model.graph.Link.Direction;
import org.openconcerto.sql.model.graph.Path;
import org.openconcerto.sql.request.ComboSQLRequest.KeepMode;
import org.openconcerto.sql.view.list.UpdateRunnable.RmAllRunnable;
import org.openconcerto.sql.view.list.search.SearchOne;
import org.openconcerto.sql.view.list.search.SearchOne.Mode;
47,6 → 49,7
import java.util.List;
import java.util.Set;
import java.util.concurrent.RunnableFuture;
import java.util.function.BiConsumer;
import java.util.logging.Level;
 
import net.jcip.annotations.GuardedBy;
199,21 → 202,25
}
 
/**
* The lines and their path affected by a change of the passed row.
* The lines affected by a change of the passed row.
*
* @param r the row that has changed.
* @return the refreshed lines and their changed paths.
* @return the refreshed lines.
*/
protected final ListMap<ListSQLLine, Path> getAffectedLines(final SQLRow r) {
return this.getAffected(r, new ListMap<ListSQLLine, Path>(), true);
protected final Set<Integer> getAffectedLines(final SQLRow r) {
final Set<Integer> res = new HashSet<>();
this.getAffected(r, (p, l) -> res.add(l.getID()));
return res;
}
 
protected final ListMap<Path, ListSQLLine> getAffectedPaths(final SQLRow r) {
return this.getAffected(r, new ListMap<Path, ListSQLLine>(), false);
final ListMap<Path, ListSQLLine> res = new ListMap<>();
this.getAffected(r, res::add);
return res;
}
 
// must be called from within this queue, as this method use fullList
private <K, V> ListMap<K, V> getAffected(final SQLRow r, final ListMap<K, V> res, final boolean byLine) {
private void getAffected(final SQLRow r, final BiConsumer<Path, ListSQLLine> cons) {
final List<ListSQLLine> fullList = this.getFullList();
synchronized (fullList) {
final SQLTable t = r.getTable();
221,6 → 228,7
if (id < SQLRow.MIN_VALID_ID)
throw new IllegalArgumentException("invalid ID: " + id);
if (!fullList.isEmpty()) {
final boolean keepGraph = this.getModel().getReq().getKeepMode() == KeepMode.GRAPH;
final SQLRowValues proto = this.getState().getReq().getGraphToFetch();
final List<Path> pathsToT = new ArrayList<Path>();
proto.getGraph().walk(proto, pathsToT, new ITransformer<State<List<Path>>, List<Path>>() {
232,46 → 240,64
return input.getAcc();
}
}, RecursionType.BREADTH_FIRST, Direction.ANY);
// paths aren't stored when !keepGraph
if (!keepGraph) {
final RowRef idRef = r.getRowRef();
for (final ListSQLLine line : fullList) {
if (line.getPKs().contains(idRef))
cons.accept(null, line);
}
}
for (final Path p : pathsToT) {
final String lastReferentField = SearchQueue.getLastReferentField(p);
final RowRef foreignRef;
if (lastReferentField != null && r.exists()) {
final SQLRowAccessor foreign = r.getNonEmptyForeign(lastReferentField);
foreignRef = foreign != null ? foreign.getRowRef() : null;
} else {
foreignRef = null;
}
// fullList already checked above, so if there's no foreignRef, there's nothing
// to do
if (!keepGraph && foreignRef == null)
continue;
for (final ListSQLLine line : fullList) {
boolean put = false;
for (final SQLRowValues current : line.getRow().followPath(p, CreateMode.CREATE_NONE, false)) {
if (keepGraph) {
for (final SQLRowValues current : line.getRow().getDistantRows(p)) {
// works for rowValues w/o any ID
if (current != null && current.getID() == id) {
put = true;
}
}
// if the modified row isn't in the existing line, it might still affect it
// if it's a referent row insertion
if (!put && lastReferentField != null && r.exists() && !r.isForeignEmpty(lastReferentField)) {
// no NPE, even without an undefined ID since we tested isForeignEmpty()
final int foreignID = r.getInt(lastReferentField);
for (final SQLRowValues current : line.getRow().followPath(p.minusLast(), CreateMode.CREATE_NONE, false)) {
if (current.getID() == foreignID) {
}
/*
* If the modified row isn't in the existing line, it might still affect it
* if it's a referent row insertion (a referent row update or deletion would
* mean than the current line contains the modified row and so "put" would
* already be true).
*/
if (!put && foreignRef != null) {
if (keepGraph) {
for (final SQLRowValues current : line.getRow().getDistantRows(p.minusLast())) {
if (current.getID() == foreignRef.getID().intValue()) {
put = true;
}
}
} else {
put = line.getPKs().contains(foreignRef);
}
}
if (put) {
// add to the list of paths that have been refreshed
add(byLine, res, p, line);
cons.accept(p, line);
}
}
}
}
}
return res;
}
 
@SuppressWarnings("unchecked")
<V, K> void add(boolean byLine, ListMap<K, V> res, final Path p, final ListSQLLine line) {
if (byLine)
res.add((K) line, (V) p);
else
res.add((K) p, (V) line);
}
 
final void setFullList(final List<ListSQLLine> tmp, final SQLTableModelColumns cols) {
final List<ListSQLLine> fullList = this.getFullList();
synchronized (fullList) {
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/IListe.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
20,6 → 20,7
import org.openconcerto.sql.element.SQLComponent;
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.element.SQLElementDirectory;
import org.openconcerto.sql.model.RowRef;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
26,6 → 27,7
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.ComboSQLRequest.KeepMode;
import org.openconcerto.sql.request.ListSQLRequest;
import org.openconcerto.sql.request.UpdateBuilder;
import org.openconcerto.sql.users.User;
33,11 → 35,15
import org.openconcerto.sql.users.rights.TableAllRights;
import org.openconcerto.sql.view.FileTransfertHandler;
import org.openconcerto.sql.view.IListener;
import org.openconcerto.sql.view.RowMetadata;
import org.openconcerto.sql.view.RowMetadataCache;
import org.openconcerto.sql.view.list.IListeAction.ButtonsBuilder;
import org.openconcerto.sql.view.list.IListeAction.IListeEvent;
import org.openconcerto.sql.view.list.IListeAction.PopupBuilder;
import org.openconcerto.sql.view.list.IListeAction.PopupEvent;
import org.openconcerto.sql.view.list.RowAction.PredicateRowAction;
import org.openconcerto.sql.view.list.action.ListEvent;
import org.openconcerto.sql.view.list.action.SQLRowValuesAction;
import org.openconcerto.ui.FontUtils;
import org.openconcerto.ui.FormatEditor;
import org.openconcerto.ui.MenuUtils;
91,7 → 97,6
import java.text.DateFormat;
import java.text.Format;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
98,6 → 103,7
import java.util.EventObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
105,6 → 111,7
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.function.BiFunction;
 
import javax.swing.AbstractAction;
import javax.swing.Action;
146,16 → 153,13
*/
public final class IListe extends JPanel {
 
static private final class LockAction extends RowAction {
static private final class LockAction extends SQLRowValuesAction {
private final boolean lock;
 
public LockAction(final boolean lock) {
super(new AbstractAction(TM.tr(lock ? "ilist.lockRows" : "ilist.unlockRows")) {
@Override
public void actionPerformed(ActionEvent e) {
final IListe list = IListe.get(e);
final List<Integer> ids = list.getSelection().getSelectedIDs();
final SQLTable t = list.getSource().getPrimaryTable();
super(false, true, (e) -> {
final List<Number> ids = e.getSelectedIDs();
final SQLTable t = e.getTable();
final UpdateBuilder update = new UpdateBuilder(t);
update.setObject(SQLComponent.READ_ONLY_FIELD, lock ? SQLComponent.READ_ONLY_VALUE : SQLComponent.READ_WRITE_VALUE);
final User user = UserManager.getUser();
168,15 → 172,15
final Collection<? extends Number> fireIDs = ids.size() < 12 ? ids : Collections.singleton(SQLRow.NONEXISTANT_ID);
for (final Number fireID : fireIDs)
t.fireTableModified(fireID.intValue(), update.getFieldsNames());
}
}, false, true);
});
this.setName(TM.tr(lock ? "ilist.lockRows" : "ilist.unlockRows"));
this.lock = lock;
}
 
@Override
public boolean enabledFor(IListeEvent evt) {
public boolean enabledFor(ListEvent evt) {
boolean hasRight = TableAllRights.currentUserHasRight(this.lock ? TableAllRights.USER_UI_LOCK_ROW : TableAllRights.USER_UI_UNLOCK_ROW, evt.getTable());
return !evt.getSelectedRows().isEmpty() && hasRight;
return !evt.getSelectedRowAccessors().isEmpty() && hasRight;
}
}
 
199,6 → 203,9
return UNLOCK_ACTION;
}
 
private static final int MD_BATCH_SIZE = 100;
private static final RowMetadataCache MD_CACHE = new RowMetadataCache(120, 5000, IListe.class.getName());
 
/**
* When this system property is set, table {@link JTableStateManager state} is never read nor
* written. I.e. the user can change the table state but it will be reset at each launch.
333,6 → 340,11
 
this.sorter = new TableSorter();
this.jTable = new JTable(this.sorter) {
 
// By default the tooltip doesn't follow the mouse if the string remains the same
// (probably for performance reasons)
private final boolean followMouseWorkaround = !Boolean.getBoolean("jtable.tooltip_follow_mouse.disable");
 
@Override
public String getToolTipText(MouseEvent event) {
final String original = super.getToolTipText(event);
353,37 → 365,73
infoL.add(original);
}
 
final SQLRowValues row = ITableModel.getLine(this.getModel(), rowIndex).getRow();
final SQLRowAccessor row = ITableModel.getLine(this.getModel(), rowIndex).getRowAccessor();
 
final String create = getLine(true, row, getSource().getPrimaryTable().getCreationUserField(), getSource().getPrimaryTable().getCreationDateField());
final RowRef cacheKey = row.getRowRef();
final RowMetadata md = MD_CACHE.get(cacheKey);
if (md != null) {
final String create = getLine(true, md);
final String modif = getLine(false, md);
if (create == null && modif == null) {
infoL.add(TM.tr("ilist.metadata.na"));
} else {
if (create != null)
infoL.add(create);
final String modif = getLine(false, row, getSource().getPrimaryTable().getModifUserField(), getSource().getPrimaryTable().getModifDateField());
if (modif != null)
infoL.add(modif);
}
// TODO locked by
 
} else {
final int half = MD_BATCH_SIZE / 2;
final int firstIndex = Math.max(0, rowIndex - half);
final int lastIndex = Math.min(getRowCount(), rowIndex + half);
final Set<Number> ids = CollectionUtils.newHashSet(MD_BATCH_SIZE);
for (int i = firstIndex; i < lastIndex; i++) {
ids.add(ITableModel.getLine(this.getModel(), i).getRowAccessor().getIDNumber());
}
MD_CACHE.fetch(cacheKey, ids);
 
infoL.add(TM.tr("ilist.metadata.loading"));
}
 
final String info;
if (infoL.size() == 0)
if (infoL.size() == 0) {
info = null;
else
info = "<html>" + CollectionUtils.join(infoL, "<br/>") + "</html>";
// ATTN doesn't follow the mouse if info remains the same, MAYBE add an identifier
} else {
final StringBuilder sb = new StringBuilder(256);
sb.append("<html>");
sb.append(CollectionUtils.join(infoL, "<br/>"));
sb.append("</html>");
if (this.followMouseWorkaround) {
// This force the JRE to repaint the tooltip at the mouse location :
// 1. even without mainInfo changing (e.g. "unavailable")
// 2. but only when changing row
// Otherwise (e.g. adding or not a space at the end, for each call) the
// tooltip is drawn continuously and CPU load is quite heavy.
sb.append("<!--");
sb.append(rowIndex);
sb.append("-->");
}
info = sb.toString();
}
 
return info;
}
 
public String getLine(final boolean created, final SQLRowValues row, final SQLField userF, final SQLField dateF) {
final Calendar date = dateF == null ? null : row.getDate(dateF.getName());
final SQLRowAccessor user = userF == null || row.getObject(userF.getName()) == null || row.isForeignEmpty(userF.getName()) ? null : row.getForeign(userF.getName());
if (user == null && date == null)
public String getLine(final boolean created, final RowMetadata md) {
final Date date = created ? md.getCreation() : md.getModification();
final Integer userID = created ? md.getUserCreate() : md.getUserModify();
if (userID == null && date == null)
return null;
 
final int userParam;
final String firstName, lastName;
if (user != null) {
if (userID != null) {
userParam = 1;
firstName = user.getString("PRENOM");
lastName = user.getString("NOM");
final User user = UserManager.getInstance().getUser(userID);
firstName = user.getFirstName();
lastName = user.getName();
} else {
userParam = 0;
firstName = null;
390,7 → 438,7
lastName = null;
}
 
return TM.tr("ilist.metadata", created ? 1 : 0, userParam, firstName, lastName, date == null ? 0 : 1, date == null ? null : date.getTime());
return TM.tr("ilist.metadata", created ? 1 : 0, userParam, firstName, lastName, date == null ? 0 : 1, date);
}
 
@Override
601,6 → 649,65
return res;
}
 
// Transitional class while we convert RowAction to SQLRowValuesAction
@Deprecated
static public final class ConvertedAction extends SQLRowValuesAction {
private final RowAction rowAction;
 
public ConvertedAction(final RowAction a) {
super(a.inHeader(), a.inPopupMenu(), a.getID(), (evt) -> {
a.getAction().actionPerformed(new ActionEvent(evt.getSource(), ActionEvent.ACTION_PERFORMED, null));
});
this.rowAction = a;
if (a.getAction().getValue(Action.NAME) != null)
this.setName(String.valueOf(a.getAction().getValue(Action.NAME)));
}
 
@Override
public boolean enabledFor(ListEvent evt) {
return this.getRowAction().enabledFor(evt);
}
 
public final RowAction getRowAction() {
return this.rowAction;
}
}
 
public final RowAction addRowValuesAction(SQLRowValuesAction a) {
final RowAction action;
if (a instanceof ConvertedAction) {
action = ((ConvertedAction) a).getRowAction();
} else {
action = new RowAction(new AbstractAction(a.getName()) {
@Override
public void actionPerformed(ActionEvent e) {
a.getAction().accept(IListe.get(e).createListEvent());
}
}, a.inHeader(), a.inPopupMenu(), a.getID()) {
 
@Override
public boolean enabledFor(ListEvent evt) {
return a.enabledFor(evt);
}
 
};
}
if (this.addIListeAction(action))
return action;
else
return null;
}
 
public final Map<SQLRowValuesAction, IListeAction> addRowValuesActions(Collection<? extends SQLRowValuesAction> actions) {
final Map<SQLRowValuesAction, IListeAction> res = new IdentityHashMap<>();
for (final SQLRowValuesAction a : actions) {
final RowAction action = addRowValuesAction(a);
if (action != null)
res.put(a, action);
}
return res;
}
 
public final void addIListeActions(Collection<? extends IListeAction> actions) {
for (final IListeAction a : actions)
this.addIListeAction(a);
619,14 → 726,14
return -1;
}
 
public final void addIListeAction(IListeAction action) {
public final boolean addIListeAction(IListeAction action) {
// we need to handle addition of an already added action at least for setDefaultRowAction()
if (this.rowActions.containsKey(action))
return;
return false;
final ButtonsBuilder headerBtns = action.getHeaderButtons();
this.rowActions.put(action, headerBtns);
if (headerBtns.getContent().size() > 0) {
updateButton(headerBtns, new IListeEvent(this));
updateButton(headerBtns, this.createListEvent());
for (final JButton headerBtn : headerBtns.getContent().keySet()) {
headerBtn.setOpaque(false);
this.btnPanel.add(headerBtn, findGroupIndex((String) headerBtn.getClientProperty(ButtonsBuilder.GROUPNAME_PROPNAME)));
633,6 → 740,7
}
this.btnPanel.setVisible(true);
}
return true;
}
 
public final void removeIListeActions(Collection<? extends IListeAction> actions) {
656,7 → 764,7
}
 
private void updateButtons() {
final IListeEvent evt = new IListeEvent(this);
final IListeEvent evt = this.createListEvent();
for (final ButtonsBuilder btns : this.rowActions.values()) {
this.updateButton(btns, evt);
}
670,7 → 778,7
 
private JPopupMenu updatePopupMenu(final boolean onRows) {
this.popup.removeAll();
final PopupEvent evt = new PopupEvent(this, onRows);
final PopupEvent evt = this.createPopupEvent(onRows);
final Action defaultAction = this.defaultRowAction != null ? this.defaultRowAction.getDefaultAction(evt) : null;
final VirtualMenu menu = VirtualMenu.createRoot(null);
for (final IListeAction a : this.rowActions.keySet()) {
721,12 → 829,33
// special method needed since sometimes getPopupContent() can access the DB (optionally
// creating threads) or be slow
if (this.defaultRowAction != null) {
final Action defaultAction = this.defaultRowAction.getDefaultAction(new IListeEvent(this));
final Action defaultAction = this.defaultRowAction.getDefaultAction(this.createListEvent());
if (defaultAction != null)
defaultAction.actionPerformed(new ActionEvent(e.getSource(), e.getID(), null, e.getWhen(), e.getModifiers()));
}
}
 
final IListeEvent createListEvent() {
return createEvent((vals, accessors) -> new IListeEvent(this, vals, accessors));
}
 
final PopupEvent createPopupEvent(final boolean onRows) {
return createEvent((vals, accessors) -> new PopupEvent(this, vals, accessors, onRows));
}
 
private final <E extends ListEvent> E createEvent(final BiFunction<List<SQLRowValues>, List<? extends SQLRowAccessor>, E> ctor) {
final List<SQLRowValues> vals;
final List<? extends SQLRowAccessor> accessors;
if (this.getSource().getKeepMode() == KeepMode.GRAPH) {
vals = this.getSelectedRows();
accessors = vals;
} else {
vals = null;
accessors = this.getSelectedRowAccessors();
}
return ctor.apply(vals, accessors);
}
 
private void uiInit() {
// * filter
this.filter.addMouseListener(new MouseAdapter() {
780,6 → 909,9
final int columnIndex = ((Number) cb.getClientProperty(COL_INDEX_KEY)).intValue();
final TableColumn col = colModel.getColumn(columnIndex, false);
final boolean newValue = !colModel.isColumnVisible(col);
// Workaround for crash on linux Java 16 with Nimbus L&F or Flat L&F
IListe.this.jTable.getTableHeader().setDraggedColumn(null);
 
// don't remove last column
if (newValue || colModel.getColumnCount(true) > 1)
colModel.setColumnVisible(col, newValue);
949,8 → 1081,8
this.setTransferHandler(new FileTransfertHandler(getSource().getPrimaryTable()));
 
if (this.getSource().getPrimaryTable().getFieldRaw(SQLComponent.READ_ONLY_FIELD) != null) {
this.addIListeAction(getUnlockAction());
this.addIListeAction(getLockAction());
this.addRowValuesAction(getUnlockAction());
this.addRowValuesAction(getLockAction());
}
}
 
1149,7 → 1281,9
if (clazz == SQLRowValues.class) {
toCast = line.getRow().toImmutable();
} else if (clazz == SQLRow.class) {
toCast = line.getRow().asRow();
toCast = line.getRowAccessor().asRow();
} else if (clazz == SQLRowAccessor.class) {
toCast = line.getRowAccessor();
} else if (clazz == ListSQLLine.class) {
toCast = line;
} else {
1173,6 → 1307,10
return this.getSelectedRow(SQLRowValues.class);
}
 
public SQLRowAccessor getSelectedRowAccessor() {
return this.getSelectedRow(SQLRowAccessor.class);
}
 
// selected row cannot be inferred from iterateSelectedRows() since the user might have selected
// the last row anywhere in the selection
private final <R extends SQLRowAccessor> R getSelectedRow(final Class<R> clazz) {
1195,6 → 1333,10
return iterateSelectedRows(ListSQLLine.class);
}
 
public final List<SQLRowAccessor> getSelectedRowAccessors() {
return iterateSelectedRows(SQLRowAccessor.class);
}
 
private final <R> List<R> iterateSelectedRows(final Class<R> clazz) {
final ListSelectionModel selectionModel = this.getJTable().getSelectionModel();
if (selectionModel.isSelectionEmpty())
1471,6 → 1613,13
}
}
 
public final boolean saveTableState() throws IOException {
final boolean hasFile = this.getConfigFile() != null;
if (hasFile)
this.tableStateManager.saveState();
return hasFile;
}
 
private boolean loadTableState() {
// - if configFile changes setConfigFile() calls us
// - if the model changes, fireTableStructureChanged() is called and thus
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/AutoCompletionManager.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
228,7 → 228,7
// SQLRowValues(AutoCompletionManager.this.fillFrom.getTable());
final SQLRow rowV;
if (r != null) {
rowV = r.asRow();
rowV = r.fetchNewRow(false);
} else {
rowV = AutoCompletionManager.this.fillFrom.getTable().getRow(id);
}
292,6 → 292,10
return this.fillBy.keySet();
}
 
public String getToField(String fromField) {
return this.fillBy.get(fromField);
}
 
public void fillRowValues(SQLRowAccessor from, Set<String> fields, SQLRowValues to) {
for (String fromField : fields) {
String toField = AutoCompletionManager.this.fillBy.get(fromField);
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/action/SQLRowValuesAction.java
New file
0,0 → 1,157
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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.list.action;
 
import org.openconcerto.utils.cc.IPredicate;
import org.openconcerto.utils.i18n.TranslationManager;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
 
/**
* An action that act on rows of a list. Either {@link #enabledFor(ListEvent)} or
* {@link #enabledFor(List)} must be overloaded.
*
* @author Sylvain CUAZ
*/
public abstract class SQLRowValuesAction {
 
public static class PredicateRowAction extends SQLRowValuesAction {
private IPredicate<? super ListEvent> pred = null;
 
public PredicateRowAction(boolean header, Consumer<? super ListEvent> action) {
super(header, action);
}
 
public PredicateRowAction(boolean header, boolean popupMenu, Consumer<? super ListEvent> action) {
super(header, popupMenu, action);
}
 
public PredicateRowAction(boolean header, final String id, Consumer<? super ListEvent> action) {
super(header, id, action);
}
 
public PredicateRowAction(boolean header, boolean popupMenu, final String id, Consumer<? super ListEvent> action) {
super(header, popupMenu, id, action);
}
 
public final PredicateRowAction setPredicate(IPredicate<? super ListEvent> pred) {
if (pred == null) {
throw new IllegalArgumentException("null predicate");
}
this.pred = pred;
return this;
}
 
public final IPredicate<? super ListEvent> getPredicate() {
return this.pred;
}
 
@Override
public boolean enabledFor(ListEvent evt) {
if (this.pred == null) {
throw new IllegalStateException("No predicate for " + this);
}
return this.pred.evaluateChecked(evt);
}
}
 
private final Consumer<? super ListEvent> action;
private final boolean header, popupMenu;
private List<String> path;
private final String id;
private String name;
 
public SQLRowValuesAction(boolean header, Consumer<? super ListEvent> action) {
this(header, true, action);
}
 
public SQLRowValuesAction(boolean header, boolean popupMenu, Consumer<? super ListEvent> action) {
this(header, popupMenu, null, action);
}
 
public SQLRowValuesAction(boolean header, final String id, Consumer<? super ListEvent> action) {
this(header, true, id, action);
}
 
public SQLRowValuesAction(boolean header, boolean popupMenu, final String id, Consumer<? super ListEvent> action) {
super();
this.action = action;
this.header = header;
this.popupMenu = popupMenu;
this.setGroup(null);
this.id = id;
if (id != null)
this.setName(TranslationManager.getInstance().getTranslationForAction(id));
}
 
public final String getID() {
return this.id;
}
 
public final SQLRowValuesAction setName(String name) {
this.name = name;
return this;
}
 
public final String getName() {
return this.name;
}
 
public final Consumer<? super ListEvent> getAction() {
return this.action;
}
 
public final boolean inHeader() {
return this.header;
}
 
public final boolean inPopupMenu() {
return this.popupMenu;
}
 
public final SQLRowValuesAction setGroup(String groupName) {
this.path = Arrays.asList(groupName);
return this;
}
 
public final SQLRowValuesAction setPath(List<String> path) {
this.path = Collections.unmodifiableList(new ArrayList<String>(path));
return this;
}
 
public final List<String> getPath() {
return this.path;
}
 
/**
* Whether the action should be enabled in the header or in the popup.
*
* @param evt the state of the IListe.
* @return <code>true</code> if the action can be performed.
*/
public abstract boolean enabledFor(ListEvent evt);
 
public Consumer<? super ListEvent> getDefaultAction(ListEvent evt) {
return this.enabledFor(evt) ? this.getAction() : null;
}
 
@Override
public String toString() {
return this.getClass().getSimpleName() + (this.getID() == null ? "" : " ID '" + this.getID()) + "'";
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/action/ListEvent.java
New file
0,0 → 1,127
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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.list.action;
 
import org.openconcerto.sql.element.SQLElement;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.utils.CollectionUtils;
import org.openconcerto.utils.cc.IPredicate;
 
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
 
public class ListEvent {
 
static private final IPredicate<ListEvent> emptyTotalRowCountPredicate = createTotalRowCountPredicate(0, 0);
 
static public final IPredicate<ListEvent> getEmptyListPredicate() {
return emptyTotalRowCountPredicate;
}
 
static public final IPredicate<ListEvent> createTotalRowCountPredicate(final int min, final int max) {
return new IPredicate<ListEvent>() {
@Override
public boolean evaluateChecked(ListEvent e) {
return e.getTotalRowCount() >= min && e.getTotalRowCount() <= max;
}
};
}
 
static private final IPredicate<ListEvent> singleSelectionPredicate = createSelectionCountPredicate(1, 1);
static private final IPredicate<ListEvent> nonEmptySelectionPredicate = createNonEmptySelectionPredicate(Integer.MAX_VALUE);
 
static public final IPredicate<ListEvent> getSingleSelectionPredicate() {
return singleSelectionPredicate;
}
 
static public final IPredicate<ListEvent> getNonEmptySelectionPredicate() {
return nonEmptySelectionPredicate;
}
 
static public final IPredicate<ListEvent> createNonEmptySelectionPredicate(final int max) {
return createSelectionCountPredicate(1, max);
}
 
static public final IPredicate<ListEvent> createSelectionCountPredicate(final int min, final int max) {
return new IPredicate<ListEvent>() {
@Override
public boolean evaluateChecked(ListEvent e) {
// this is the fastest since it involves no object creation
final List<?> selectedIDs = e.getSelectedRowAccessors();
return selectedIDs.size() >= min && selectedIDs.size() <= max;
}
};
}
 
static public final ListEvent createFromRowValues(final Object source, final SQLElement elem, final int totalRowCount, final List<SQLRowValues> selection) {
return new ListEvent(source, elem, totalRowCount, selection, selection);
}
 
static public final ListEvent createFromRowAccessors(final Object source, final SQLElement elem, final int totalRowCount, final List<? extends SQLRowAccessor> selection) {
return new ListEvent(source, elem, totalRowCount, null, selection);
}
 
private final Object source;
private final SQLElement elem;
private final int totalRowCount;
private final List<SQLRowValues> selection;
private final List<? extends SQLRowAccessor> selectionAccessor;
 
protected ListEvent(final Object source, final SQLElement elem, final int totalRowCount, final List<SQLRowValues> selection, final List<? extends SQLRowAccessor> selectionAccessor) {
super();
this.source = source;
this.elem = elem;
this.totalRowCount = totalRowCount;
assert selection == null || selection == selectionAccessor : "Wasting memory";
this.selection = selection;
this.selectionAccessor = Objects.requireNonNull(selectionAccessor);
}
 
public final Object getSource() {
return this.source;
}
 
public final SQLRowAccessor getSelectedRow() {
return CollectionUtils.getFirst(this.getSelectedRowAccessors());
}
 
public final List<SQLRowValues> getSelectedRows() {
if (this.selection == null)
throw new IllegalStateException("SQLRowValues unavailable, use getSelectedRowAccessors()");
return this.selection;
}
 
public final List<? extends SQLRowAccessor> getSelectedRowAccessors() {
return this.selectionAccessor;
}
 
public final List<Number> getSelectedIDs() {
return SQLRowAccessor.getIDs(this.getSelectedRowAccessors(), new ArrayList<>());
}
 
public final int getTotalRowCount() {
return this.totalRowCount;
}
 
public final SQLTable getTable() {
return this.getElement().getTable();
}
 
public final SQLElement getElement() {
return this.elem;
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/AbstractUpdateOneRunnable.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
29,8 → 29,10
import org.openconcerto.utils.cc.ITransformer;
 
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
 
abstract class AbstractUpdateOneRunnable extends UpdateRunnable {
 
134,5 → 136,27
}
}
 
protected final void updateLines(final Set<Integer> affectedLines, final Integer primaryID) {
final Set<Integer> idsToFetch;
if (primaryID != null && !affectedLines.contains(primaryID)) {
idsToFetch = new HashSet<>(affectedLines);
idsToFetch.add(primaryID);
} else {
idsToFetch = affectedLines;
}
final List<ListSQLLine> newLines = getModel().getLinesSource().get(idsToFetch);
 
final Set<Integer> notUpdated = new HashSet<>(affectedLines);
synchronized (this.getUpdateQ().getFullList()) {
for (final ListSQLLine newLine : newLines) {
this.getUpdateQ().replaceLine(newLine.getID(), newLine);
notUpdated.remove(newLine.getID());
}
for (final Integer toRemove : notUpdated) {
this.getUpdateQ().replaceLine(toRemove, null);
}
}
}
 
protected abstract Collection<String> getModifedFields();
}
/trunk/OpenConcerto/src/org/openconcerto/sql/view/RowMetadataCache.java
New file
0,0 → 1,103
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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.RowRef;
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowListRSH;
import org.openconcerto.sql.model.SQLSelect;
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.SQLCache;
import org.openconcerto.utils.RTInterruptedException;
import org.openconcerto.utils.ThreadFactory;
import org.openconcerto.utils.cache.CacheResult;
 
import java.util.Collections;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
 
public final class RowMetadataCache {
private static RowMetadata createMD(final SQLRowAccessor r, final SQLField creation, final SQLField userCreate, final SQLField modification, final SQLField userModify) {
return new RowMetadata(getDateMD(r, creation), getForeignIntegerMD(r, userCreate), getDateMD(r, modification), getForeignIntegerMD(r, userModify));
}
 
private static Date getDateMD(final SQLRowAccessor row, final SQLField f) {
return f == null ? null : row.getObjectAs(f.getName(), Date.class);
}
 
private static Integer getForeignIntegerMD(final SQLRowAccessor row, final SQLField f) {
if (f == null)
return null;
final Number n = row.getNonEmptyForeignIDNumber(f.getName());
return n == null ? null : n.intValue();
}
 
private final SQLCache<RowRef, RowMetadata> cache;
private final ExecutorService exec;
 
public RowMetadataCache(final int timeoutInSeconds, final int maxCount, final String name) {
this.cache = new SQLCache<>(timeoutInSeconds, maxCount, "Cache of metadata for " + name);
this.exec = Executors.newSingleThreadExecutor(new ThreadFactory("Metadata cache thread for " + name, true));
}
 
public final RowMetadata get(final RowRef ref) {
final CacheResult<RowMetadata> cacheRes = this.cache.get(ref);
if (cacheRes.getState() == CacheResult.State.INTERRUPTED) {
// shouldn't happen since we don't use check()/removeRunning()
throw new RTInterruptedException("interrupted while waiting for the cache");
} else if (cacheRes.getState() == CacheResult.State.VALID) {
final RowMetadata res = cacheRes.getRes();
assert res != null : "Null ambiguity";
return res;
} else {
return null;
}
}
 
public final void fetch(final RowRef ref, final Set<Number> withAdditionalIDs) {
this.exec.submit(() -> {
final CacheResult<RowMetadata> cacheResult = this.cache.get(ref);
if (cacheResult.getState() == CacheResult.State.VALID)
return;
final SQLTable table = ref.getTable();
final SQLField creationDateField = table.getCreationDateField();
final SQLField creationUserField = table.getCreationUserField();
final SQLField modifDateField = table.getModifDateField();
final SQLField modifUserField = table.getModifUserField();
final SQLSelect sel = new SQLSelect();
sel.addAllSelect(table.getPrimaryKeyFields());
sel.addAllSelect(creationDateField, creationUserField, modifDateField, modifUserField);
sel.setWhere(Where.inValues(table.getKey(), withAdditionalIDs));
 
final Set<SQLTable> data = Collections.singleton(table);
 
// wait :
// 1. check errors
// 2. avoid fetching the same IDs more than once
try {
for (final SQLRowAccessor v : SQLRowListRSH.execute(sel)) {
this.cache.put(v.getRowRef(), createMD(v, creationDateField, creationUserField, modifDateField, modifUserField), data);
}
} catch (Exception exn) {
Log.get().log(Level.WARNING, "Couldn't fetch metadata for " + ref, exn);
}
});
}
}
/trunk/OpenConcerto/src/org/openconcerto/sql/PropsConfiguration.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
981,7 → 981,7
}
 
protected SQLElementDirectory createDirectory() {
return new SQLElementDirectory();
return new SQLElementDirectory(getSystemRoot());
}
 
// Use resource name to be able to use absolute (beginning with /) or relative path (to this
1201,6 → 1201,7
}
}
 
@Override
public final UserManager getUserManager() {
synchronized (this.treeLock) {
getRoot();
1208,6 → 1209,7
}
}
 
@Override
public final UserRightsManager getUserRightsManager() {
synchronized (this.treeLock) {
getRoot();
/trunk/OpenConcerto/src/org/openconcerto/sql/ui/light/LightUISQLComboRequest.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/ITextArticleWithCompletion.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
14,7 → 14,6
package org.openconcerto.sql.sqlobject;
 
import org.openconcerto.sql.model.SQLField;
import org.openconcerto.sql.model.SQLRow;
import org.openconcerto.sql.model.SQLRowAccessor;
import org.openconcerto.sql.model.SQLRowValues;
import org.openconcerto.sql.model.SQLRowValuesListFetcher;
44,8 → 43,10
import java.beans.PropertyChangeSupport;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.util.Vector;
 
97,12 → 98,15
private Stack<String> searchStack = new Stack<String>();
private boolean autoselectIfMatch;
private static final int PAUSE_MS = 150;
private final boolean hasDeclinaison;
 
public ITextArticleWithCompletion(SQLTable tableArticle, SQLTable tableARticleFournisseur) {
public ITextArticleWithCompletion(SQLTable tableArticle, SQLTable tableARticleFournisseur, boolean withDeclinaison) {
this.tableArticle = tableArticle;
this.tableArticleFournisseur = tableARticleFournisseur;
this.hasDeclinaison = withDeclinaison;
this.supp = new PropertyChangeSupport(this);
this.popup = new ITextWithCompletionPopUp(this.model, this);
this.popup.setMinWith(450);
this.text = new JTextField();
this.setLayout(new GridLayout(1, 1));
this.add(this.text);
285,16 → 289,31
if (aText.length() > 0) {
 
List<SQLSelect> listSel = new ArrayList<SQLSelect>();
 
// CODE ARTICLE = aText
SQLSelect selMatchingCode = new SQLSelect();
// selMatchingCode.addSelectStar(this.tableArticle);
 
selMatchingCode.addSelect(this.tableArticle.getKey());
selMatchingCode.addSelect(this.tableArticle.getField("CODE"));
selMatchingCode.addSelect(this.tableArticle.getField("NOM"));
selMatchingCode.addSelect(this.tableArticle.getField("CODE_BARRE"));
if (this.hasDeclinaison) {
for (String fieldName : this.tableArticle.getFieldsName()) {
if (fieldName.startsWith("ID_ARTICLE_DECLINAISON_")) {
selMatchingCode.addSelect(this.tableArticle.getField(fieldName));
 
SQLSelect selDecl = new SQLSelect();
SQLTable tableDecl = this.tableArticle.getForeignTable(fieldName);
selDecl.addSelect(tableDecl.getKey());
selDecl.addSelect(tableDecl.getField("NOM"));
listSel.add(selDecl);
}
}
}
Where wMatchingCode = new Where(this.tableArticle.getField("CODE"), "=", aText);
wMatchingCode = wMatchingCode.or(new Where(this.tableArticle.getField("NOM"), "=", aText));
wMatchingCode = wMatchingCode.or(new Where(this.tableArticle.getField("CODE_BARRE"), "=", aText));
wMatchingCode = wMatchingCode.and(new Where(this.tableArticle.getField("VIRTUEL"), "=", Boolean.FALSE));
if (this.whereAdditionnal != null) {
wMatchingCode = wMatchingCode.and(this.whereAdditionnal);
}
311,9 → 330,17
selContains.addSelect(this.tableArticle.getField("CODE"));
selContains.addSelect(this.tableArticle.getField("NOM"));
selContains.addSelect(this.tableArticle.getField("CODE_BARRE"));
if (this.hasDeclinaison) {
for (String fieldName : this.tableArticle.getFieldsName()) {
if (fieldName.startsWith("ID_ARTICLE_DECLINAISON_")) {
selContains.addSelect(this.tableArticle.getField(fieldName));
}
}
}
Where wContains = new Where(this.tableArticle.getField("CODE"), "LIKE", "%" + aText + "%");
wContains = wContains.or(new Where(this.tableArticle.getField("NOM"), "LIKE", "%" + aText + "%"));
wContains = wContains.or(new Where(this.tableArticle.getField("CODE_BARRE"), "LIKE", "%" + aText + "%"));
wContains = wContains.and(new Where(this.tableArticle.getField("VIRTUEL"), "=", Boolean.FALSE));
if (this.whereAdditionnal != null) {
wContains = wContains.and(this.whereAdditionnal);
}
366,8 → 393,15
SQLTable tableCodeArt = this.tableArticle.getDBRoot().getTable("CODE_FOURNISSEUR");
SQLRowValues rowValsCodeF = new SQLRowValues(tableCodeArt);
rowValsCodeF.putNulls("CODE");
rowValsCodeF.putRowValues("ID_ARTICLE").putNulls(this.tableArticle.getFieldsName());
 
final SQLRowValues putRowValuesArt = rowValsCodeF.putRowValues("ID_ARTICLE");
putRowValuesArt.putNulls(this.tableArticle.getFieldsName());
if (this.hasDeclinaison) {
for (String fieldName : this.tableArticle.getFieldsName()) {
if (fieldName.startsWith("ID_ARTICLE_DECLINAISON_")) {
putRowValuesArt.putRowValues(fieldName).putNulls("NOM");
}
}
}
final String codeText = aText;
SQLRowValuesListFetcher fetcher = SQLRowValuesListFetcher.create(rowValsCodeF);
fetcher.setSelTransf(new ITransformer<SQLSelect, SQLSelect>() {
383,9 → 417,18
List<SQLRowValues> resultCodeF = fetcher.fetch();
resultList.add(2, resultCodeF);
 
Map<String, Map<Number, SQLRowAccessor>> mapDecl = new HashMap<>();
for (List<? extends SQLRowAccessor> list : resultList) {
 
if (!list.isEmpty()) {
if (list.get(0).getTable().getName().startsWith("ARTICLE_DECLINAISON")) {
Map<Number, SQLRowAccessor> map = new HashMap<>();
for (SQLRowAccessor sqlRow : list) {
map.put(sqlRow.getIDNumber(), sqlRow);
}
mapDecl.put("ID_" + list.get(0).getTable().getName(), map);
} else {
for (SQLRowAccessor sqlRow : list) {
 
StringBuffer buf = new StringBuffer();
if (sqlRow.getTable().getName().equals("CODE_FOURNISSEUR")) {
393,6 → 436,23
buf.append(sqlRow.getString("CODE") + " -- ");
buf.append(rArt.getString("CODE") + " -- ");
buf.append(rArt.getString("NOM"));
if (this.hasDeclinaison) {
for (String fieldName : rArt.getFields()) {
if (fieldName.startsWith("ID_ARTICLE_DECLINAISON")) {
Number rowDecl = rArt.getObject(fieldName) == null ? null : rArt.getNonEmptyForeignIDNumber(fieldName);
if (rowDecl != null) {
final Map<Number, SQLRowAccessor> mapIdDecl = mapDecl.get(fieldName);
if (mapIdDecl != null) {
final SQLRowAccessor sqlRowAccessor = mapIdDecl.get(rowDecl);
if (sqlRowAccessor != null) {
buf.append(" -- " + sqlRowAccessor.getString("NOM"));
}
}
}
}
}
}
 
result.add(new IComboSelectionItem(rArt, buf.toString()));
} else {
if (sqlRow.getString("CODE_BARRE") != null && sqlRow.getString("CODE_BARRE").trim().length() > 0) {
400,10 → 460,28
}
buf.append(sqlRow.getString("CODE") + " -- ");
buf.append(sqlRow.getString("NOM"));
if (this.hasDeclinaison) {
for (String fieldName : sqlRow.getFields()) {
if (fieldName.startsWith("ID_ARTICLE_DECLINAISON")) {
Number rowDecl = sqlRow.getObject(fieldName) == null ? null : sqlRow.getNonEmptyForeignIDNumber(fieldName);
if (rowDecl != null) {
final Map<Number, SQLRowAccessor> mapIdDecl = mapDecl.get(fieldName);
if (mapIdDecl != null) {
final SQLRowAccessor sqlRowAccessor = mapIdDecl.get(rowDecl);
if (sqlRowAccessor != null) {
buf.append(" -- " + sqlRowAccessor.getString("NOM"));
}
}
}
}
}
}
result.add(new IComboSelectionItem(sqlRow, buf.toString()));
}
}
}
}
}
 
}
 
502,10 → 580,21
 
private synchronized void showPopup() {
if (this.model.getSize() > 0) {
if (this.popupInvoker.isShowing())
if (this.popupInvoker.isShowing()) {
 
String max = "";
for (IComboSelectionItem item : this.model.getList()) {
if (max.length() < item.getLabel().length()) {
max = item.getLabel();
}
}
final int stringWidth = this.text.getGraphics().getFontMetrics().stringWidth(max);
this.popup.setMinWith(Math.max(450, stringWidth + 20));
 
this.popup.show(this.popupInvoker, 0, this.text.getBounds().height);
}
}
}
 
public void changedUpdate(DocumentEvent e) {
updateAutoCompletion(false);
/trunk/OpenConcerto/src/org/openconcerto/sql/sqlobject/ITextArticleWithCompletionCellEditor.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
34,8 → 34,8
private final ITextArticleWithCompletion text;
private boolean listenersInited = false;
 
public ITextArticleWithCompletionCellEditor(SQLTable tableArticle, SQLTable tableARticleFournisseur) {
this.text = new ITextArticleWithCompletion(tableArticle, tableARticleFournisseur);
public ITextArticleWithCompletionCellEditor(SQLTable tableArticle, SQLTable tableARticleFournisseur, boolean withDeclinaison) {
this.text = new ITextArticleWithCompletion(tableArticle, tableARticleFournisseur,withDeclinaison);
this.text.setBorder(BorderFactory.createEmptyBorder());
this.text.getTextComp().setBorder(BorderFactory.createEmptyBorder());
}
/trunk/OpenConcerto/src/org/openconcerto/sql/Configuration.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
32,6 → 32,10
import org.openconcerto.sql.model.SQLTable;
import org.openconcerto.sql.model.Where;
import org.openconcerto.sql.request.SQLFieldTranslator;
import org.openconcerto.sql.users.User;
import org.openconcerto.sql.users.UserManager;
import org.openconcerto.sql.users.rights.UserRights;
import org.openconcerto.sql.users.rights.UserRightsManager;
import org.openconcerto.utils.BaseDirs;
import org.openconcerto.utils.FileUtils;
import org.openconcerto.utils.StringUtils;
123,6 → 127,18
 
public abstract DBRoot getRoot();
 
public abstract UserManager getUserManager();
 
public final User getCurrentUser() {
return UserManager.getCurrentUser(getUserManager());
}
 
public abstract UserRightsManager getUserRightsManager();
 
public final UserRights getCurrentUserRights() {
return UserRightsManager.getCurrentUserRights(getUserRightsManager(), getUserManager());
}
 
public abstract DBSystemRoot getSystemRoot();
 
public abstract SQLFilter getFilter();
/trunk/OpenConcerto/src/org/openconcerto/utils/OutlookEmail.vbs
File deleted
/trunk/OpenConcerto/src/org/openconcerto/utils/system/Powershell.java
New file
0,0 → 1,78
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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.system;
 
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public final class Powershell {
 
private static final Powershell INSTANCE = new Powershell();
 
// No static methods to future-proof if the syntax changes in the future
static public final Powershell getInstance() {
return INSTANCE;
}
 
static private final Pattern PS_CHARS = Pattern.compile("\\R+|'");
 
private Powershell() {
}
 
public final String quote(String s) {
final StringBuffer sb = new StringBuffer((int) (s.length() * 1.1));
quote(s, sb);
return sb.toString();
}
 
public final void quote(String s, final StringBuffer sb) {
sb.append("'");
final Matcher m = PS_CHARS.matcher(s);
while (m.find()) {
final String replacement;
if (m.group().equals("'")) {
replacement = "''";
} else {
// `n isn't expanded in single quoted strings and it's complicated to handle all
// needed escaping in double quoted strings, so just concatenate a single newline.
replacement = "' + \"`n\" + '";
}
m.appendReplacement(sb, replacement);
}
m.appendTail(sb);
sb.append("'");
}
 
// @("C:\Users\ILM\Documents", "2")
public final String quoteArray(List<String> l) {
final StringBuffer sb = new StringBuffer(l.size() * 64);
sb.append("@(");
final Iterator<String> iter = l.iterator();
while (iter.hasNext()) {
final String s = iter.next();
quote(s, sb);
if (iter.hasNext())
sb.append(", ");
}
sb.append(")");
return sb.toString();
}
 
public final String getEncodedCommand(final String s) {
return java.util.Base64.getEncoder().encodeToString(s.getBytes(StandardCharsets.UTF_16LE));
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/system/package-info.java
New file
0,0 → 1,17
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 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.
*/
/**
* Contains class to interact with the OS outside the VM.
*/
package org.openconcerto.utils.system;
/trunk/OpenConcerto/src/org/openconcerto/utils/StringUtils.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
21,6 → 21,7
import java.nio.charset.Charset;
import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
359,6 → 360,39
return res;
}
 
static private final Pattern singleQuote = Pattern.compile("'", Pattern.LITERAL);
static public final Pattern SINGLE_QUOTED_PATTERN = Pattern.compile("'(('')|[^'])*'");
static private final Pattern twoSingleQuote = Pattern.compile("''", Pattern.LITERAL);
 
/**
* Quote a string with single quotes ( e.g. for {@link SimpleDateFormat}).
*
* @param s an arbitrary string, eg "salut\ l'ami".
* @return the quoted form, eg "'salut\ l''ami'".
*/
static public final String singleQuote(String s) {
if (s.isEmpty())
return "''";
else
return "'" + singleQuote.matcher(s).replaceAll("''") + "'";
}
 
static public final String unSingleQuote(String s) {
if (!SINGLE_QUOTED_PATTERN.matcher(s).matches())
throw new IllegalArgumentException("Invalid quoted string " + s);
return unSingleQuoteUnsafe(s);
}
 
private static String unSingleQuoteUnsafe(String s) {
return twoSingleQuote.matcher(s.substring(1, s.length() - 1)).replaceAll("'");
}
 
static public final String unSingleQuote(Matcher m) {
if (m.pattern() != SINGLE_QUOTED_PATTERN)
throw new IllegalArgumentException("Matcher not from SINGLE_QUOTED_PATTERN : " + m);
return unSingleQuoteUnsafe(m.group());
}
 
static private final Pattern quotePatrn = Pattern.compile("\"", Pattern.LITERAL);
static private final Pattern slashPatrn = Pattern.compile("(\\\\+)");
 
740,10 → 774,13
}
 
/**
* convert a byte array to its hexa representation
* Convert a byte array to its hexa representation (uppercase)
*
* @param bytes to converts
* @param size of the byte array to use
* @return the hexa representation
*/
public static String bytesToHexString(byte[] bytes) {
final int length = bytes.length;
public static String bytesToHexString(byte[] bytes, int length) {
char[] hexChars = new char[length * 2];
for (int j = 0; j < length; j++) {
int v = bytes[j] & 0xFF;
753,7 → 790,25
return new String(hexChars);
}
 
public static String charToHex(char c) {
int v = c & 0xFF;
char[] hexChars = new char[2];
hexChars[0] = hexArray[v >>> 4];
hexChars[1] = hexArray[v & 0x0F];
return new String(hexChars);
}
 
/**
* Convert a byte array to its hexa representation (uppercase)
*
* @param bytes to converts
* @return the hexa representation
*/
public static String bytesToHexString(byte[] bytes) {
return bytesToHexString(bytes, bytes.length);
}
 
/**
* Whether the parameter is empty.
*
* @param s the string to test.
768,22 → 823,11
}
 
/**
* Return the first parameter that is non-empty.
*
* @param s1 the string to test.
* @param s2 the alternate value.
* @return <code>s1</code> if not <code>null</code> and not {@link String#isEmpty() empty},
* <code>s2</code> otherwise.
*/
public static String coalesce(String s1, String s2) {
return isEmpty(s1) ? s2 : s1;
}
 
/**
* Return the first value that is non-empty.
*
* @param values values to test for emptiness.
* @return the first value that is neither <code>null</code> nor {@link String#isEmpty() empty}.
* @return the first value that is neither <code>null</code> nor {@link String#isEmpty() empty},
* <code>null</code> if none.
*/
public static String coalesce(String... values) {
return coalesce(false, values);
790,9 → 834,10
}
 
public static String coalesce(final boolean trim, String... values) {
for (final String s : values)
for (final String s : values) {
if (!isEmpty(s, trim))
return s;
}
return null;
}
 
/trunk/OpenConcerto/src/org/openconcerto/utils/SetMap.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
36,6 → 36,8
}
 
static public <K, V> SetMapItf<K, V> unmodifiableMap(SetMapItf<K, V> map) {
if (map.isEmpty())
return empty();
return new Unmodifiable<K, V>(map);
}
 
/trunk/OpenConcerto/src/org/openconcerto/utils/sync/SyncClient.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
18,6 → 18,7
 
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
529,7 → 530,16
wr.close();
 
// Get the response ASAP in order to not block the server while computing locally hash256
DataInputStream in = new DataInputStream(new BufferedInputStream(conn.getInputStream()));
 
// Copy the entire data first because DataInputStream will fail on partial buffer (mainly
// readUTF)
InputStream ins = conn.getInputStream();
final ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamUtils.copy(ins, out);
ins.close();
 
DataInputStream in = new DataInputStream(new ByteArrayInputStream(out.toByteArray()));
 
int fileCount = in.readInt();
this.byteReceived += 4;
ArrayList<FileProperty> list = new ArrayList<FileProperty>();
875,4 → 885,6
public void setVerifyHost(boolean verify) {
this.verifyHost = verify;
}
 
}
/trunk/OpenConcerto/src/org/openconcerto/utils/sync/SimpleSyncClient.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
27,6 → 27,7
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
52,15 → 53,15
 
public final class SimpleSyncClient extends HTTPClient {
 
static protected final long getLong(final Object o) {
protected static final long getLong(final Object o) {
return ((Number) o).longValue();
}
 
static protected final Instant getInstant(final Object o) {
protected static final Instant getInstant(final Object o) {
return Instant.ofEpochMilli(getLong(o));
}
 
static protected abstract class BaseAttrs {
public static abstract class BaseAttrs implements Serializable {
private final String path;
private final String name;
private final Instant lastModified;
90,8 → 91,8
}
}
 
static public final class DirAttrs extends BaseAttrs {
static protected DirAttrs fromJSON(final String path, final JSONArray array) {
public static final class DirAttrs extends BaseAttrs {
protected static DirAttrs fromJSON(final String path, final JSONArray array) {
return new DirAttrs(path, (String) array.get(0), getInstant(array.get(1)));
}
 
100,9 → 101,9
}
}
 
static public final class FileAttrs extends BaseAttrs {
public static final class FileAttrs extends BaseAttrs {
 
static protected FileAttrs fromJSON(final String path, final JSONArray array) {
protected static FileAttrs fromJSON(final String path, final JSONArray array) {
return new FileAttrs(path, (String) array.get(0), getInstant(array.get(2)), getLong(array.get(1)), (String) array.get(3));
}
 
158,7 → 159,7
}
}
 
static public final class DirContent {
public static final class DirContent {
private final String path;
private final JSONObject json;
 
219,11 → 220,11
}
 
@FunctionalInterface
static public interface FileConsumer {
public static interface FileConsumer {
public void accept(FileAttrs attrs, InputStream fileStream) throws IOException;
}
 
static private final Set<Integer> GETFILE_OK_CODES = CollectionUtils.createSet(200, 404);
private static final Set<Integer> GETFILE_OK_CODES = CollectionUtils.createSet(200, 404);
 
public Response getFile(final String path, final String fileName, final FileConsumer fileConsumer) throws IOException {
if (path == null) {
246,6 → 247,26
return res;
}
 
public Integer getCounter(final String key) throws IOException {
if (key == null) {
throw new IllegalArgumentException("null key");
}
final HttpsURLConnection con = openConnection("/getCounter");
send(con, NetUtils.urlEncode("key", key), false);
final Response res = checkResponseCode(con, GETFILE_OK_CODES);
if (res.getCode() == 200) {
byte[] bytes = new byte[20];
try (final InputStream in = getInputStream(con)) {
int r = in.read(bytes);
String str = new String(bytes, 0, r);
return Integer.parseInt(str);
}
 
}
return null;
 
}
 
// ATTN contrary to other methods, the result isn't if the request was OK : it ignores
// throwsException() and always throws. The return value is true if the file existed and was
// saved.
281,7 → 302,7
}
 
public final Response renameFile(final String path, final String fileName, final String newFileName) throws IOException {
return this.renameFile(path, fileName, null, newFileName);
return this.renameFile(path, fileName, path, newFileName);
}
 
public final Response renameFile(final String path, final String fileName, final String newPath, final String newFileName) throws IOException {
331,4 → 352,15
 
return checkResponseCode(send(con, ba.toByteArray(), true));
}
 
public Response createDir(String path, String fileName) throws IOException {
if (path == null) {
throw new IllegalArgumentException("null path");
}
if (fileName == null) {
throw new IllegalArgumentException("null fileName");
}
final HttpsURLConnection con = openConnection("/mkdir");
return checkResponseCode(send(con, NetUtils.urlEncode("rn", fileName, "rp", path), false));
}
}
/trunk/OpenConcerto/src/org/openconcerto/utils/DesktopEnvironment.java
1,7 → 1,7
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
* Copyright 2011-2019 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
160,62 → 160,12
}
 
static public final class Windows extends DEisOS {
static private boolean needsQuoting(String s) {
final int len = s.length();
if (len == 0) // empty string have to be quoted
return true;
for (int i = 0; i < len; i++) {
switch (s.charAt(i)) {
case ' ':
case '\t':
case '\\':
case '"':
return true;
}
}
return false;
}
 
// on Windows program themselves are required to parse the command line, thus a lot of them
// do it differently, see http://www.autohotkey.net/~deleyd/parameters/parameters.htm
 
static private final Pattern quotePatrn = Pattern.compile("([\\\\]*)\"");
static private final Pattern endSlashPatrn = Pattern.compile("([\\\\]*)\\z");
 
// see http://bugs.sun.com/view_bug.do?bug_id=6468220
// e.g. find.exe, choice.exe
public String quoteParamForMsftC(String s) {
if (!needsQuoting(s))
return s;
if (s.length() > 0) {
// replace '(\*)"' by '$1$1\"', e.g. '\quote " \"' by '\quote \" \\\"'
// $1 needed so that the backslash we add isn't escaped itself by a preceding
// backslash
s = quotePatrn.matcher(s).replaceAll("$1$1\\\\\"");
// replace '(\*)\z' by '$1$1', e.g. 'foo\' by 'foo\\'
// needed to not escape closing quote
s = endSlashPatrn.matcher(s).replaceAll("$1$1");
}
return '"' + s + '"';
}
 
// e.g. bash.exe
public String quoteParamForGCC(String s) {
return StringUtils.doubleQuote(s);
}