Dépôt officiel du code source de l'ERP OpenConcerto
/trunk/OpenConcerto/.classpath |
---|
37,16 → 37,16 |
<classpathentry kind="lib" path="lib/icudata_56.jar"/> |
<classpathentry kind="lib" path="lib/javax.mail-1.6.0.jar"/> |
<classpathentry kind="lib" path="lib/json-smart-2.2.1.jar"/> |
<classpathentry kind="lib" path="lib/mysql-connector-java-5.1.40-bin.jar"/> |
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> |
<classpathentry exported="true" kind="lib" path="lib/poi-3.17.jar"/> |
<classpathentry kind="lib" path="lib/piccolo-1.04.jar"/> |
<classpathentry kind="lib" path="lib/fontbox-2.0.19.jar"/> |
<classpathentry kind="lib" path="lib/javax.activation-1.1.1.jar"/> |
<classpathentry kind="lib" path="lib/pdfbox-2.0.19.jar"/> |
<classpathentry kind="lib" path="lib/pdfbox.jar"/> |
<classpathentry kind="lib" path="lib/flatlaf-0.41.jar"/> |
<classpathentry kind="lib" path="lib/postgresql-42.2.8.jar"/> |
<classpathentry kind="lib" path="lib/jsch-0.1.55.jar"/> |
<classpathentry exported="true" kind="lib" path="lib/poi-5.0.0.jar"/> |
<classpathentry kind="lib" path="lib/mysql-connector-java-5.1.45-bin.jar"/> |
<classpathentry kind="lib" path="lib/flatlaf-1.2.jar"/> |
<classpathentry kind="lib" path="lib/fontbox-2.0.22.jar"/> |
<classpathentry kind="lib" path="lib/pdfbox-2.0.22.jar"/> |
<classpathentry kind="output" path="bin"/> |
</classpath> |
/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/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/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/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/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/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/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/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/jOpenDocument-1.4rc2.jar |
---|
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/Configuration/Template/Default/ReportingVentes.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/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/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/ReleveCheque.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/DIPE.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/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/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/ReportingEcoContribution.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/JournauxMois.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/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/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/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/Operations Report.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/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/AvoirF.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/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/Relance2.odt |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/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/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/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/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/EtatChargesPaye.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/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/EtatStocks.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/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/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/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/ReportingTaxeComplementaire.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/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/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/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/GrandLivre.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/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/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/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/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/ListeDebiteur.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/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/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/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/ExportArticle.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/VentilationAnalytique.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/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/Relance3.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/FichePayeSimplifiee.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/JournauxAnalytique.ods |
---|
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/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/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/ReleveChequeEmis.ods |
---|
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/DemandePrix.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/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/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/Devis.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/src/product.properties |
---|
1,5 → 1,5 |
NAME=OpenConcerto |
VERSION=1.7.0 |
VERSION=1.7.1 |
ORGANIZATION_NAME=OpenConcerto |
ORGANIZATION_ID=org.openconcerto |
/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/utils/OutlookEmail.vbs |
---|
File deleted |
/trunk/OpenConcerto/src/org/openconcerto/utils/TinyMap.java |
---|
13,187 → 13,223 |
package org.openconcerto.utils; |
import java.util.AbstractMap.SimpleImmutableEntry; |
import java.util.AbstractMap; |
import java.util.AbstractSet; |
import java.util.ArrayList; |
import java.util.Collection; |
import java.util.HashSet; |
import java.util.ConcurrentModificationException; |
import java.util.Iterator; |
import java.util.Map; |
import java.util.NoSuchElementException; |
import java.util.Objects; |
import java.util.Set; |
import java.util.function.Function; |
public class TinyMap<K, V> implements Map<K, V> { |
private final ArrayList<K> keys; |
private final ArrayList<V> values; |
public class TinyMap<K, V> extends AbstractMap<K, V> { |
private static final Function<Entry<?, ?>, Object> KEY_GETTER = Entry::getKey; |
private static final Function<Entry<?, ?>, Object> VALUE_GETTER = Entry::getValue; |
private final ArrayList<Entry<K, V>> entries; |
private transient Set<Map.Entry<K, V>> entrySet; |
/** |
* The number of times this HashMap has been structurally modified Structural modifications are |
* those that change the number of mappings in the HashMap or otherwise modify its internal |
* structure (e.g., rehash). This field is used to make iterators on Collection-views of the |
* HashMap fail-fast. (See ConcurrentModificationException). |
*/ |
transient int modCount = 0; |
public TinyMap() { |
this(10); |
} |
public TinyMap(int initialCapacity) { |
keys = new ArrayList<K>(initialCapacity); |
values = new ArrayList<V>(initialCapacity); |
public TinyMap(final int initialCapacity) { |
this.entries = new ArrayList<>(initialCapacity); |
} |
public TinyMap(final Map<? extends K, ? extends V> m) { |
this.entries = new ArrayList<>(m.size()); |
// don't call putAll() to avoid indexOfKey() |
for (final Entry<? extends K, ? extends V> e : m.entrySet()) { |
this.entries.add(new SimpleEntry<>(e)); |
} |
++this.modCount; |
} |
@Override |
public int size() { |
return keys.size(); |
return this.entries.size(); |
} |
@Override |
public boolean isEmpty() { |
return keys.isEmpty(); |
return this.entries.isEmpty(); |
} |
@Override |
public boolean containsKey(Object key) { |
return keys.contains(key); |
public boolean containsKey(final Object key) { |
return indexOfKey(key) >= 0; |
} |
@Override |
public boolean containsValue(Object value) { |
return values.contains(value); |
public boolean containsValue(final Object value) { |
return this.indexOf(value, VALUE_GETTER) >= 0; |
} |
@Override |
public V get(Object key) { |
final int size = this.keys.size(); |
for (int i = 0; i < size; i++) { |
if (this.keys.get(i).equals(key)) { |
return this.values.get(i); |
private int indexOfKey(final Object key) { |
return this.indexOf(key, KEY_GETTER); |
} |
private final int indexOf(final Object o, final Function<Entry<?, ?>, Object> getter) { |
final int stop = this.entries.size(); |
for (int i = 0; i < stop; i++) { |
final Entry<K, V> e = this.entries.get(i); |
if (Objects.equals(o, getter.apply(e))) |
return i; |
} |
return -1; |
} |
@Override |
public V get(final Object key) { |
final int i = indexOfKey(key); |
if (i < 0) |
return null; |
return this.entries.get(i).getValue(); |
} |
@Override |
public V put(K key, V value) { |
final int size = this.keys.size(); |
for (int i = 0; i < size; i++) { |
if (this.keys.get(i).equals(key)) { |
final V old = this.values.get(i); |
this.values.set(i, value); |
return old; |
public V put(final K key, final V value) { |
final int i = this.indexOfKey(key); |
final V res; |
if (i < 0) { |
this.entries.add(new SimpleEntry<>(key, value)); |
++this.modCount; |
res = null; |
} else { |
res = this.entries.get(i).setValue(value); |
} |
return res; |
} |
this.keys.add(key); |
this.values.add(value); |
return null; |
} |
@Override |
public V remove(Object key) { |
final int size = this.keys.size(); |
for (int i = 0; i < size; i++) { |
if (this.keys.get(i).equals(key)) { |
this.keys.remove(i); |
return this.values.remove(i); |
public V remove(final Object key) { |
return this.remove(indexOfKey(key)); |
} |
} |
private V remove(final int i) { |
if (i < 0) |
return null; |
final Entry<K, V> res = this.entries.remove(i); |
++this.modCount; |
return res.getValue(); |
} |
@Override |
public void putAll(Map<? extends K, ? extends V> m) { |
final Set<? extends K> keySet = m.keySet(); |
for (Iterator<? extends K> iterator = keySet.iterator(); iterator.hasNext();) { |
K key = (K) iterator.next(); |
put(key, m.get(key)); |
public boolean remove(final Object key, final Object value) { |
final int i = indexOfKey(key); |
if (i < 0) |
return false; |
final boolean eqVal = Objects.equals(this.entries.get(i).getValue(), value); |
if (eqVal) { |
this.remove(i); |
} |
return eqVal; |
} |
@Override |
public void clear() { |
this.keys.clear(); |
this.values.clear(); |
this.entries.clear(); |
++this.modCount; |
} |
// Views |
@Override |
public Set<K> keySet() { |
return new HashSet<K>(this.keys) { |
public Set<Map.Entry<K, V>> entrySet() { |
Set<Map.Entry<K, V>> es; |
return (es = this.entrySet) == null ? (this.entrySet = new EntrySet()) : es; |
} |
final class EntrySet extends AbstractSet<Map.Entry<K, V>> { |
@Override |
public boolean remove(Object o) { |
TinyMap.this.remove(o); |
return super.remove(o); |
public final int size() { |
return TinyMap.this.size(); |
} |
@Override |
public void clear() { |
clear(); |
super.clear(); |
public final void clear() { |
TinyMap.this.clear(); |
} |
}; |
} |
@Override |
public Collection<V> values() { |
public final Iterator<Map.Entry<K, V>> iterator() { |
return new Iterator<Map.Entry<K, V>>() { |
return new ArrayList<V>(this.values()) { |
private int expectedModCount = TinyMap.this.modCount; |
/** |
* The index last returned, i.e. initial value just before first item. |
* |
* <pre> |
* a b c |
* -1 0 1 2 |
* </pre> |
*/ |
private int currentPos = -1; |
private Entry<K, V> lastReturned = null; |
@Override |
public V remove(int index) { |
keys.remove(index); |
values.remove(index); |
return super.remove(index); |
public boolean hasNext() { |
final int nextIndex = this.currentPos + 1; |
return nextIndex < size(); |
} |
@Override |
public boolean remove(Object o) { |
int index = values.indexOf(o); |
if (index >= 0) { |
keys.remove(index); |
values.remove(index); |
public Entry<K, V> next() { |
checkForComodification(); |
if (!hasNext()) |
throw new NoSuchElementException(); |
this.currentPos++; |
final Entry<K, V> res = TinyMap.this.entries.get(this.currentPos); |
this.lastReturned = res; |
return res; |
} |
return super.remove(o); |
} |
@Override |
public void clear() { |
clear(); |
super.clear(); |
public void remove() { |
checkForComodification(); |
if (this.lastReturned == null) |
throw new IllegalStateException(); |
TinyMap.this.remove(this.currentPos); |
this.currentPos--; |
// per doc : cannot be called twice |
this.lastReturned = null; |
this.expectedModCount = TinyMap.this.modCount; |
} |
}; |
} |
@Override |
public Set<Entry<K, V>> entrySet() { |
final Set<Entry<K, V>> set = new HashSet<Map.Entry<K, V>>() { |
@Override |
public boolean remove(Object o) { |
Map.Entry<K, V> entry = (Map.Entry<K, V>) o; |
int index = values.indexOf(entry.getValue()); |
if (index >= 0) { |
keys.remove(index); |
values.remove(index); |
final void checkForComodification() { |
if (TinyMap.this.modCount != this.expectedModCount) |
throw new ConcurrentModificationException(); |
} |
return super.remove(o); |
}; |
} |
@Override |
public boolean removeAll(Collection<?> c) { |
for (Iterator iterator = c.iterator(); iterator.hasNext();) { |
Entry<K, V> entry = (Entry<K, V>) iterator.next(); |
int index = values.indexOf(entry.getValue()); |
if (index >= 0) { |
keys.remove(index); |
values.remove(index); |
public final boolean contains(final Object o) { |
if (!(o instanceof Map.Entry)) |
return false; |
final Map.Entry<?, ?> e = (Map.Entry<?, ?>) o; |
return TinyMap.this.entries.contains(e); |
} |
} |
return super.removeAll(c); |
} |
@Override |
public void clear() { |
clear(); |
super.clear(); |
public final boolean remove(final Object o) { |
if (o instanceof Map.Entry) { |
final Map.Entry<?, ?> e = (Map.Entry<?, ?>) o; |
final Object key = e.getKey(); |
final Object value = e.getValue(); |
return TinyMap.this.remove(key, value); |
} |
}; |
final int size = this.keys.size(); |
for (int i = 0; i < size; i++) { |
set.add(new SimpleImmutableEntry<K, V>(this.keys.get(i), this.values.get(i))); |
return false; |
} |
return set; |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/reentrantEventsNaive.seq.violet.html |
---|
New file |
0,0 → 1,800 |
<HTML> |
<HEAD> |
<META name="description" |
content="Violet UML Editor cross format document" /> |
<META name="keywords" content="Violet, UML" /> |
<META charset="UTF-8" /> |
<SCRIPT type="text/javascript"> |
function switchVisibility() { |
var obj = document.getElementById("content"); |
obj.style.display = (obj.style.display == "block") ? "none" : "block"; |
} |
</SCRIPT> |
</HEAD> |
<BODY> |
This file was generated with Violet UML Editor 3.0.0. |
( <A href=# onclick="switchVisibility()">View Source</A> / <A href="http://sourceforge.net/projects/violet/files/violetumleditor/" target="_blank">Download Violet</A> ) |
<BR /> |
<BR /> |
<SCRIPT id="content" type="text/xml"><![CDATA[<SequenceDiagramGraph id="1"> |
<nodes id="2"> |
<LifelineNode id="3"> |
<id id="4" value="72e47375-469f-4a88-abb6-e40e46c324ce"/> |
<revision>0</revision> |
<children id="5"> |
<ActivationBarNode id="6"> |
<id id="7" value="a94abf1e-083c-482c-8940-d77d8a5e62a3"/> |
<revision>1</revision> |
<children id="8"/> |
<parent class="LifelineNode" reference="3"/> |
<location class="Point2D.Double" id="9" x="112.5" y="75.0"/> |
<backgroundColor id="10"> |
<red>255</red> |
<green>255</green> |
<blue>255</blue> |
<alpha>255</alpha> |
</backgroundColor> |
<borderColor id="11"> |
<red>191</red> |
<green>191</green> |
<blue>191</blue> |
<alpha>255</alpha> |
</borderColor> |
<textColor id="12"> |
<red>51</red> |
<green>51</green> |
<blue>51</blue> |
<alpha>255</alpha> |
</textColor> |
</ActivationBarNode> |
</children> |
<location class="Point2D.Double" id="13" x="50.0" y="0.0"/> |
<textColor reference="12"/> |
<name id="14"> |
<text>evt 1</text> |
</name> |
<type id="15"> |
<text>SQLElementEvent</text> |
</type> |
<endOfLife>true</endOfLife> |
</LifelineNode> |
<LifelineNode id="16"> |
<id id="17" value="2da76d69-e422-46b0-a548-1d7b83093d9f"/> |
<revision>1</revision> |
<children id="18"> |
<ActivationBarNode id="19"> |
<id id="20" value="030a1385-4bfe-4889-8ec1-ffa386c5db22"/> |
<revision>0</revision> |
<children id="21"/> |
<parent class="LifelineNode" reference="16"/> |
<location class="Point2D.Double" id="22" x="112.5" y="110.0"/> |
<backgroundColor reference="10"/> |
<borderColor reference="11"/> |
<textColor id="23"> |
<red>51</red> |
<green>51</green> |
<blue>51</blue> |
<alpha>255</alpha> |
</textColor> |
</ActivationBarNode> |
</children> |
<location class="Point2D.Double" id="24" x="980.0" y="10.0"/> |
<textColor reference="23"/> |
<name id="25"> |
<text>evt 2</text> |
</name> |
<type id="26"> |
<text>SQLElementEvent</text> |
</type> |
<endOfLife>true</endOfLife> |
</LifelineNode> |
<LifelineNode id="27"> |
<id id="28" value="d3539181-e292-45d4-92ee-bb25e1cd9aaf"/> |
<revision>1</revision> |
<children id="29"> |
<ActivationBarNode id="30"> |
<id id="31" value="4f050be9-7427-4789-b824-b1f78f32cd07"/> |
<revision>0</revision> |
<children id="32"/> |
<parent class="LifelineNode" reference="27"/> |
<location class="Point2D.Double" id="33" x="133.0" y="90.0"/> |
<backgroundColor reference="10"/> |
<borderColor reference="11"/> |
<textColor reference="23"/> |
</ActivationBarNode> |
<ActivationBarNode id="34"> |
<id id="35" value="3af702fc-e885-4000-b97b-bd4921b5fc56"/> |
<revision>0</revision> |
<children id="36"/> |
<parent class="LifelineNode" reference="27"/> |
<location class="Point2D.Double" id="37" x="133.0" y="140.0"/> |
<backgroundColor reference="10"/> |
<borderColor reference="11"/> |
<textColor reference="23"/> |
</ActivationBarNode> |
</children> |
<location class="Point2D.Double" id="38" x="370.0" y="10.0"/> |
<textColor reference="23"/> |
<name id="39"> |
<text>l1</text> |
</name> |
<type id="40"> |
<text>SQLElementEventListener</text> |
</type> |
<endOfLife>false</endOfLife> |
</LifelineNode> |
<LifelineNode id="41"> |
<id id="42" value="66c2c3a4-4956-4a4b-b819-07da280e5582"/> |
<revision>1</revision> |
<children id="43"> |
<ActivationBarNode id="44"> |
<id id="45" value="bf8e10a3-b20f-4568-af06-4c8ffe13924e"/> |
<revision>0</revision> |
<children id="46"/> |
<parent class="LifelineNode" reference="41"/> |
<location class="Point2D.Double" id="47" x="133.0" y="200.0"/> |
<backgroundColor reference="10"/> |
<borderColor reference="11"/> |
<textColor reference="23"/> |
</ActivationBarNode> |
<ActivationBarNode id="48"> |
<id id="49" value="ac5b4a69-b3fa-4a3b-8c4a-570c49d3b4cb"/> |
<revision>0</revision> |
<children id="50"/> |
<parent class="LifelineNode" reference="41"/> |
<location class="Point2D.Double" id="51" x="133.0" y="310.0"/> |
<backgroundColor reference="10"/> |
<borderColor reference="11"/> |
<textColor reference="23"/> |
</ActivationBarNode> |
</children> |
<location class="Point2D.Double" id="52" x="680.0" y="0.0"/> |
<textColor reference="23"/> |
<name id="53"> |
<text>l2</text> |
</name> |
<type id="54"> |
<text>SQLElementEventListener</text> |
</type> |
<endOfLife>false</endOfLife> |
</LifelineNode> |
<NoteNode id="55"> |
<id id="56" value="d4154bcf-2a20-4ffc-aaf5-43d47a5a34f7"/> |
<revision>1</revision> |
<children id="57"/> |
<location class="Point2D.Double" id="58" x="730.0" y="410.0"/> |
<backgroundColor id="59"> |
<red>254</red> |
<green>222</green> |
<blue>188</blue> |
<alpha>255</alpha> |
</backgroundColor> |
<borderColor id="60"> |
<red>253</red> |
<green>186</green> |
<blue>113</blue> |
<alpha>255</alpha> |
</borderColor> |
<textColor id="61"> |
<red>51</red> |
<green>51</green> |
<blue>51</blue> |
<alpha>255</alpha> |
</textColor> |
<text id="62"> |
<text>l2 is notified of evt 2 before evt 1</text> |
</text> |
</NoteNode> |
</nodes> |
<edges id="63"> |
<SynchronousCallEdge id="64"> |
<id id="65" value="69d0c441-b334-4eb1-8b1e-89a9d77ad913"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="6"/> |
<startLocation class="Point2D.Double" id="66" x="7.5" y="15.0"/> |
<endNode class="ActivationBarNode" reference="30"/> |
<endLocation class="Point2D.Double" id="67" x="150.0" y="90.0"/> |
<transitionPoints id="68"/> |
<backgroundColor id="69"> |
<red>255</red> |
<green>255</green> |
<blue>255</blue> |
<alpha>255</alpha> |
</backgroundColor> |
<borderColor id="70"> |
<red>191</red> |
<green>191</green> |
<blue>191</blue> |
<alpha>255</alpha> |
</borderColor> |
<textColor id="71"> |
<red>51</red> |
<green>51</green> |
<blue>51</blue> |
<alpha>255</alpha> |
</textColor> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>0</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>3</selectedEndArrowhead> |
<startLabel id="72"> |
<text></text> |
</startLabel> |
<centerLabel id="73"> |
<text>modification(evt1)</text> |
</centerLabel> |
<endLabel id="74"> |
<text></text> |
</endLabel> |
</SynchronousCallEdge> |
<SynchronousCallEdge id="75"> |
<id id="76" value="2a43a89d-a435-46ef-ba93-fe869e1af5f0"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="30"/> |
<startLocation class="Point2D.Double" id="77" x="7.0" y="20.0"/> |
<endNode class="ActivationBarNode" reference="19"/> |
<endLocation class="Point2D.Double" id="78" x="120.0" y="100.0"/> |
<transitionPoints id="79"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>0</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>3</selectedEndArrowhead> |
<startLabel id="80"> |
<text></text> |
</startLabel> |
<centerLabel id="81"> |
<text>fire</text> |
</centerLabel> |
<endLabel id="82"> |
<text></text> |
</endLabel> |
</SynchronousCallEdge> |
<SynchronousCallEdge id="83"> |
<id id="84" value="0c060aa6-6c2b-4238-a9ae-0b4417cdcbda"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="19"/> |
<startLocation class="Point2D.Double" id="85" x="7.5" y="20.0"/> |
<endNode class="ActivationBarNode" reference="34"/> |
<endLocation class="Point2D.Double" id="86" x="130.0" y="130.0"/> |
<transitionPoints id="87"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>0</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>3</selectedEndArrowhead> |
<startLabel id="88"> |
<text></text> |
</startLabel> |
<centerLabel id="89"> |
<text>modification(evt2)</text> |
</centerLabel> |
<endLabel id="90"> |
<text></text> |
</endLabel> |
</SynchronousCallEdge> |
<SynchronousCallEdge id="91"> |
<id id="92" value="7866f976-b5c5-4dff-b55e-817e0acd0851"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="19"/> |
<startLocation class="Point2D.Double" id="93" x="7.5" y="70.0"/> |
<endNode class="ActivationBarNode" reference="44"/> |
<endLocation class="Point2D.Double" id="94" x="140.0" y="200.0"/> |
<transitionPoints id="95"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>0</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>3</selectedEndArrowhead> |
<startLabel id="96"> |
<text></text> |
</startLabel> |
<centerLabel id="97"> |
<text>modification(evt2)</text> |
</centerLabel> |
<endLabel id="98"> |
<text></text> |
</endLabel> |
</SynchronousCallEdge> |
<ReturnEdge id="99"> |
<id id="100" value="8a1ad364-cc2f-43ce-99d4-77158785be3d"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="19"/> |
<startLocation class="Point2D.Double" id="101" x="7.5" y="130.0"/> |
<endNode class="ActivationBarNode" reference="30"/> |
<endLocation class="Point2D.Double" id="102" x="7.0" y="160.0"/> |
<transitionPoints id="103"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>1</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>1</selectedEndArrowhead> |
<startLabel id="104"> |
<text></text> |
</startLabel> |
<centerLabel id="105"> |
<text></text> |
</centerLabel> |
<endLabel id="106"> |
<text></text> |
</endLabel> |
</ReturnEdge> |
<ReturnEdge id="107"> |
<id id="108" value="5e7f4a16-1fc3-4fc6-9dac-1d228ab268f8"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="34"/> |
<startLocation class="Point2D.Double" id="109" x="7.0" y="20.0"/> |
<endNode class="ActivationBarNode" reference="19"/> |
<endLocation class="Point2D.Double" id="110" x="7.5" y="60.0"/> |
<transitionPoints id="111"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>1</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>1</selectedEndArrowhead> |
<startLabel id="112"> |
<text></text> |
</startLabel> |
<centerLabel id="113"> |
<text></text> |
</centerLabel> |
<endLabel id="114"> |
<text></text> |
</endLabel> |
</ReturnEdge> |
<ReturnEdge id="115"> |
<id id="116" value="5fe641a1-312d-4394-96df-a684b18f6a1e"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="44"/> |
<startLocation class="Point2D.Double" id="117" x="7.0" y="30.0"/> |
<endNode class="ActivationBarNode" reference="19"/> |
<endLocation class="Point2D.Double" id="118" x="7.5" y="110.0"/> |
<transitionPoints id="119"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>1</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>1</selectedEndArrowhead> |
<startLabel id="120"> |
<text></text> |
</startLabel> |
<centerLabel id="121"> |
<text></text> |
</centerLabel> |
<endLabel id="122"> |
<text></text> |
</endLabel> |
</ReturnEdge> |
<ReturnEdge id="123"> |
<id id="124" value="5769cc8e-d632-4921-9aea-5d2e0e15d351"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="30"/> |
<startLocation class="Point2D.Double" id="125" x="7.0" y="170.0"/> |
<endNode class="ActivationBarNode" reference="6"/> |
<endLocation class="Point2D.Double" id="126" x="7.5" y="185.0"/> |
<transitionPoints id="127"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>1</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>1</selectedEndArrowhead> |
<startLabel id="128"> |
<text></text> |
</startLabel> |
<centerLabel id="129"> |
<text></text> |
</centerLabel> |
<endLabel id="130"> |
<text></text> |
</endLabel> |
</ReturnEdge> |
<SynchronousCallEdge id="131"> |
<id id="132" value="1334344f-9a27-4aff-b5d4-5ddfda00b259"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="6"/> |
<startLocation class="Point2D.Double" id="133" x="7.5" y="215.0"/> |
<endNode class="ActivationBarNode" reference="48"/> |
<endLocation class="Point2D.Double" id="134" x="140.0" y="290.0"/> |
<transitionPoints id="135"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>0</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>3</selectedEndArrowhead> |
<startLabel id="136"> |
<text></text> |
</startLabel> |
<centerLabel id="137"> |
<text>modification(evt1)</text> |
</centerLabel> |
<endLabel id="138"> |
<text></text> |
</endLabel> |
</SynchronousCallEdge> |
<ReturnEdge id="139"> |
<id id="140" value="397b25a5-6d9e-43d2-9c80-f1ec604ef31c"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="48"/> |
<startLocation class="Point2D.Double" id="141" x="7.0" y="30.0"/> |
<endNode class="ActivationBarNode" reference="6"/> |
<endLocation class="Point2D.Double" id="142" x="7.5" y="255.0"/> |
<transitionPoints id="143"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>1</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>1</selectedEndArrowhead> |
<startLabel id="144"> |
<text></text> |
</startLabel> |
<centerLabel id="145"> |
<text></text> |
</centerLabel> |
<endLabel id="146"> |
<text></text> |
</endLabel> |
</ReturnEdge> |
</edges> |
</SequenceDiagramGraph>]]></SCRIPT> |
<BR /> |
<BR /> |
<IMG alt="embedded diagram image" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABJQAAAHDCAIAAAD9e9f6AABLXklEQVR4Xu3d7ZscV33n/zxP/oA8 |
C8/zUDYxtgO2g43txDiA8cbceSGQJb84sF7uQkiWkIQENkuW+PczBLi4AiGEu2Agu5hkCWhmNLeS |
LM8wI41lyyNL1vgGjSVL49GMpJme35cpVDqqqu4+n65T1ae73q+rL1/Vpz91uqrnfNX9dU/3/MJu |
YOhsN0b2zAGUkC2w0rJ3AKCEbIF5yE4BDL5f2N1TMQDRatSSbtTJApWqopqqmBNopt6qqbe9gGjR |
vGEINWpJN+pkgUpVUU1VzAk0U2/V1NteQLRo3jCEGrWkG3WyQKWqqKYq5gSaqbdq6m0vIFo0bxhC |
jVrSjTpZoFJVVFMVcwLN1Fs19bYXEC2aNwyhRi3pRp0sUKkqqqmKOYFm6q2aetsLiBbNG4ZQo5Z0 |
o04WqFQV1VTFnEAz9VZNve0FRIvmDUOoUUu6UScLVKqKaqpiTqCZequm3vYCokXzhiHUqCXdqJMF |
KlVFNVUxJ9BMvVVTb3sB0aJ5wxBq1JJu1MkClaqimqqYE2im3qqpt72AaNG8YQg1akk36mSBSlVR |
TVXMCTRTb9XU215AtGjeMIQataQbdbJApaqopirmBJqpt2rqbS8gWjRvGEKNWtKNOlmgUlVUUxVz |
As3UWzX1thcQLZo3DKFGLelGnSxQqSqqqYo5gWbqrZp62wuIFs0bhlCjlnSjThaoVBXVVMWcQDP1 |
Vk297QVEi+YNQ6hRS7pRJwtUqopqqmJOoJl6q6be9gKiRfOGIdSoJd2okwUqVUU1VTEn0Ey9VVNv |
ewHRonnDEGrUkm7UyQKVqqKaqpgTaKbeqqm3vYBo0bxhCDVqSTfqZIFKVVFNVcwJNFNv1dTbXkC0 |
aN4whBq1pBt1skClqqimKuYEmqm3auptLyBaNG8YQo1a0o06WaBSVVRTFXMCzdRbNfW2FxAtmjcM |
oUYt6UadLFCpKqqpijmBZuqtmnrbC4gWzVt9du3IjqICjVrSPifbnLXXnDONxJA94D7VpPKZc8ge |
xg6ac6aRGLIH3Kea8nrbC6ohW2wxq6958/yhHj169B/+4R/uvffeq666yiff1cLCwv3333/77bdf |
d911d91111/+5V8eOnQok1ldXf3c5z73O7/zO9dff73F7r777gcffPDMmTOZWOdTSG7NywScPWJU |
eJCXT+ZKmVilpHusZ0lHwudkM48eVdZfhQd5+WSulIlVqvAeCwdTnW9Necb6zqeaVD5zuo/PY489 |
9slPfvKNb3zjtddeazX1vve97+DBg1fGZVSopPAgL5/MlTKxShXeY+FgqvOtKc9Y3/lUU15ve6l8 |
HkOqu+8KD/LyyVwpE6uUdI/RNW+XHzO/fGc//vGPr7766sycmWmXlpZ+8zd/M5vYteu222574okn |
3GQy7o64rtz7skzA2SNGhQd5+WSulIlVSrrHepZ0JHxONvPoXf4RXjneG6pMVXiQl0/mSplYpQrv |
sXBQFWSSGvhUk8pnTvfxSbZdVl8/+tGPrtxDQIWqCg/y8slcKROrVOE9Fg6qgkxSA59qyuttL5XP |
Y5hkXFR3zQoP8vLJXCkTq5R0j9E1b2984xs/97nPPfnkk575zmw2m+S+++47fPjw+fPnl5eXH374 |
4XvvvTcNrK+vv+51r7PMPffcMzExsba2du7cuampKbtqg3fccYeNpOHOh9T51m2PQAwKD7JwsGbS |
MdSzpCPhc7KZR48q66/CgywcrFnhMRQOqoJMUgOfalL5zOk+Pm95y1seeuihp556amNjw2rq7W9/ |
u930hje84co9BFSoqvAgCwdrVngMhYOqIJPUwKea8nrbS+XzGFLdfVd4kIWDNZOOQWjevv/977/r |
Xe961atedc0119x5551/93d/t7q6auOnT5+2EWv9V1ZW3LytmFe+8pU333zzxYsXk2NyuclCnrHO |
XvGKV9gkL7zwQvaGS/75n//ZAnfddZe7fI1dtXKym7785S+ng50PqfOt20WBdg9pIsnbkfzZn/2Z |
PZK33HKLHa2Nnzlz5k/+5E9uuOGGm2666cEHH2y1Wpdn9JvTfjQf+9jHbE6b4YEHHkhnSG51uePp |
JC6fn34yUsWBteO5pIeDz8m2e9DajUuossI51cXsbmf0t8rc7Tz3Vns58tnPfvb1r3/9ddddd+ut |
t77nPe/Zv39/mnGlu5c54ITPDHZgn/zkJ+2BevnLX+7sWsCnmlQ+c2YeFpe9GrObrMqyN3ijQgvn |
bLeukltd7ng6iYsKLTzghM8MVVeo517tDtXn53v5sbvETbZDde+iup1bXemOhbyaN5vdHsfsxLt2 |
Wcf/0ksvWeDDH/6wXf3qV7/q7vXd737XBj/96U9v64e13fGhTHXN2Hq1wBe+8AV79LO37bAH2gL/ |
/u//nr1he9sG7aZ3vOMd6Ujnu+t86/aVga4PaZp///vf7wZGR0ff9ra3uSO2XNQ5P/jBD7qBb3zj |
G+6tLnc82c7r+tOv7sDa6bqkh4nPybZ70NqNu7pmqLJdRXOqi9ndzutjlbnbee6tH/3oRy/vfEma |
yQyWP2D/GT7wgQ8kG1dddVUy3o5PNal85kwOLzu649SpU3bT3Xffnb3hkg77JqjQXUVztltX7mDC |
HU+286jQVIQV2nWvrofa9eeb3bP9T8RFde+iup1bXekdFfJq3r7zne/s2vnt2B/+8Ie2PjY2Nubm |
5t761rfaoDWOFti7d69tv/nNb3b3SgJLS0vJVZ+jcfnku2aSpZmwPth+AA8//HDaJZsbb7xxV5v/ |
aWFNtt1kgXSk892ld5SRCSTbXR/SNH/PPfc8/vjj9uP/xCc+YVetp8+MpIXnP6f9pI4cObK6uvrx |
j3/crlomuTUNpFfdwbzk1q4//eoOrJ2uS3qY+Jxsu0ev3bira4YqK5xTXczJYF5yax+rrHAw5d56 |
ww032PY//dM/nTlzZm1t7cCBA/fdd18+lih/wP4zWGZ8fPzcuXPJYAc+1aTymTM5zuzoDjsXu+mL |
X/xi9oZLOuyboEIL52y3rtJAetUdzEtupULzB+w/Q9UV2nWvrofa9ee7XfQYdkV176K6L3HvsSuv |
5u3ee++1GR999FF38MSJEzb4ute9bnun6Ux+pzZdxIuLi3b17W9/e5qXDmtbz7ezb9+++++///rr |
r08m3LWzUE5f+l8UL3/5y21kc3Pzyp1+Jnkf3H07u/MhpfNnZALJdteHdPtS/ic/+Uly9eTJk4Uj |
N998c3LVf875+fnk6k9/+lO7ag9Omk8C6VV3MC+5tetPv7oDa6frkh4mPifb7tFrN66iyvJzqos5 |
GcxLbu1jlRUOptxbkyN873vf+/nPf/6RRx7Z2toqjCXKH7D/DD/+8Y/dTAc+1aTymTM5zuzo9vYP |
fvCDq666yk7HXg1kb1NQofk5262rNJBedQfzklup0O3cAfvPUHWFdt2r66F2/fluFz2GnVHdSZ7q |
dgPp1c52+zRv1113XTJp3jXXXJNkvvzlL9vVBx98MLma9JTf+9730kmSfHq1KzXfmf0j9eSTT37z |
m9+84447bFo7vGS8w/+TsMFdO5/mTEc6H1LnW7evDPg8pMnV8+fPJ1dt9RSOpL9mUGbO5GoaSK92 |
GHR1/ulXd2DtdF3Sw8TnZNs9eu3Ge0OVdZ4zuZoG0qsdBl39qrLCwZR768zMjD3LJiPmt3/7tw8f |
PpyPJcofsP8M+W/EbsenmlQ+cybHmRm013ZXX321PbtnvhGuZ1Ro5zmTq2kgvdph0EWFRluhXffy |
OdTOP9/tosewA6o7zefXD9XdlVfzdu211yaTFkoyp06dskO0RWAHtLa29us7/L/EJk/Ne1paWrJp |
b7/99uTqO9/5zl0dfxv4/vvvT0c6H1LnW7evDPg8pO62z0iQOfOBdoOuzj/96g6sna5Lepj4nGy7 |
R6/deElUWbpdOJIPtBt09avKCgdTmVs3NjbGx8c/9alP3XrrrTZuP/rC2HaIA+5ths58qknlM2f+ |
OB966KGrd4yMjLjjQVCh6XbhSD7QbtBFhWZGepuhM59qyuu6l8+hdv75bivnQnXn8z4jQebMB9oN |
ujr/9Ks7sHa8mrfkFze7/iXBD33oQ7t23jf8l3/5F9v42Mc+5t6a/Dlg9236zqTT8Je8U5m2wl/9 |
6ld37XymMPPL1nb1jTtfwLpnz550sPMhdb51+8qAz0Oan7DzSJA5C39M+b3yOvz0qzuwdrou6WHi |
c7L5h7fzeElUWYcZttss5vxeeX2psvxerna3Jr/uct111yVX8zOXP+DeZujMp5pUPnNmjjMpGXvQ |
3P+vHxAV2mGG7aLlup3LFKJC3ZHeZujMp5ryuu7lc6jbHX++20WPYSGqO5WfsPNIkDkLf0z5vfI6 |
/PSrO7B2dvs0b7bCbMZbbrnlO9/5zvLy8sbGxurq6uHDh7/+9a+/6U1vSmMzMzO7dt6rTf58xNzc |
nDPH9k033WSDk5OTnkeWP88e2JHYwrVDtQO2FtkOKfniHVuyScCW72tf+9pdOx8inJqasqvr6+t2 |
InZ118534LScryjtfEidb92+MuDzkOYn7DwSZM7CH1N+r7wOP/3qDqydrkt6mPicbP7h7TwuocrU |
OQsXc36vvL5UWX4vl3vr7/3e742Pj58+fdqWwbe+9a1dzqcp8jOXP+DeZujMp5pUPnO6x/nFL35x |
185ru29/+9tXpnpEhapz5pdrPlOICnVHepuhM59qyuu6l8+hbnf8+W4XPYZ5VHfnn37nkSBzFv6Y |
8nvldfjpV3dg7ez2ad7M3/zN3yR3k5dmbAXceeedyR+dyP/Nwcz3Y2ZuTbkZVza3o8NNiSvn+Lmr |
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 |
S3a9ArjS/w9VgMdaZn7aJwAAAABJRU5ErkJg" /> |
</BODY> |
</HTML> |
/trunk/OpenConcerto/src/org/openconcerto/utils/FileUtils.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 |
277,6 → 277,7 |
* @return the relative path, eg "../x/y.txt". |
* @throws IOException if an error occurs while canonicalizing the files. |
* @throws IllegalArgumentException if fromDir exists and is not directory. |
* @see {@link Path#relativize(Path)} |
*/ |
public static final String relative(File fromDir, File to) throws IOException { |
if (fromDir.exists() && !fromDir.isDirectory()) |
/trunk/OpenConcerto/src/org/openconcerto/utils/prog/ExtractFromBEncoding.java |
---|
New file |
0,0 → 1,234 |
/* |
* 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.prog; |
import org.openconcerto.utils.CollectionUtils; |
import org.openconcerto.utils.text.CSVReader; |
import java.io.IOException; |
import java.io.Writer; |
import java.nio.file.CopyOption; |
import java.nio.file.Files; |
import java.nio.file.OpenOption; |
import java.nio.file.Path; |
import java.nio.file.Paths; |
import java.nio.file.StandardCopyOption; |
import java.nio.file.StandardOpenOption; |
import java.util.ArrayList; |
import java.util.Arrays; |
import java.util.Collections; |
import java.util.HashSet; |
import java.util.List; |
import java.util.Map; |
import java.util.Set; |
import java.util.regex.Pattern; |
import javax.mail.internet.InternetAddress; |
import javax.mail.internet.MimeUtility; |
import javax.mail.internet.ParseException; |
import com.zimbra.common.util.BEncoding; |
import com.zimbra.common.util.BEncoding.BEncodingException; |
/** |
* Load a CSV file and replace some {@link BEncoding zimbra benconded map} columns by some of their |
* toplevel values. Sample SQL to generate the CSV : |
* |
* <pre> |
*SELECT 'ID', 'sujet', 'date', 'metadonnées' |
UNION ALL |
SELECT id, subject, from_unixtime(floor(date)), metadata |
FROM mail_item |
WHERE mailbox_id=70 and type=5 |
ORDER BY date DESC |
INTO OUTFILE '/tmp/zimbraMails.csv' FIELDS TERMINATED BY ',' ENCLOSED BY '\"' ESCAPED BY '\\' LINES TERMINATED BY '\n' ; |
* </pre> |
* |
* @author sylvain |
*/ |
public class ExtractFromBEncoding { |
static public final class ExtractInfo { |
private final int fieldIndex; |
private final List<String> keys, names; |
public ExtractInfo(int fieldIndex, String[] keysAndNames) { |
super(); |
this.fieldIndex = fieldIndex; |
final List<String> keys = new ArrayList<>(keysAndNames.length); |
final List<String> names = new ArrayList<>(keysAndNames.length); |
for (final String kAn : keysAndNames) { |
final int colonIndex = kAn.indexOf(':'); |
keys.add(kAn.substring(0, colonIndex)); |
names.add(kAn.substring(colonIndex + 1)); |
} |
this.keys = Collections.unmodifiableList(keys); |
this.names = Collections.unmodifiableList(names); |
assert this.keys.size() > 0 && this.keys.size() == this.names.size(); |
} |
public final int getFieldIndex() { |
return this.fieldIndex; |
} |
public final List<String> getKeys() { |
return this.keys; |
} |
public final List<String> getNames() { |
return this.names; |
} |
} |
private static final boolean OVERWRITE_FILE = Boolean.getBoolean("overwrite"); |
private static final Pattern DOUBLE_QUOTE_PATTERN = Pattern.compile("\"", Pattern.LITERAL); |
private static final Pattern SINGLE_QUOTED_ENCODEDWORD_PATTERN = Pattern.compile("'(=\\?.+?\\?=)'"); |
public static void main(String[] args) throws IOException, BEncodingException, ParseException { |
if (args.length == 0) { |
System.out.println("Load a CSV file and replace some {@link BEncoding zimbra benconded map} columns by some of their toplevel values"); |
System.out.println("The bencoded colums are removed and the new columns are added at the end"); |
System.out.println("inputFile outputFile [bencodedIndex key1:label1,key2:label2,...]..."); |
System.exit(1); |
} |
final Path in = Paths.get(args[0]); |
final Path out = Paths.get(args[1]); |
if (Files.exists(out) && Files.isSameFile(in, out)) |
throw new IllegalArgumentException("Same file"); |
final List<ExtractInfo> extractInfos = new ArrayList<>(); |
// can't have the same index more than one time because we remove it |
final Set<Integer> indexes = new HashSet<>(); |
for (int i = 2; i < args.length; i += 2) { |
final int index = Integer.parseInt(args[i]); |
if (!indexes.add(index)) |
throw new IllegalArgumentException("Duplicate index " + index); |
final String[] keys = args[i + 1].split(","); |
extractInfos.add(new ExtractInfo(index, keys)); |
} |
System.out.println("From " + in + " to " + out); |
final int lines = replace(in, out, extractInfos); |
System.out.println("Processed " + lines + " line(s)"); |
} |
/** |
* Read from <code>in</code> and write to <code>out</code>. |
* |
* @param in the input CSV. |
* @param out the output CSV. |
* @param extractInfos what to extract. |
* @return number of processed lines. |
* @throws IOException if an error occurs while reading or writing. |
* @throws BEncodingException if a value isn't b-encoded. |
* @throws ParseException if a value isn't a valid "encoded-word". |
*/ |
public static int replace(final Path in, final Path out, final List<ExtractInfo> extractInfos) throws IOException, ParseException, BEncodingException { |
if (extractInfos.isEmpty()) { |
Files.copy(in, out, OVERWRITE_FILE ? new CopyOption[] { StandardCopyOption.REPLACE_EXISTING } : new CopyOption[0]); |
return 0; |
} |
int totalLines = 0; |
final OpenOption[] openOptions = OVERWRITE_FILE ? new OpenOption[] { StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING } : new OpenOption[] { StandardOpenOption.CREATE_NEW }; |
final StringBuilder sb = new StringBuilder(512); |
try (final CSVReader r = new CSVReader(Files.newBufferedReader(in), ',', '"', '\\', 0, true); |
// CSVWriter doesn't handle escaping quotes by doubling them (as needed by |
// LibreOffice), so output CSV ourselves |
final Writer w = Files.newBufferedWriter(out, openOptions)) { |
// column names |
String[] line = r.readNext(); |
final int itemsCount = line.length; |
int additionalCols = 0; |
final List<String> listToWrite = new ArrayList<>(itemsCount); |
listToWrite.addAll(Arrays.asList(line)); |
for (final ExtractInfo e : extractInfos) { |
listToWrite.remove(e.getFieldIndex()); |
for (final String n : e.getNames()) { |
listToWrite.add(n); |
} |
// we remove the metadata |
additionalCols += e.getKeys().size() - 1; |
} |
final int outputColCount = itemsCount + additionalCols; |
writeCSV(sb, listToWrite, outputColCount); |
w.append(sb); |
while ((line = r.readNext()) != null) { |
totalLines++; |
if (itemsCount != line.length) { |
throw new IllegalStateException("Expected " + itemsCount + " items but got " + line.length + " : " + Arrays.asList(line)); |
} |
listToWrite.clear(); |
listToWrite.addAll(Arrays.asList(line)); |
for (final ExtractInfo e : extractInfos) { |
String item = line[e.getFieldIndex()]; |
if (item.startsWith("=?")) { |
item = MimeUtility.decodeWord(item); |
} |
final Map<?, ?> bdecoded = (Map<?, ?>) BEncoding.decode(item); |
listToWrite.remove(e.getFieldIndex()); |
for (final String key : e.getKeys()) { |
// BEncoding cannot encode null but a key can be missing |
final Object val = bdecoded.get(key); |
final String itemToWrite; |
if (val == null) { |
itemToWrite = null; |
} else { |
String toParse = val.toString(); |
// illegal single quoting, replace with double quote |
if (toParse.contains("'=?")) |
toParse = SINGLE_QUOTED_ENCODEDWORD_PATTERN.matcher(toParse).replaceAll("\"$1\""); |
// strict=false because we don't care if there's "illegal character" |
// (e.g. annabelle@testaud@ac-paris.fr or |
// <'jeromebarral83@yahoo.fr'>), we want it to be legible |
final InternetAddress[] mimeDecoded = InternetAddress.parseHeader(toParse, false); |
itemToWrite = CollectionUtils.join(Arrays.asList(mimeDecoded), ", ", InternetAddress::toUnicodeString); |
} |
listToWrite.add(itemToWrite); |
} |
} |
writeCSV(sb, listToWrite, outputColCount); |
w.append(sb); |
} |
} |
return totalLines; |
} |
private static void writeCSV(final StringBuilder sb, final List<String> l, final int outputColCount) { |
if (l.size() != outputColCount) |
throw new IllegalStateException("Wrong column count"); |
sb.setLength(0); |
for (final String i : l) { |
if (i == null) { |
sb.append("\\N"); |
} else { |
sb.append('"'); |
sb.append(DOUBLE_QUOTE_PATTERN.matcher(i).replaceAll("\"\"")); |
sb.append('"'); |
} |
sb.append(','); |
} |
// replace last field separator |
sb.setCharAt(sb.length() - 1, '\n'); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/ProductInfo.java |
---|
30,9 → 30,12 |
public static final String PROPERTIES_NAME = "/product.properties"; |
public static final String ORGANIZATION_NAME = "ORGANIZATION_NAME"; |
public static final String ORGANIZATION_ID = "ORGANIZATION_ID"; |
// e.g. LibreOffice |
public static final String NAME = "NAME"; |
public static final String ID = "ID"; |
public static final String VERSION = "VERSION"; |
// Allow multiple end-user applications (e.g. icons) to share a product name, e.g. Writer |
public static final String LAUNCHER = "LAUNCHER"; |
private static ProductInfo INSTANCE; |
156,6 → 159,18 |
return orgID == null ? null : orgID + '.' + this.getID(); |
} |
public final String getLauncher() { |
return this.getProperty(LAUNCHER); |
} |
public final String getFullLauncherID() { |
final String launcher = this.getLauncher(); |
final String fullID = this.getFullID(); |
if (launcher == null || fullID == null) |
return fullID; |
return fullID + '-' + sanitizeDomainPart(launcher); |
} |
public final String getVersion() { |
return this.getProperty(VERSION); |
} |
162,10 → 177,14 |
public final void store(final Path p) throws IOException { |
try (final OutputStream out = Files.newOutputStream(p)) { |
this.getProps().store(out, this.getClass().getName()); |
store(out); |
} |
} |
public final void store(final OutputStream out) throws IOException { |
this.getProps().store(out, this.getClass().getName()); |
} |
@Override |
public String toString() { |
return this.getClass().getSimpleName() + " for " + getName() + " " + getVersion(); |
/trunk/OpenConcerto/src/org/openconcerto/utils/Outlook.powershell |
---|
New file |
0,0 → 1,31 |
Add-Type -assembly "Microsoft.Office.Interop.Outlook" |
Add-Type -assembly "System.Runtime.Interopservices" |
try { |
$outlook = [Runtime.Interopservices.Marshal]::GetActiveObject('Outlook.Application') |
$outlookWasAlreadyRunning = $true |
} catch { |
try { |
$Outlook = New-Object -comobject Outlook.Application |
$outlookWasAlreadyRunning = $false |
} catch { |
write-host "You must exit Outlook first." |
exit |
} |
} |
$mail = $Outlook.CreateItem( [Microsoft.Office.Interop.Outlook.OlItemType]::olMailItem) |
$stdin = [Console]::OpenStandardInput() |
try { |
$sr = New-Object -TypeName 'System.IO.StreamReader' -ArgumentList $stdin,[Text.UTF8Encoding] |
$mail.body = $sr.ReadToEnd() |
} finally { |
$stdin.Close() |
} |
$mail.subject = @subject@ |
$mail.to = @to@ |
foreach ( $att in @attachments@ ) { |
$mail.attachments.add($att) |
} |
$mail.Display() |
/trunk/OpenConcerto/src/org/openconcerto/utils/CollectionUtils.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.utils; |
import org.openconcerto.utils.cc.CustomEquals; |
import org.openconcerto.utils.cc.IClosure; |
import org.openconcerto.utils.cc.IPredicate; |
import org.openconcerto.utils.cc.ITransformer; |
47,7 → 48,10 |
import java.util.TreeMap; |
import java.util.TreeSet; |
import java.util.function.Function; |
import java.util.function.Supplier; |
import java.util.regex.Pattern; |
import java.util.stream.Collectors; |
import java.util.stream.Stream; |
/** |
* Une classe regroupant des méthodes utilitaires pour les collections. |
403,13 → 407,53 |
} |
static public final boolean identityContains(final Collection<?> coll, final Object item) { |
final int size = coll.size(); |
if (size > 0) { |
if (coll instanceof RandomAccess && coll instanceof List) { |
final List<?> list = (List<?>) coll; |
for (int i = 0; i < size; i++) { |
final Object v = list.get(i); |
if (item == v) |
return true; |
} |
} else { |
for (final Object v : coll) { |
if (item == v) |
return true; |
} |
} |
} |
return false; |
} |
static public final boolean identityRemove(final Collection<?> coll, final Object item) { |
final int size = coll.size(); |
if (size > 0) { |
if (coll instanceof RandomAccess && coll instanceof List) { |
final List<?> list = (List<?>) coll; |
for (int i = 0; i < size; i++) { |
final Object v = list.get(i); |
if (item == v) { |
list.remove(i); |
return true; |
} |
} |
} else { |
for (Iterator<?> i = coll.iterator(); i.hasNext();) { |
if (item == i.next()) { |
i.remove(); |
return true; |
} |
} |
} |
} |
return false; |
} |
static public final <T> int identityIndexOf(final List<T> coll, final T item) { |
return CustomEquals.indexOf(coll, item, CustomEquals.getIdentity()); |
} |
static public final boolean identityEquals(final List<?> coll1, final List<?> coll2) { |
if (coll1 == coll2) |
return true; |
463,11 → 507,19 |
return null; |
} |
final List<E> result; |
if (list instanceof RandomAccess) { |
final int size = list.size(); |
final List<E> result = new ArrayList<E>(size); |
for (int i = 0; i < list.size(); i++) { |
result = new ArrayList<>(size); |
for (int i = 0; i < size; i++) { |
result.add(c.cast(list.get(i))); |
} |
} else { |
result = new LinkedList<>(); |
for (final Object o : list) { |
result.add(c.cast(o)); |
} |
} |
return result; |
} |
484,11 → 536,15 |
* a <code>E</code> or a value which the type is not a <code>F</code>. |
*/ |
public static <E, F> Map<E, F> castMap(final Map<?, ?> map, Class<E> cKey, Class<F> cValue) throws ClassCastException { |
return castMap(map, cKey, cValue, HashMap::new); |
} |
public static <E, F> Map<E, F> castMap(final Map<?, ?> map, Class<E> cKey, Class<F> cValue, final Supplier<Map<E, F>> ctor) throws ClassCastException { |
if (map == null) { |
return null; |
} |
final Map<E, F> result = new HashMap<E, F>(); |
final Map<E, F> result = ctor.get(); |
for (final Entry<?, ?> mapEntry : map.entrySet()) { |
final E key; |
try { |
507,6 → 563,14 |
return result; |
} |
public static <T> Stream<T> subclassFilter(final Collection<? super T> coll, final Class<T> subclass) { |
return coll.stream().filter(subclass::isInstance).map(subclass::cast); |
} |
public static <T> List<T> subclassFilterToList(final Collection<? super T> coll, final Class<T> subclass) { |
return subclassFilter(coll, subclass).collect(Collectors.toList()); |
} |
/** |
* The number of equals item between a and b, starting from the end. |
* |
948,13 → 1012,42 |
} |
} |
@SafeVarargs |
public static final <T> List<T> toImmutableList(final T... items) { |
final int length = items.length; |
if (length == 0) |
return Collections.emptyList(); |
else if (length == 1) |
return Collections.singletonList(items[0]); |
return Collections.unmodifiableList(Arrays.asList(Arrays.copyOf(items, length))); |
} |
public static final <T> List<T> toImmutableList(final Collection<? extends T> coll) { |
return toImmutableList(coll, ArrayList::new); |
} |
public static final <T, C extends Collection<? extends T>> List<T> toImmutableList(final C coll, final Function<? super C, ? extends List<T>> createColl) { |
if (coll.isEmpty()) |
return toImmutableList(coll, createColl, true); |
} |
/** |
* Create a new immutable list. |
* |
* @param <T> type of items |
* @param <C> type of collection |
* @param coll the collection holding the items. |
* @param createColl how to copy the collection into a new list. |
* @param std <code>true</code> if the created <code>createColl</code> use |
* {@link Object#equals(Object)}, in that case {@link Collections#singletonList(Object)} |
* can be returned. |
* @return an immutable list with the same items as <code>coll</code>. |
*/ |
public static final <T, C extends Collection<? extends T>> List<T> toImmutableList(final C coll, final Function<? super C, ? extends List<T>> createColl, final boolean std) { |
final int size = coll.size(); |
if (size == 0) |
return Collections.emptyList(); |
else if (std && size == 1) |
return Collections.singletonList(coll.iterator().next()); |
return Collections.unmodifiableList(createColl.apply(coll)); |
} |
966,7 → 1059,7 |
// but not at runtime. |
return toImmutableSet((SortedSet<T>) coll, TreeSet::new); |
} else if (coll instanceof IdentitySet) { |
return toImmutableSet((IdentitySet<? extends T>) coll, LinkedIdentitySet::new); |
return toImmutableSet((IdentitySet<? extends T>) coll, LinkedIdentitySet::new, false); |
} else { |
// In doubt, keep order |
// ATTN LinkedHashSet extends HashSet |
975,8 → 1068,28 |
} |
public static final <T, C extends Collection<? extends T>> Set<T> toImmutableSet(final C coll, final Function<? super C, ? extends Set<T>> createColl) { |
if (coll.isEmpty()) |
return toImmutableSet(coll, createColl, true); |
} |
/** |
* Create a new immutable set. |
* |
* @param <T> type of items |
* @param <C> type of collection |
* @param coll the collection holding the items. |
* @param createColl how to copy the collection into a new list. |
* @param std <code>true</code> if the created <code>createColl</code> use |
* {@link Object#equals(Object)}, in that case {@link Collections#singleton(Object)} can |
* be returned. |
* @return an immutable set with the same items as <code>coll</code>. |
*/ |
public static final <T, C extends Collection<? extends T>> Set<T> toImmutableSet(final C coll, final Function<? super C, ? extends Set<T>> createColl, final boolean std) { |
final int size = coll.size(); |
if (size == 0) |
return Collections.emptySet(); |
else if (std && size == 1) |
return Collections.singleton(coll.iterator().next()); |
final Set<T> res = createColl.apply(coll); |
return Collections.unmodifiableSet(res); |
} |
995,7 → 1108,7 |
// ATTN see eclipse bug below about wrong constructor |
return toImmutableMap((SortedMap<K, ? extends V>) map, TreeMap::new); |
} else if (map instanceof IdentityHashMap) { |
return toImmutableMap((IdentityHashMap<? extends K, ? extends V>) map, IdentityHashMap::new); |
return toImmutableMap((IdentityHashMap<? extends K, ? extends V>) map, IdentityHashMap::new, false); |
} else { |
// In doubt, keep order |
// ATTN LinkedHashMap extends HashMap |
1003,6 → 1116,10 |
} |
} |
public static final <K, V, InMap extends Map<? extends K, ? extends V>> Map<K, V> toImmutableMap(final InMap map, final Function<? super InMap, ? extends Map<K, V>> copyFunction) { |
return toImmutableMap(map, copyFunction, true); |
} |
/** |
* Return an immutable map with the same entries as the passed one. NOTE: <code>copyMap</code> |
* <strong>must</strong> copy the entries so that a modification of <code>map</code> doesn't |
1016,11 → 1133,20 |
* {@link TreeMap#TreeMap(Map)} is (correctly) executed at runtime. |
* @param map the map. |
* @param copyFunction how to copy the passed map. |
* @param std <code>true</code> if the created <code>createColl</code> use |
* {@link Object#equals(Object)}, in that case |
* {@link Collections#singletonMap(Object, Object)} can be returned. |
* @return an immutable map. |
*/ |
public static final <K, V, InMap extends Map<? extends K, ? extends V>> Map<K, V> toImmutableMap(final InMap map, final Function<? super InMap, ? extends Map<K, V>> copyFunction) { |
if (map.isEmpty()) |
public static final <K, V, InMap extends Map<? extends K, ? extends V>> Map<K, V> toImmutableMap(final InMap map, final Function<? super InMap, ? extends Map<K, V>> copyFunction, |
final boolean std) { |
final int size = map.size(); |
if (size == 0) { |
return Collections.emptyMap(); |
} else if (std && size == 1) { |
final Entry<? extends K, ? extends V> e = map.entrySet().iterator().next(); |
return Collections.singletonMap(e.getKey(), e.getValue()); |
} |
return Collections.unmodifiableMap(copyFunction.apply(map)); |
} |
1055,7 → 1181,77 |
return res; |
} |
// same as HashMap and HashSet |
private static final float DEFAULT_LOAD_FACTOR = 0.75f; |
public static interface MapConstructor<K, V, M extends Map<K, V>> { |
M create(int initialCapacity, float loadFactor); |
} |
public static final int getCapacity(final int plannedSize, final float loadFactor) { |
return Math.max((int) Math.ceil(plannedSize / loadFactor), 4); |
} |
public static final <K, V> LinkedHashMap<K, V> newLinkedHashMap(final int plannedSize) { |
return newMap(plannedSize, DEFAULT_LOAD_FACTOR, (MapConstructor<K, V, LinkedHashMap<K, V>>) LinkedHashMap::new); |
} |
public static final <K, V> HashMap<K, V> newHashMap(final int plannedSize) { |
return newHashMap(plannedSize, DEFAULT_LOAD_FACTOR); |
} |
/** |
* Create a new map with no rehash operations until at least a certain size. |
* |
* @param <K> the type of keys maintained by the new map |
* @param <V> the type of mapped values |
* @param plannedSize the number of entries expected to be added to the returned map. |
* @param loadFactor the load factor of the returned map, not advised to be <code>1.0f</code> |
* because it increases the chances of hash collisions. |
* @return a new map that won't rehash until at least <code>plannedSize</code> items. |
*/ |
public static final <K, V> HashMap<K, V> newHashMap(final int plannedSize, final float loadFactor) { |
return newMap(plannedSize, loadFactor, (MapConstructor<K, V, HashMap<K, V>>) HashMap::new); |
} |
/** |
* Create a new map with no rehash operations until at least a certain size. |
* |
* @param <K> the type of keys maintained by the new map |
* @param <V> the type of mapped values |
* @param <M> the type of the new map |
* @param plannedSize the number of entries expected to be added to the returned map |
* @param loadFactor the load factor, passed to <code>ctor</code>. |
* @param ctor how to create the new map, will be passed the computed initial capacity and |
* <code>loadFactor</code>, e.g. {@link LinkedHashMap#LinkedHashMap(int, float) |
* LinkedHashMap::new}. |
* @return a new map that won't rehash until at least <code>plannedSize</code> items. |
*/ |
public static final <K, V, M extends Map<K, V>> M newMap(final int plannedSize, final float loadFactor, final MapConstructor<K, V, M> ctor) { |
return ctor.create(getCapacity(plannedSize, loadFactor), loadFactor); |
} |
public static interface SetConstructor<V, S extends Set<V>> { |
S create(int initialCapacity, float loadFactor); |
} |
public static final <V> LinkedHashSet<V> newLinkedHashSet(final int plannedSize) { |
return newSet(plannedSize, DEFAULT_LOAD_FACTOR, (SetConstructor<V, LinkedHashSet<V>>) LinkedHashSet::new); |
} |
public static final <V> HashSet<V> newHashSet(final int plannedSize) { |
return newHashSet(plannedSize, DEFAULT_LOAD_FACTOR); |
} |
public static final <V> HashSet<V> newHashSet(final int plannedSize, final float loadFactor) { |
return newSet(plannedSize, loadFactor, (SetConstructor<V, HashSet<V>>) HashSet::new); |
} |
public static final <V, S extends Set<V>> S newSet(final int plannedSize, final float loadFactor, final SetConstructor<V, S> ctor) { |
return ctor.create(getCapacity(plannedSize, loadFactor), loadFactor); |
} |
/** |
* Creates a map with null values. |
* |
* @param <K> type of key. |
/trunk/OpenConcerto/src/org/openconcerto/utils/TimeUtils.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,9 |
import java.math.BigDecimal; |
import java.math.BigInteger; |
import java.time.LocalDate; |
import java.time.LocalDateTime; |
import java.time.ZoneId; |
import java.util.Arrays; |
import java.util.Calendar; |
import java.util.Collection; |
424,4 → 427,30 |
} |
return finerAmount == finer.convert(coarserAmount, coarser); |
} |
public static LocalDateTime toLocalDateTime(Calendar calendar) { |
if (calendar == null) |
return null; |
return LocalDateTime.ofInstant(calendar.toInstant(), getTZ(calendar)); |
} |
private static ZoneId getTZ(Calendar calendar) { |
final TimeZone tz = calendar.getTimeZone(); |
return tz == null ? ZoneId.systemDefault() : tz.toZoneId(); |
} |
public static final Calendar toCalendar(final LocalDateTime dt) { |
final Calendar cal = Calendar.getInstance(); |
cal.setTimeInMillis(dt.atZone(getTZ(cal)).toInstant().toEpochMilli()); |
return cal; |
} |
public static LocalDate toLocalDate(Calendar calendar) { |
if (calendar == null) |
return null; |
if (calendar instanceof GregorianCalendar) |
return LocalDate.of(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.DAY_OF_MONTH)); |
else |
return toLocalDateTime(calendar).toLocalDate(); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/EmailClient.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,21 → 20,23 |
import org.openconcerto.utils.DesktopEnvironment.XFCE; |
import org.openconcerto.utils.OSFamily.Unix; |
import org.openconcerto.utils.io.PercentEncoder; |
import org.openconcerto.utils.system.Powershell; |
import java.io.BufferedOutputStream; |
import java.io.BufferedWriter; |
import java.io.File; |
import java.io.IOException; |
import java.io.OutputStreamWriter; |
import java.io.OutputStream; |
import java.io.PrintStream; |
import java.io.Writer; |
import java.lang.ProcessBuilder.Redirect; |
import java.net.URI; |
import java.net.URISyntaxException; |
import java.nio.charset.StandardCharsets; |
import java.util.ArrayList; |
import java.util.Arrays; |
import java.util.List; |
import java.util.regex.Matcher; |
import java.util.regex.Pattern; |
import java.util.stream.Collectors; |
public abstract class EmailClient { |
344,26 → 346,34 |
public static final EmailClient Outlook = new EmailClient(EmailClientType.Outlook) { |
@Override |
protected boolean composeNative(String to, String subject, String body, File... attachments) throws IOException, InterruptedException { |
final DesktopEnvironment de = DesktopEnvironment.getDE(); |
final File vbs = FileUtils.getFile(EmailClient.class.getResource("OutlookEmail.vbs")); |
final List<String> l = new ArrayList<String>(6); |
l.add("cscript"); |
l.add(de.quoteParamForExec(vbs.getAbsolutePath())); |
if (to != null) |
l.add(createVBParam("to", to)); |
if (subject != null) |
l.add(createVBParam("subject", subject)); |
// at least set a parameter otherwise the usage get displayed |
l.add(createVBParam("unicodeStdIn", "1")); |
for (File attachment : attachments) { |
l.add(de.quoteParamForExec(attachment.getAbsolutePath())); |
return composePowershell(to, subject, body, attachments); |
} |
final Process process = new ProcessBuilder(l).start(); |
// VBScript only knows ASCII and UTF-16 |
final Writer writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream(), StringUtils.UTF16)); |
writer.write(body); |
writer.close(); |
// only tested with powershell 5.1 |
protected boolean composePowershell(String to, String subject, String body, File... attachments) throws IOException, InterruptedException { |
final Powershell pwsh = Powershell.getInstance(); |
// Don't create temporary file : |
// https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.1 |
String template = new String(StreamUtils.read(EmailClient.class.getResourceAsStream("Outlook.powershell")), StandardCharsets.UTF_8); |
template = template.replace("@to@", pwsh.quote(to == null ? "" : to)); |
template = template.replace("@subject@", pwsh.quote(subject == null ? "" : subject)); |
template = template.replace("@attachments@", pwsh.quoteArray(Arrays.asList(attachments).stream().map(File::getAbsolutePath).collect(Collectors.toList()))); |
final ProcessBuilder pb = new ProcessBuilder(); |
pb.command().add("powershell"); |
// Apparently piping (i.e. "-Command -") only supports ASCII (and would require |
// embedding the body in the script). |
pb.command().add("-EncodedCommand"); |
pb.command().add(pwsh.getEncodedCommand(template)); |
pb.inheritIO(); |
pb.redirectInput(Redirect.PIPE); |
final Process process = pb.start(); |
try (final OutputStream in = process.getOutputStream()) { |
in.write(body.getBytes(StandardCharsets.UTF_8)); |
} |
final int returnCode = process.waitFor(); |
if (returnCode != 0) |
throw new IllegalStateException("Non zero return code: " + returnCode); |
/trunk/OpenConcerto/src/org/openconcerto/utils/reentrantEventsIn-order.seq.violet.html |
---|
New file |
0,0 → 1,1181 |
<HTML> |
<HEAD> |
<META name="description" |
content="Violet UML Editor cross format document" /> |
<META name="keywords" content="Violet, UML" /> |
<META charset="UTF-8" /> |
<SCRIPT type="text/javascript"> |
function switchVisibility() { |
var obj = document.getElementById("content"); |
obj.style.display = (obj.style.display == "block") ? "none" : "block"; |
} |
</SCRIPT> |
</HEAD> |
<BODY> |
This file was generated with Violet UML Editor 3.0.0. |
( <A href=# onclick="switchVisibility()">View Source</A> / <A href="http://sourceforge.net/projects/violet/files/violetumleditor/" target="_blank">Download Violet</A> ) |
<BR /> |
<BR /> |
<SCRIPT id="content" type="text/xml"><![CDATA[<SequenceDiagramGraph id="1"> |
<nodes id="2"> |
<LifelineNode id="3"> |
<id id="4" value="72e47375-469f-4a88-abb6-e40e46c324ce"/> |
<revision>0</revision> |
<children id="5"> |
<ActivationBarNode id="6"> |
<id id="7" value="a94abf1e-083c-482c-8940-d77d8a5e62a3"/> |
<revision>1</revision> |
<children id="8"/> |
<parent class="LifelineNode" reference="3"/> |
<location class="Point2D.Double" id="9" x="112.5" y="75.0"/> |
<backgroundColor id="10"> |
<red>255</red> |
<green>255</green> |
<blue>255</blue> |
<alpha>255</alpha> |
</backgroundColor> |
<borderColor id="11"> |
<red>191</red> |
<green>191</green> |
<blue>191</blue> |
<alpha>255</alpha> |
</borderColor> |
<textColor id="12"> |
<red>51</red> |
<green>51</green> |
<blue>51</blue> |
<alpha>255</alpha> |
</textColor> |
</ActivationBarNode> |
</children> |
<location class="Point2D.Double" id="13" x="320.0" y="0.0"/> |
<textColor reference="12"/> |
<name id="14"> |
<text>evt 1</text> |
</name> |
<type id="15"> |
<text>SQLElementEvent</text> |
</type> |
<endOfLife>true</endOfLife> |
</LifelineNode> |
<LifelineNode id="16"> |
<id id="17" value="2da76d69-e422-46b0-a548-1d7b83093d9f"/> |
<revision>1</revision> |
<children id="18"> |
<ActivationBarNode id="19"> |
<id id="20" value="030a1385-4bfe-4889-8ec1-ffa386c5db22"/> |
<revision>0</revision> |
<children id="21"/> |
<parent class="LifelineNode" reference="16"/> |
<location class="Point2D.Double" id="22" x="112.5" y="150.0"/> |
<backgroundColor reference="10"/> |
<borderColor reference="11"/> |
<textColor id="23"> |
<red>51</red> |
<green>51</green> |
<blue>51</blue> |
<alpha>255</alpha> |
</textColor> |
</ActivationBarNode> |
</children> |
<location class="Point2D.Double" id="24" x="1180.0" y="0.0"/> |
<textColor reference="23"/> |
<name id="25"> |
<text>evt 2</text> |
</name> |
<type id="26"> |
<text>SQLElementEvent</text> |
</type> |
<endOfLife>true</endOfLife> |
</LifelineNode> |
<LifelineNode id="27"> |
<id id="28" value="d3539181-e292-45d4-92ee-bb25e1cd9aaf"/> |
<revision>1</revision> |
<children id="29"> |
<ActivationBarNode id="30"> |
<id id="31" value="4f050be9-7427-4789-b824-b1f78f32cd07"/> |
<revision>0</revision> |
<children id="32"/> |
<parent class="LifelineNode" reference="27"/> |
<location class="Point2D.Double" id="33" x="133.0" y="120.0"/> |
<backgroundColor reference="10"/> |
<borderColor reference="11"/> |
<textColor reference="23"/> |
</ActivationBarNode> |
<ActivationBarNode id="34"> |
<id id="35" value="bbede6d5-d0e1-434f-a671-95e1bbc6de05"/> |
<revision>0</revision> |
<children id="36"/> |
<parent class="LifelineNode" reference="27"/> |
<location class="Point2D.Double" id="37" x="133.0" y="300.0"/> |
<backgroundColor id="38"> |
<red>255</red> |
<green>255</green> |
<blue>255</blue> |
<alpha>255</alpha> |
</backgroundColor> |
<borderColor id="39"> |
<red>191</red> |
<green>191</green> |
<blue>191</blue> |
<alpha>255</alpha> |
</borderColor> |
<textColor reference="23"/> |
</ActivationBarNode> |
</children> |
<location class="Point2D.Double" id="40" x="580.0" y="10.0"/> |
<textColor reference="23"/> |
<name id="41"> |
<text>l1</text> |
</name> |
<type id="42"> |
<text>SQLElementEventListener</text> |
</type> |
<endOfLife>false</endOfLife> |
</LifelineNode> |
<LifelineNode id="43"> |
<id id="44" value="66c2c3a4-4956-4a4b-b819-07da280e5582"/> |
<revision>1</revision> |
<children id="45"> |
<ActivationBarNode id="46"> |
<id id="47" value="f993e4c8-6b9f-4be1-ad82-c3f04af74f39"/> |
<revision>0</revision> |
<children id="48"/> |
<parent class="LifelineNode" reference="43"/> |
<location class="Point2D.Double" id="49" x="133.0" y="220.0"/> |
<backgroundColor reference="38"/> |
<borderColor reference="39"/> |
<textColor reference="23"/> |
</ActivationBarNode> |
<ActivationBarNode id="50"> |
<id id="51" value="27fc63e2-922c-4899-bf6c-017a35d6e97f"/> |
<revision>0</revision> |
<children id="52"/> |
<parent class="LifelineNode" reference="43"/> |
<location class="Point2D.Double" id="53" x="133.0" y="360.0"/> |
<backgroundColor reference="38"/> |
<borderColor reference="39"/> |
<textColor reference="23"/> |
</ActivationBarNode> |
</children> |
<location class="Point2D.Double" id="54" x="880.0" y="0.0"/> |
<textColor reference="23"/> |
<name id="55"> |
<text>l2</text> |
</name> |
<type id="56"> |
<text>SQLElementEventListener</text> |
</type> |
<endOfLife>false</endOfLife> |
</LifelineNode> |
<NoteNode id="57"> |
<id id="58" value="d4154bcf-2a20-4ffc-aaf5-43d47a5a34f7"/> |
<revision>1</revision> |
<children id="59"/> |
<location class="Point2D.Double" id="60" x="920.0" y="540.0"/> |
<backgroundColor id="61"> |
<red>254</red> |
<green>222</green> |
<blue>188</blue> |
<alpha>255</alpha> |
</backgroundColor> |
<borderColor id="62"> |
<red>253</red> |
<green>186</green> |
<blue>113</blue> |
<alpha>255</alpha> |
</borderColor> |
<textColor id="63"> |
<red>51</red> |
<green>51</green> |
<blue>51</blue> |
<alpha>255</alpha> |
</textColor> |
<text id="64"> |
<text>l2 is notified of evt 1 before evt 2</text> |
</text> |
</NoteNode> |
<LifelineNode id="65"> |
<id id="66" value="163a5ef7-6b32-4747-8b4d-fe2335ca3610"/> |
<revision>1</revision> |
<children id="67"> |
<ActivationBarNode id="68"> |
<id id="69" value="d7791be4-637c-448a-b7a7-3651c074b60e"/> |
<revision>0</revision> |
<children id="70"/> |
<parent class="LifelineNode" reference="65"/> |
<location class="Point2D.Double" id="71" x="127.0" y="90.0"/> |
<backgroundColor id="72"> |
<red>255</red> |
<green>255</green> |
<blue>255</blue> |
<alpha>255</alpha> |
</backgroundColor> |
<borderColor id="73"> |
<red>191</red> |
<green>191</green> |
<blue>191</blue> |
<alpha>255</alpha> |
</borderColor> |
<textColor id="74"> |
<red>51</red> |
<green>51</green> |
<blue>51</blue> |
<alpha>255</alpha> |
</textColor> |
</ActivationBarNode> |
<ActivationBarNode id="75"> |
<id id="76" value="b8bfffe0-2ac3-4ae4-98a4-e9551538026a"/> |
<revision>0</revision> |
<children id="77"/> |
<parent class="LifelineNode" reference="65"/> |
<location class="Point2D.Double" id="78" x="127.0" y="180.0"/> |
<backgroundColor reference="38"/> |
<borderColor reference="39"/> |
<textColor reference="74"/> |
</ActivationBarNode> |
</children> |
<location class="Point2D.Double" id="79" x="30.0" y="0.0"/> |
<textColor reference="74"/> |
<name id="80"> |
<text></text> |
</name> |
<type id="81"> |
<text>ReentrantEventDispatcher</text> |
</type> |
<endOfLife>false</endOfLife> |
</LifelineNode> |
<NoteNode id="82"> |
<id id="83" value="511fb94e-4412-4622-93b3-f262f1546e87"/> |
<revision>1</revision> |
<children id="84"/> |
<location class="Point2D.Double" id="85" x="640.0" y="540.0"/> |
<backgroundColor reference="61"/> |
<borderColor reference="62"/> |
<textColor reference="63"/> |
<text id="86"> |
<text>When evt2.fire() returns, |
its listeners have been notitied</text> |
</text> |
</NoteNode> |
</nodes> |
<edges id="87"> |
<SynchronousCallEdge id="88"> |
<id id="89" value="dba1c89a-89af-42c6-b657-904452a6d3c3"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="6"/> |
<startLocation class="Point2D.Double" id="90" x="7.5" y="25.0"/> |
<endNode class="ActivationBarNode" reference="68"/> |
<endLocation class="Point2D.Double" id="91" x="120.0" y="110.0"/> |
<transitionPoints id="92"/> |
<backgroundColor reference="38"/> |
<borderColor reference="39"/> |
<textColor id="93"> |
<red>51</red> |
<green>51</green> |
<blue>51</blue> |
<alpha>255</alpha> |
</textColor> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>0</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>3</selectedEndArrowhead> |
<startLabel id="94"> |
<text></text> |
</startLabel> |
<centerLabel id="95"> |
<text>fire(evt1)</text> |
</centerLabel> |
<endLabel id="96"> |
<text></text> |
</endLabel> |
</SynchronousCallEdge> |
<SynchronousCallEdge id="97"> |
<id id="98" value="24a957d6-a98c-46af-bc95-d25b3b90a8cd"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="68"/> |
<startLocation class="Point2D.Double" id="99" x="3.0" y="30.0"/> |
<endNode class="ActivationBarNode" reference="30"/> |
<endLocation class="Point2D.Double" id="100" x="7.0" y="20.0"/> |
<transitionPoints id="101"/> |
<backgroundColor reference="38"/> |
<borderColor reference="39"/> |
<textColor reference="93"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>0</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>3</selectedEndArrowhead> |
<startLabel id="102"> |
<text></text> |
</startLabel> |
<centerLabel id="103"> |
<text>modification(evt1)</text> |
</centerLabel> |
<endLabel id="104"> |
<text></text> |
</endLabel> |
</SynchronousCallEdge> |
<SynchronousCallEdge id="105"> |
<id id="106" value="dece2a55-78fe-4e6f-92fb-df870ae50d7e"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="30"/> |
<startLocation class="Point2D.Double" id="107" x="7.0" y="20.0"/> |
<endNode class="ActivationBarNode" reference="19"/> |
<endLocation class="Point2D.Double" id="108" x="7.5" y="20.0"/> |
<transitionPoints id="109"/> |
<backgroundColor reference="38"/> |
<borderColor reference="39"/> |
<textColor reference="93"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>0</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>3</selectedEndArrowhead> |
<startLabel id="110"> |
<text></text> |
</startLabel> |
<centerLabel id="111"> |
<text>fire</text> |
</centerLabel> |
<endLabel id="112"> |
<text></text> |
</endLabel> |
</SynchronousCallEdge> |
<SynchronousCallEdge id="113"> |
<id id="114" value="cdfe85c3-922f-4901-93bf-137cb825fa48"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="19"/> |
<startLocation class="Point2D.Double" id="115" x="7.5" y="20.0"/> |
<endNode class="ActivationBarNode" reference="75"/> |
<endLocation class="Point2D.Double" id="116" x="130.0" y="290.0"/> |
<transitionPoints id="117"/> |
<backgroundColor id="118"> |
<red>255</red> |
<green>255</green> |
<blue>255</blue> |
<alpha>255</alpha> |
</backgroundColor> |
<borderColor id="119"> |
<red>191</red> |
<green>191</green> |
<blue>191</blue> |
<alpha>255</alpha> |
</borderColor> |
<textColor id="120"> |
<red>51</red> |
<green>51</green> |
<blue>51</blue> |
<alpha>255</alpha> |
</textColor> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>0</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>3</selectedEndArrowhead> |
<startLabel id="121"> |
<text></text> |
</startLabel> |
<centerLabel id="122"> |
<text>fire(evt2)</text> |
</centerLabel> |
<endLabel id="123"> |
<text></text> |
</endLabel> |
</SynchronousCallEdge> |
<SynchronousCallEdge id="124"> |
<id id="125" value="6459bc49-5f19-42a3-89a6-6f9cec6ef186"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="75"/> |
<startLocation class="Point2D.Double" id="126" x="3.0" y="20.0"/> |
<endNode class="ActivationBarNode" reference="46"/> |
<endLocation class="Point2D.Double" id="127" x="140.0" y="220.0"/> |
<transitionPoints id="128"/> |
<backgroundColor reference="118"/> |
<borderColor reference="119"/> |
<textColor reference="120"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>0</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>3</selectedEndArrowhead> |
<startLabel id="129"> |
<text></text> |
</startLabel> |
<centerLabel id="130"> |
<text>modification(evt1)</text> |
</centerLabel> |
<endLabel id="131"> |
<text></text> |
</endLabel> |
</SynchronousCallEdge> |
<ReturnEdge id="132"> |
<id id="133" value="aeba7c53-ec88-4436-8c73-4076539f3ff4"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="46"/> |
<startLocation class="Point2D.Double" id="134" x="7.0" y="20.0"/> |
<endNode class="ActivationBarNode" reference="75"/> |
<endLocation class="Point2D.Double" id="135" x="3.0" y="60.0"/> |
<transitionPoints id="136"/> |
<backgroundColor reference="118"/> |
<borderColor reference="119"/> |
<textColor reference="120"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>1</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>1</selectedEndArrowhead> |
<startLabel id="137"> |
<text></text> |
</startLabel> |
<centerLabel id="138"> |
<text></text> |
</centerLabel> |
<endLabel id="139"> |
<text></text> |
</endLabel> |
</ReturnEdge> |
<SynchronousCallEdge id="140"> |
<id id="141" value="707e1877-ea13-4950-a9ed-fa4f626a42a9"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="75"/> |
<startLocation class="Point2D.Double" id="142" x="13.0" y="80.0"/> |
<endNode class="ActivationBarNode" reference="34"/> |
<endLocation class="Point2D.Double" id="143" x="140.0" y="340.0"/> |
<transitionPoints id="144"/> |
<backgroundColor reference="118"/> |
<borderColor reference="119"/> |
<textColor reference="120"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>0</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>3</selectedEndArrowhead> |
<startLabel id="145"> |
<text></text> |
</startLabel> |
<centerLabel id="146"> |
<text>modification(evt2)</text> |
</centerLabel> |
<endLabel id="147"> |
<text></text> |
</endLabel> |
</SynchronousCallEdge> |
<ReturnEdge id="148"> |
<id id="149" value="c3f811d3-2375-4e8b-8a63-62e9671368db"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="34"/> |
<startLocation class="Point2D.Double" id="150" x="7.0" y="30.0"/> |
<endNode class="ActivationBarNode" reference="75"/> |
<endLocation class="Point2D.Double" id="151" x="13.0" y="180.0"/> |
<transitionPoints id="152"/> |
<backgroundColor reference="118"/> |
<borderColor reference="119"/> |
<textColor reference="120"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>1</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>1</selectedEndArrowhead> |
<startLabel id="153"> |
<text></text> |
</startLabel> |
<centerLabel id="154"> |
<text></text> |
</centerLabel> |
<endLabel id="155"> |
<text></text> |
</endLabel> |
</ReturnEdge> |
<SynchronousCallEdge id="156"> |
<id id="157" value="4c0dc965-8e0e-468c-a95f-e12f99e7635f"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="75"/> |
<startLocation class="Point2D.Double" id="158" x="3.0" y="160.0"/> |
<endNode class="ActivationBarNode" reference="50"/> |
<endLocation class="Point2D.Double" id="159" x="140.0" y="360.0"/> |
<transitionPoints id="160"/> |
<backgroundColor reference="118"/> |
<borderColor reference="119"/> |
<textColor reference="120"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>0</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>3</selectedEndArrowhead> |
<startLabel id="161"> |
<text></text> |
</startLabel> |
<centerLabel id="162"> |
<text>modification(evt2)</text> |
</centerLabel> |
<endLabel id="163"> |
<text></text> |
</endLabel> |
</SynchronousCallEdge> |
<ReturnEdge id="164"> |
<id id="165" value="1317c4c3-a12a-4319-b23a-5dfa9c228004"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="50"/> |
<startLocation class="Point2D.Double" id="166" x="7.0" y="20.0"/> |
<endNode class="ActivationBarNode" reference="75"/> |
<endLocation class="Point2D.Double" id="167" x="3.0" y="210.0"/> |
<transitionPoints id="168"/> |
<backgroundColor reference="118"/> |
<borderColor reference="119"/> |
<textColor reference="120"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>1</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>1</selectedEndArrowhead> |
<startLabel id="169"> |
<text></text> |
</startLabel> |
<centerLabel id="170"> |
<text></text> |
</centerLabel> |
<endLabel id="171"> |
<text></text> |
</endLabel> |
</ReturnEdge> |
<ReturnEdge id="172"> |
<id id="173" value="dec23fcb-69ae-4795-a9fb-f0e8f300384d"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="75"/> |
<startLocation class="Point2D.Double" id="174" x="3.0" y="230.0"/> |
<endNode class="ActivationBarNode" reference="19"/> |
<endLocation class="Point2D.Double" id="175" x="7.5" y="270.0"/> |
<transitionPoints id="176"/> |
<backgroundColor reference="118"/> |
<borderColor reference="119"/> |
<textColor reference="120"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>1</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>1</selectedEndArrowhead> |
<startLabel id="177"> |
<text></text> |
</startLabel> |
<centerLabel id="178"> |
<text></text> |
</centerLabel> |
<endLabel id="179"> |
<text></text> |
</endLabel> |
</ReturnEdge> |
<ReturnEdge id="180"> |
<id id="181" value="b1771709-2dfc-4b9c-b2f3-fd73233602aa"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="19"/> |
<startLocation class="Point2D.Double" id="182" x="7.5" y="280.0"/> |
<endNode class="ActivationBarNode" reference="30"/> |
<endLocation class="Point2D.Double" id="183" x="7.0" y="310.0"/> |
<transitionPoints id="184"/> |
<backgroundColor reference="118"/> |
<borderColor reference="119"/> |
<textColor reference="120"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>1</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>1</selectedEndArrowhead> |
<startLabel id="185"> |
<text></text> |
</startLabel> |
<centerLabel id="186"> |
<text></text> |
</centerLabel> |
<endLabel id="187"> |
<text></text> |
</endLabel> |
</ReturnEdge> |
<ReturnEdge id="188"> |
<id id="189" value="a019b2a9-840d-4fad-9f9a-e9abb7dfa4f2"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="30"/> |
<startLocation class="Point2D.Double" id="190" x="7.0" y="330.0"/> |
<endNode class="ActivationBarNode" reference="68"/> |
<endLocation class="Point2D.Double" id="191" x="3.0" y="360.0"/> |
<transitionPoints id="192"/> |
<backgroundColor reference="118"/> |
<borderColor reference="119"/> |
<textColor reference="120"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>1</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>1</selectedEndArrowhead> |
<startLabel id="193"> |
<text></text> |
</startLabel> |
<centerLabel id="194"> |
<text></text> |
</centerLabel> |
<endLabel id="195"> |
<text></text> |
</endLabel> |
</ReturnEdge> |
<ReturnEdge id="196"> |
<id id="197" value="407a6a66-f746-43fa-83da-524d9ae14c1c"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="68"/> |
<startLocation class="Point2D.Double" id="198" x="3.0" y="380.0"/> |
<endNode class="ActivationBarNode" reference="6"/> |
<endLocation class="Point2D.Double" id="199" x="7.5" y="405.0"/> |
<transitionPoints id="200"/> |
<backgroundColor reference="118"/> |
<borderColor reference="119"/> |
<textColor reference="120"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>1</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>1</selectedEndArrowhead> |
<startLabel id="201"> |
<text></text> |
</startLabel> |
<centerLabel id="202"> |
<text></text> |
</centerLabel> |
<endLabel id="203"> |
<text></text> |
</endLabel> |
</ReturnEdge> |
</edges> |
</SequenceDiagramGraph>]]></SCRIPT> |
<BR /> |
<BR /> |
<IMG alt="embedded diagram image" src="data:image/png;base64,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 |
58m4sexZtd785jdnvxPljp95+O |