Dépôt officiel du code source de l'ERP OpenConcerto
/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/poi-3.17.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/flatlaf-0.41.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/fontbox-2.0.19.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/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-4.1.0.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/jOpenDocument-1.4rc2.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/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/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/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/Avoir.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/EtatStocks.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/ReleveChequeEmis.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/DepotCheque.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/DepotCheque.ods |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/OpenConcerto/Configuration/Template/Default/BalanceAgee.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/Operations Report.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/EtatStockInventaire.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/EtatStockInventaire.ods |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/OpenConcerto/Configuration/Template/Default/GrandLivre.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/BonReception.xml |
---|
25,7 → 25,7 |
</element> |
<element location="B6" type="fill"> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/> |
</element> |
<element location="B7" type="replace" replacePattern="_"> |
/trunk/OpenConcerto/Configuration/Template/Default/FichePayeSimplifiee.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/BonReception.odsp |
---|
1,6 → 1,6 |
<odsp> |
<spliteveryrow> |
<sheet number="0">57</sheet> |
<sheet number="0">58</sheet> |
</spliteveryrow> |
<offset x="40" y ="20"/> |
<resize percent="85"/> |
/trunk/OpenConcerto/Configuration/Template/Default/ReleveCheque.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/DIPE.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/FichePayeSimplifiee.odsp |
---|
1,6 → 1,7 |
<odsp> |
<spliteveryrow> |
<sheet number="0">61</sheet> |
<sheet number="0">65</sheet> |
</spliteveryrow> |
<offset x="20" y ="20"/> |
<resize percent="92"/> |
<offset x="23" y ="0"/> |
</odsp> |
/trunk/OpenConcerto/Configuration/Template/Default/Devis.xml |
---|
23,7 → 23,7 |
</element> |
<element location="B6" type="fill"> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/> |
</element> |
<element location="B7" type="replace" replacePattern="_"> |
62,7 → 62,7 |
<field name="OBJET" /> |
</element> |
<element location="I10" type="fill"> |
<element location="H10" type="fill"> |
<field name="ID_CLIENT"> |
<field name="FORME_JURIDIQUE" /> |
<field name="NOM" /> |
69,7 → 69,7 |
</field> |
</element> |
<element location="I11" type="address.customer.full"> |
<element location="H11" type="address.customer.full"> |
</element> |
<element location="L63" type="fill"> |
/trunk/OpenConcerto/Configuration/Template/Default/VenteFactureSituation.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/FichePaye.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/VenteFactureTicket.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/VenteFactureTicket.ods |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/OpenConcerto/Configuration/Template/Default/DemandePrix.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/ReportingEcoContribution.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/AvoirF.xml |
---|
27,7 → 27,7 |
</element> |
<element location="B6" type="fill"> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/> |
</element> |
<element location="B7" type="replace" replacePattern="_"> |
/trunk/OpenConcerto/Configuration/Template/Default/SituationCompte.xml |
---|
New file |
0,0 → 1,57 |
<?xml version="1.0" encoding="UTF-8" ?> |
<contentDocument> |
<element0 location="A1" type="Value" ValueName="SOCIETE_NOM"> |
</element0> |
<element0 location="A2" type="Value" ValueName="SOCIETE_ADRESSE"> |
</element0> |
<element0 location="A4" type="Value" ValueName="SOCIETE_VILLE"> |
</element0> |
<element0 location="A5" type="Value" prefix="Tél:" ValueName="SOCIETE_TEL"> |
</element0> |
<element0 location="D6" type="Value" ValueName="NOM_CLIENT"> |
</element0> |
<element0 location="F8" type="Value" ValueName="DATE"> |
</element0> |
<element0 location="G51" type="Value" ValueName="TOTAL_ECHEANCE"> |
</element0> |
<element0 location="G52" type="Value" ValueName="TOTAL_FUTUR"> |
</element0> |
<element0 location="G53" type="Value" ValueName="TOTAL_PASSE"> |
</element0> |
<table0 firstLine="17" endLine="50" endPageLine="58" lastColumn="G" base="Societe" table="ECHEANCE_CLIENT" pageRef="F1"> |
<element location="A" type="fill" useOOFormat="false"> |
<field base="Societe" name="DATE"/> |
</element> |
<element location="B" type="fill" useOOFormat="false"> |
<field base="Societe" name="PIECE"/> |
</element> |
<element location="C" type="fill" useOOFormat="false" maxChar="25"> |
<field base="Societe" name="LIBELLE"/> |
</element> |
<element location="D" type="fill" useOOFormat="false"> |
<field base="Societe" name="DATE_ECHEANCE"/> |
</element> |
<element location="E" type="fill" > |
<field base="Societe" name="DU"/> |
</element> |
<element location="F" type="fill" > |
<field base="Societe" name="REGLE"/> |
</element> |
<element location="G" type="fill" > |
<field base="Societe" name="SOLDE"/> |
</element> |
</table0> |
</contentDocument> |
/trunk/OpenConcerto/Configuration/Template/Default/CommandeClient.odsp |
---|
1,6 → 1,6 |
<odsp> |
<spliteveryrow> |
<sheet number="0">65</sheet> |
<sheet number="0">66</sheet> |
</spliteveryrow> |
<offset x="40" y ="20"/> |
<resize percent="85"/> |
/trunk/OpenConcerto/Configuration/Template/Default/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/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/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/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/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/FactureFournisseur.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/ReportingVentes.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/LivrePaye.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/EtatStockInventaire.odsp |
---|
New file |
0,0 → 1,9 |
<odsp> |
<spliteveryrow> |
<sheet number="0">57</sheet> |
</spliteveryrow> |
<offset x="40" y ="20"/> |
<resize percent="85"/> |
</odsp> |
/trunk/OpenConcerto/Configuration/Template/Default/JournauxAnalytique.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/ReportingClient.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/ReportingClient.ods |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/OpenConcerto/Configuration/Template/Default/DepotCheque.xml |
---|
New file |
0,0 → 1,60 |
<?xml version="1.0" encoding="UTF-8" ?> |
<contentDocument> |
<element location="H1" type="fill"> |
<field base="Societe" name="DATE"> |
</field> |
</element> |
<element location="K56" type="fill"> |
<field base="Societe" name="MONTANT" type="devise"> |
</field> |
</element> |
<element location="A55" type="fill"> |
<field base="Societe" name="NOM"> |
</field> |
</element> |
<element location="A56" type="fill"> |
<field base="Societe" name="ID_BANQUE"> |
<field base="Societe" name="NOM" prefix ="Dépôt sur la banque "/> |
</field> |
</element> |
<table endPageLine="56" firstLine="5" endLine="53" lastColumn="L" base="Societe" table="DEPOT_CHEQUE_ELEMENT"> |
<element location="A" type="fill"> |
<field name="BANQUE" /> |
</element> |
<element location="B" type="fill"> |
<field name="DATE" /> |
</element> |
<element location="C" type="fill"> |
<field name="NUMERO" /> |
</element> |
<element location="E" type="fill"> |
<field name="ID_CLIENT" > |
<field name="NOM" /> |
</field> |
</element> |
<element location="J" type="fill"> |
<field name="PIECE" /> |
</element> |
<element location="K" type="fill"> |
<field name="MONTANT" type="devise"/> |
</element> |
<element location="L" type="LineReference"> |
<field name="MONTANT" type="LineReference"/> |
</element> |
</table> |
</contentDocument> |
/trunk/OpenConcerto/Configuration/Template/Default/EtatVentes.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/CommandeClient.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/EtatStockInventaire.xml |
---|
New file |
0,0 → 1,33 |
<?xml version="1.0" encoding="UTF-8" ?> |
<contentDocument> |
<!-- <element location="A3" type="Value" ValueName="DATE"> |
</element> |
--> |
<table firstLine="2" endLine="17000" endPageLine="25000" lastColumn="E" table="SAISIE_VENTE_FACTURE"> |
<element location="A" type="fill"> |
<field name="FAMILLE" /> |
</element> |
<element location="B" type="fill"> |
<field name="CODE" /> |
</element> |
<element location="C" type="fill"> |
<field name="NOM" /> |
</element> |
<element location="D" type="fill"> |
<field name="TAILLE" /> |
</element> |
<element location="E" type="fill"> |
<field name="COULEUR" /> |
</element> |
<element location="F" type="fill"> |
<field name="QTE" /> |
</element> |
</table> |
</contentDocument> |
/trunk/OpenConcerto/Configuration/Template/Default/ListeDebiteur.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/BonLivraison.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/FicheRelance.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/FichePayeSimplifiee.xml |
---|
47,7 → 47,7 |
<field name="AU" prefix=" au " type="date" datePattern="dd/MM/yy" /> |
</element> |
<element location="D54" type="fill"> |
<element location="D58" type="fill"> |
<field name="DU" type="datePaye" /> |
</element> |
89,7 → 89,7 |
</element> |
<element location="F12" type="fill"> |
<field name="ID_CUMULS_CONGES"> |
<field name="ACQUIS" /> |
<field name="ACQUIS" type="cumulConges" /> |
</field> |
</element> |
<element location="B6" type="replace" replacePattern="_"> |
122,79 → 122,159 |
</element> |
<element location="I54" type="fill"> |
<field name="NET_A_PAYER" /> |
<element location="H54" type="fill"> |
<field name="NET_AVANT_PAS" /> |
</element> |
<element location="I55" type="fill"> |
<field name="REDUCTION_GVT" /> |
</element> |
<element location="D58" type="fill"> |
<element location="E57" type="fill"> |
<field name="NET_IMP" /> |
</element> |
<element location="F57" type="fill"> |
<field name="ID_PAS"> |
<field name="TAUX_PAS" /> |
</field> |
</element> |
<element location="I57" type="fill"> |
<field name="TOTAL_PAS" /> |
</element> |
<element location="I58" type="fill"> |
<field name="NET_A_PAYER" /> |
</element> |
<element location="D62" type="fill"> |
<field name="SAL_BRUT" /> |
</element> |
<element location="E58" type="fill"> |
<element location="E62" type="fill"> |
<field name="COT_SAL" /> |
</element> |
<element location="F58" type="fill"> |
<element location="F62" type="fill"> |
<field name="COT_PAT" /> |
</element> |
<element location="G58" type="fill"> |
<element location="G62" type="fill"> |
<field name="AVANTAGE_NATURE" /> |
</element> |
<element location="H58" type="fill"> |
<element location="H62" type="fill"> |
<field name="NET_IMP" /> |
</element> |
<element location="I58" type="fill"> |
<element location="I62" type="fill"> |
<field name="ALLEGEMENT_COTISATION" /> |
</element> |
<element location="D59" type="fill"> |
<element location="D63" type="fill"> |
<field name="SAL_BRUT" type="cumulPaye" /> |
</element> |
<element location="E59" type="fill"> |
<element location="E63" type="fill"> |
<field name="COT_SAL" type="cumulPaye" /> |
</element> |
<element location="F59" type="fill"> |
<element location="F63" type="fill"> |
<field name="COT_PAT" type="cumulPaye" /> |
</element> |
<element location="G59" type="fill"> |
<element location="G63" type="fill"> |
<field name="AVANTAGE_NATURE" type="cumulPaye" /> |
</element> |
<element location="H59" type="fill"> |
<element location="H63" type="fill"> |
<field name="NET_IMP" type="cumulPaye" /> |
</element> |
<element location="I59" type="fill"> |
<element location="I63" type="fill"> |
<field name="ALLEGEMENT_COTISATION" type="cumulPaye" /> |
</element> |
<table endPageLine="61" firstLine="15" endLine="51" lastColumn="J" base="Societe" table="FICHE_PAYE_ELEMENT" |
<element location="J16" type="fill"> |
<field name="ID_SALARIE"> |
<field name="CODE" /> |
</field> |
</element> |
<element location="J18" type="fill"> |
<field name="ID_SALARIE"> |
<field name="ID_INFOS_SALARIE_PAYE"> |
<field name="DATE_ARRIVE"> |
</field> |
</field> |
</field> |
</element> |
<element location="J21" type="fichepaye.smic" /> |
<element location="J23" type="fichepaye.plafond" /> |
<element location="J28" type="fill"> |
<field name="ID_VARIABLE_SALARIE"> |
<field name="HEURE_TRAV" /> |
</field> |
</element> |
<element location="J30" type="fichepaye.heure.sup.total"> |
</element> |
<element location="J32" type="fichepaye.heure.total"> |
</element> |
<element location="J35" type="fill"> |
<field name="ID_VARIABLE_SALARIE"> |
<field name="HEURE_TRAV_CUMUL_VAL" /> |
</field> |
</element> |
<element location="J37" type="fichepaye.heure.sup.cumul.total"> |
</element> |
<element location="J39" type="fichepaye.heure.cumul.total"> |
</element> |
<table endPageLine="65" firstLine="15" endLine="51" lastColumn="J" base="Societe" table="FICHE_PAYE_ELEMENT" |
blankLineBeforeStyle="Titre 1,Titre 2" fieldWhere="IMPRESSION" orderBy="POSITION"> |
<element location="B" type="fill" cellSize="52"> |
<field name="NOM" /> |
</element> |
<element location="E" type="fill"> |
<element location="D" type="fill"> |
<field name="NB_BASE" /> |
</element> |
<element location="E" type="fill"> |
<field name="TAUX_SAL"> |
<exclude value="0.00" /> |
<exclude value="0.00%" /> |
<exclude value="0.000000" /> |
</field> |
</element> |
<element location="F" type="fill"> |
<field name="TAUX_SAL" /> |
<field name="MONTANT_SAL_AJ"> |
<exclude value="0.00" /> |
<exclude value="0.00%" /> |
<exclude value="0.000000" /> |
</field> |
</element> |
<element location="G" type="fill"> |
<field name="MONTANT_SAL_AJ" /> |
<field name="MONTANT_SAL_DED"> |
<exclude value="0.00" /> |
<exclude value="0.00%" /> |
<exclude value="0.000000" /> |
</field> |
</element> |
<element location="H" type="fill"> |
<field name="MONTANT_SAL_DED" /> |
<field name="TAUX_PAT"> |
<exclude value="0.00" /> |
<exclude value="0.00%" /> |
<exclude value="0.000000" /> |
</field> |
</element> |
<!-- <element location="I" type="fill"> |
<field name="TAUX_PAT" type="Devise" /> |
</element> --> |
<element location="J" type="fill"> |
<field name="MONTANT_PAT" /> |
<element location="I" type="fill"> |
<field name="MONTANT_PAT"> |
<exclude value="0.00" /> |
<exclude value="0.00%" /> |
<exclude value="0.000000" /> |
</field> |
</element> |
/trunk/OpenConcerto/Configuration/Template/Default/ExportArticle.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/VentilationAnalytique.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/VenteFactureSituation.xml |
---|
24,7 → 24,7 |
</element> |
<element location="B6" type="fill"> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/> |
</element> |
<element location="B7" type="replace" replacePattern="_"> |
/trunk/OpenConcerto/Configuration/Template/Default/EtatChargesPaye.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/VenteFactureTicket.xml |
---|
New file |
0,0 → 1,90 |
<?xml version="1.0" encoding="UTF-8" ?> |
<contentDocument> |
<element0 location="B1" type="Value" ValueName="SOCIETE_NOM"> |
</element0> |
<element0 location="B2" type="Value" ValueName="SOCIETE_RUE"> |
</element0> |
<element0 location="B3" type="Value" ValueName="SOCIETE_CODE_POSTAL_VILLE"> |
</element0> |
<element0 location="B5" type="Value" ValueName="SOCIETE_TYPE_CAPITAL"> |
</element0> |
<element0 location="B6" type="Value" ValueName="SOCIETE_SIRET" prefix="N° de SIREN "> |
</element0> |
<element0 location="B7" type="Value" ValueName="SOCIETE_TVA" prefix="N° de TVA "> |
</element0> |
<element0 location="B8" type="Value" ValueName="SOCIETE_TEL" prefix="N° de Téléphone "> |
</element0> |
<element0 location="B10" type="Value" ValueName="SOCIETE_MAIL" prefix="Email "> |
</element0> |
<element0 location="B13" type="Value" ValueName="NUMERO"> |
</element0> |
<element0 location="C13" type="Value" ValueName="DATE"> |
</element0> |
<element0 location="H10" type="Value" ValueName="CLIENT"> |
</element0> |
<element0 location="H11" type="Value" ValueName="ADRESSE"> |
</element0> |
<element0 location="L62" type="Value" ValueName="T_HT"> |
</element0> |
<element0 location="L65" type="Value" ValueName="T_TTC"> |
</element0> |
<element0 location="L63" type="Value" ValueName="T_TVA_1"> |
</element0> |
<element0 location="I63" type="Value" ValueName="TVA_1"> |
</element0> |
<element0 location="L64" type="Value" ValueName="T_TVA_2"> |
</element0> |
<element0 location="I64" type="Value" ValueName="TVA_2"> |
</element0> |
<table0 endPageLine="66" firstLine="20" endLine="60" blankLineBeforeStyle="Titre 1,Titre 2" lastColumn="K" |
base="Societe" table="SAISIE_VENTE_FACTURE_ELEMENT" pageRef="L16"> |
<element location="B" type="fill" cellSize="45"> |
<field name="CODE" /> |
</element> |
<element location="C" type="fill" cellSize="45"> |
<field name="NOM" /> |
</element> |
<element location="H" type="fill"> |
<field name="POURCENT_REMISE"> |
<exclude value="0.000000" /> |
</field> |
</element> |
<element location="I" type="fill"> |
<field name="PV_HT" type="devise"> |
<exclude value="0.000000" /> |
</field> |
</element> |
<element location="J" type="fill"> |
<field name="QTE" /> |
</element> |
<element location="K" type="fill"> |
<field name="TVA"> |
</field> |
</element> |
<element location="L" type="fill"> |
<field name="T_HT_REMISE" type="devise"> |
<exclude value="0.000000" /> |
</field> |
</element> |
</table0> |
</contentDocument> |
/trunk/OpenConcerto/Configuration/Template/Default/DemandePrix.xml |
---|
25,7 → 25,7 |
</element> |
<element location="B6" type="fill"> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/> |
</element> |
<element location="B7" type="replace" replacePattern="_"> |
/trunk/OpenConcerto/Configuration/Template/Default/FicheClient.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/FactureFournisseur.odsp |
---|
1,6 → 1,6 |
<odsp> |
<spliteveryrow> |
<sheet number="0">57</sheet> |
<sheet number="0">58</sheet> |
</spliteveryrow> |
<offset x="40" y ="20"/> |
<resize percent="85"/> |
/trunk/OpenConcerto/Configuration/Template/Default/VenteFactureTicket.odsp |
---|
New file |
0,0 → 1,9 |
<odsp> |
<spliteveryrow> |
<sheet number="0">66</sheet> |
</spliteveryrow> |
<offset x="40" y ="20"/> |
<resize percent="85"/> |
</odsp> |
/trunk/OpenConcerto/Configuration/Template/Default/BonReception.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/ListeFacture.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/Courrier.odt |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/RepartitionAnalytique.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/Commande.xml |
---|
25,7 → 25,7 |
</element> |
<element location="B6" type="fill"> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/> |
</element> |
<element location="B7" type="replace" replacePattern="_"> |
/trunk/OpenConcerto/Configuration/Template/Default/VenteFacture.xml |
---|
25,7 → 25,7 |
</element> |
<element location="B6" type="fill"> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/> |
</element> |
<element location="B7" type="replace" replacePattern="_"> |
/trunk/OpenConcerto/Configuration/Template/Default/FactureFournisseur.xml |
---|
25,7 → 25,7 |
</element> |
<element location="B6" type="fill"> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN " /> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET " /> |
</element> |
<element location="B7" type="replace" replacePattern="_"> |
/trunk/OpenConcerto/Configuration/Template/Default/Devis.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/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/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/AvoirF.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/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/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/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 Set<SQLTable> toRefresh = new HashSet<SQLTable>(); |
SQLUtils.executeAtomic(t.getDBSystemRoot().getDataSource(), new ConnectionHandlerNoSetup<Object, SQLException>() { |
final TablesMap tables = new TablesMap(); |
SQLUtils.executeAtomic(t.getDBSystemRoot().getDataSource(), new ConnectionHandlerNoSetup<Void, SQLException>() { |
@Override |
public Object handle(final SQLDataSource ds) throws SQLException { |
public Void handle(final SQLDataSource ds) throws SQLException { |
final Set<SQLTable> toRefresh = new HashSet<SQLTable>(); |
// 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,20 → 303,13 |
// 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) { |
/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/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/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/TextSearchSpec.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
55,8 → 55,8 |
this.mode = mode; |
this.filterString = filterString; |
this.normalizedFilterString = normalize(filterString); |
this.formats = new HashMap<Class<?>, FormatGroup>(); |
this.parsedFilter = new HashMap<Format, Object>(); |
this.formats = new HashMap<>(); |
this.parsedFilter = new HashMap<>(); |
} |
private String normalize(String s) { |
88,7 → 88,7 |
private final String format(final Format fmt, final Object cell) { |
try { |
return fmt.format(cell); |
} catch (Exception e) { |
} catch (final Exception e) { |
throw new IllegalStateException("Couldn't format " + cell + '(' + getClass(cell) + ") with " + fmt, e); |
} |
} |
101,7 → 101,7 |
if (!this.parsedFilterD_tried) { |
try { |
this.parsedFilterD = Double.valueOf(this.filterString); |
} catch (NumberFormatException e) { |
} catch (final NumberFormatException e) { |
this.parsedFilterD = null; |
} |
this.parsedFilterD_tried = true; |
/trunk/OpenConcerto/src/org/openconcerto/sql/view/search/SearchItemComponent.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
52,6 → 52,7 |
import javax.swing.event.DocumentEvent; |
import javax.swing.event.TableModelEvent; |
import javax.swing.event.TableModelListener; |
import javax.swing.table.TableModel; |
import javax.swing.text.BadLocationException; |
import net.jcip.annotations.Immutable; |
89,30 → 90,38 |
} |
} |
private JTextField textFieldRecherche = new JTextField(10); |
private final JComboBox comboColonnePourRecherche; |
private final JComboBox searchMode; |
private JCheckBox invertSearch = new JCheckBox(TM.tr("toReverse")); |
private JButton buttonAdd = new JButton("+"); |
private JButton buttonRemove = new JButton(); |
private final JTextField textFieldRecherche = new JTextField(10); |
private final JComboBox<Column> comboColonnePourRecherche; |
private final JComboBox<String> searchMode; |
private final JCheckBox invertSearch = new JCheckBox(TM.tr("toReverse")); |
private final JButton buttonAdd = new JButton("+"); |
private final JButton buttonRemove = new JButton(); |
final SearchListComponent list; |
// final to ease removing listener, SearchListComponent.reset() removes every item |
// when the TableModel changes. |
private final TableModel tableModel; |
private String text = ""; |
public SearchItemComponent(final SearchListComponent list) { |
super(); |
this.list = list; |
this.tableModel = this.list.getTableModel(); |
this.setOpaque(false); |
// Initialisation de l'interface graphique |
this.searchMode = new JComboBox(new String[] { TM.tr("contains"), TM.tr("contains.exactly"), TM.tr("isLessThan"), TM.tr("isEqualTo"), TM.tr("isExactlyEqualTo"), TM.tr("isGreaterThan"), |
TM.tr("isEmpty") }); |
final ListComboBoxModel comboModel = new ListComboBoxModel(Arrays.asList(TOUT)); |
this.searchMode = new JComboBox<>( |
new String[] { TM.tr("contains"), TM.tr("contains.exactly"), TM.tr("isLessThan"), TM.tr("isEqualTo"), TM.tr("isExactlyEqualTo"), TM.tr("isGreaterThan"), TM.tr("isEmpty") }); |
final ListComboBoxModel<Column> comboModel = new ListComboBoxModel<>(Arrays.asList(TOUT)); |
comboModel.setSelectOnAdd(false); |
// allow getColIndex() and thus getSearchItem() to work from now on |
assert comboModel.getSelectedItem() != null; |
this.comboColonnePourRecherche = new JComboBox(comboModel); |
this.comboColonnePourRecherche = new JComboBox<>(comboModel); |
uiInit(); |
} |
final TableModel getTableModel() { |
return this.tableModel; |
} |
private void uiInit() { |
this.setLayout(new GridBagLayout()); |
final GridBagConstraints c = new GridBagConstraints(); |
123,11 → 132,12 |
// designation |
// don't just use DefaultListCellRenderer, it fails on some l&f |
final ListCellRenderer old = this.comboColonnePourRecherche.getRenderer(); |
this.comboColonnePourRecherche.setRenderer(new ListCellRenderer() { |
@SuppressWarnings("unchecked") |
final ListCellRenderer<Object> old = (ListCellRenderer<Object>) this.comboColonnePourRecherche.getRenderer(); |
this.comboColonnePourRecherche.setRenderer(new ListCellRenderer<Column>() { |
@Override |
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { |
return old.getListCellRendererComponent(list, ((Column) value).getLabel(), index, isSelected, cellHasFocus); |
public Component getListCellRendererComponent(JList<? extends Column> list, Column value, int index, boolean isSelected, boolean cellHasFocus) { |
return old.getListCellRendererComponent(list, value.getLabel(), index, isSelected, cellHasFocus); |
} |
}); |
// hand tuned for a IListPanel width of 1024px |
171,11 → 181,13 |
initInvertSearch(); |
this.buttonAdd.addActionListener(new ActionListener() { |
@Override |
public void actionPerformed(ActionEvent e) { |
SearchItemComponent.this.list.addNewSearchItem(); |
} |
}); |
this.buttonRemove.addActionListener(new ActionListener() { |
@Override |
public void actionPerformed(ActionEvent e) { |
SearchItemComponent.this.list.removeSearchItem(SearchItemComponent.this); |
} |
184,6 → 196,7 |
private void initInvertSearch() { |
this.invertSearch.addActionListener(new ActionListener() { |
@Override |
public void actionPerformed(ActionEvent e) { |
updateSearchList(); |
} |
192,12 → 205,13 |
private void initSearchText() { |
this.textFieldRecherche.getDocument().addDocumentListener(new SimpleDocumentListener() { |
@Override |
public void update(DocumentEvent e) { |
try { |
// One ne peut pas appeler chercher() car le texte n'est pas encore a jour |
SearchItemComponent.this.text = e.getDocument().getText(0, e.getDocument().getLength()).trim(); |
updateSearchList(); |
} catch (BadLocationException exn) { |
} catch (final BadLocationException exn) { |
// impossible |
exn.printStackTrace(); |
} |
207,6 → 221,7 |
private void initCombo() { |
final ItemListener listener = new ItemListener() { |
@Override |
public void itemStateChanged(ItemEvent e) { |
updateSearchList(); |
} |
223,13 → 238,14 |
}; |
// allow the TableModel to die |
this.addHierarchyListener(new HierarchyListener() { |
@Override |
public void hierarchyChanged(HierarchyEvent e) { |
if ((e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) != 0) { |
if (e.getChanged().isDisplayable()) { |
columnsChanged(listener); |
SearchItemComponent.this.list.getTableModel().addTableModelListener(tableModelL); |
SearchItemComponent.this.getTableModel().addTableModelListener(tableModelL); |
} else { |
SearchItemComponent.this.list.getTableModel().removeTableModelListener(tableModelL); |
SearchItemComponent.this.getTableModel().removeTableModelListener(tableModelL); |
} |
} |
} |
244,7 → 260,7 |
private void fillColumnCombo(ItemListener listener) { |
// sort column names alphabetically |
final int columnCount = this.list.getTableModel().getColumnCount(); |
final int columnCount = this.getTableModel().getColumnCount(); |
final String[][] names = new String[columnCount][]; |
final int[] indexes = new int[columnCount]; |
for (int i = 0; i < columnCount; i++) { |
253,7 → 269,7 |
} |
// use column index as columns names are not unique |
final SortedMap<String, Integer> map = solve(names, indexes); |
final List<Column> cols = new ArrayList<Column>(columnCount); |
final List<Column> cols = new ArrayList<>(columnCount); |
cols.add(TOUT); |
for (final Entry<String, Integer> e : map.entrySet()) { |
final int colIndex = e.getValue().intValue(); |
263,7 → 279,7 |
// don't fire when filling, we will fire when selecting |
this.comboColonnePourRecherche.removeItemListener(listener); |
final ListComboBoxModel comboModel = (ListComboBoxModel) this.comboColonnePourRecherche.getModel(); |
final ListComboBoxModel<Column> comboModel = (ListComboBoxModel<Column>) this.comboColonnePourRecherche.getModel(); |
assert !comboModel.isSelectOnAdd() : "Otherwise our following select might not fire"; |
comboModel.removeAllElements(); |
comboModel.addAll(cols); |
273,7 → 289,7 |
private void columnsChanged(final ItemListener listener) { |
final String currentID = ((Column) this.comboColonnePourRecherche.getSelectedItem()).getID(); |
fillColumnCombo(listener); |
final ListComboBoxModel comboModel = (ListComboBoxModel) this.comboColonnePourRecherche.getModel(); |
final ListComboBoxModel<Column> comboModel = (ListComboBoxModel<Column>) this.comboColonnePourRecherche.getModel(); |
// no selection since the model was just emptied |
assert this.comboColonnePourRecherche.getSelectedIndex() == -1 && this.comboColonnePourRecherche.getSelectedItem() == null; |
// try to reselect the same column if it's still there |
296,7 → 312,7 |
private SortedMap<String, Integer> solve(final String[][] names, final int[] indexes) { |
final int columnCount = names.length; |
// columns' index by name |
final ListMap<String, Integer> collisions = new ListMap<String, Integer>(columnCount); |
final ListMap<String, Integer> collisions = new ListMap<>(columnCount); |
for (int i = 0; i < columnCount; i++) { |
final int index = indexes[i]; |
if (index >= names[i].length) |
304,8 → 320,8 |
final String columnName = names[i][index]; |
collisions.add(columnName, i); |
} |
final SortedMap<String, Integer> res = new TreeMap<String, Integer>(); |
for (Entry<String, ? extends Collection<Integer>> e : collisions.entrySet()) { |
final SortedMap<String, Integer> res = new TreeMap<>(); |
for (final Entry<String, ? extends Collection<Integer>> e : collisions.entrySet()) { |
final Collection<Integer> indexesWithCollision = e.getValue(); |
if (indexesWithCollision.size() > 1) { |
// increment only the minimum indexes to try to solve the conflict with the lowest |
/trunk/OpenConcerto/src/org/openconcerto/sql/view/search/DateSearchSpec.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
18,10 → 18,10 |
public class DateSearchSpec implements SearchSpec { |
// include or exclude filterString |
private boolean excludeFilterString; |
private int columnIndex; |
private Date fromDate; |
private Date toDate; |
private final boolean excludeFilterString; |
private final int columnIndex; |
private final Date fromDate; |
private final Date toDate; |
public DateSearchSpec(Date from, Date to, int columnIndex) { |
this(false, from, to, columnIndex); |
44,8 → 44,7 |
* @return <code>true</code> si la ligne contient. |
*/ |
static private boolean contains(Object line, Date startDate, Date stopDate, int index) { |
List list = (List) line; |
final List<?> list = (List<?>) line; |
final int start; |
final int stop; |
if (index < 0) { |
62,7 → 61,7 |
final Object cell = list.get(i); |
if (cell != null) { |
if (cell instanceof Date) { |
Date date = (Date) cell; |
final Date date = (Date) cell; |
if (date.after(startDate) && date.before(stopDate)) { |
return true; |
} |
75,6 → 74,7 |
return false; |
} |
@Override |
public boolean match(Object line) { |
return this.excludeFilterString ^ contains(line, this.fromDate, this.toDate, this.columnIndex); |
} |
83,6 → 83,7 |
System.out.println(this.excludeFilterString + ":" + this.fromDate + "->" + this.toDate + " col:" + this.columnIndex); |
} |
@Override |
public boolean isEmpty() { |
return true; |
} |
/trunk/OpenConcerto/src/org/openconcerto/sql/view/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/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/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/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/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/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(); |
} |
RowValuesTableControlPanel.this.model.removeRowsAt(table.getSelectedRows()); |
SQLField validationField = RowValuesTableControlPanel.this.model.getValidationField(); |
if (validationField != null) { |
boolean canDelete = true; |
for (int i : table.getSelectedRows()) { |
SQLRowValues rowVals = RowValuesTableControlPanel.this.model.getRowValuesAt(i); |
canDelete &= (!rowVals.contains(validationField.getName()) || rowVals.getObject(validationField.getName()) == null || !rowVals.getBoolean(validationField.getName())); |
} |
if (canDelete) { |
RowValuesTableControlPanel.this.model.removeRowsAt(table.getSelectedRows()); |
} |
// MAYBE show popup if can't delete |
} else { |
RowValuesTableControlPanel.this.model.removeRowsAt(table.getSelectedRows()); |
} |
table.clearSelection(); |
} |
}); |
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/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,7 → 237,9 |
} |
rowVal.put(fieldName, value); |
for (SQLTableElement sqlTableElem : this.list) { |
sqlTableElem.fireModification(rowVal); |
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/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,37 → 153,34 |
*/ |
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(); |
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(); |
if (user != null) |
update.setObject(SQLComponent.READ_ONLY_USER_FIELD, user.getId()); |
update.setWhere(new Where(t.getKey(), ids)); |
t.getDBSystemRoot().getDataSource().execute(update.asString()); |
// don't fire too many times, as each one will cause UpdateQueue to issue a |
// request |
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); |
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(); |
if (user != null) |
update.setObject(SQLComponent.READ_ONLY_USER_FIELD, user.getId()); |
update.setWhere(new Where(t.getKey(), ids)); |
t.getDBSystemRoot().getDataSource().execute(update.asString()); |
// don't fire too many times, as each one will cause UpdateQueue to issue a |
// request |
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()); |
}); |
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()); |
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 |
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); |
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/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,7 → 97,23 |
this.setRow(row); |
this.id = id; |
this.state = state; |
this.clearCache(); |
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 |
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,7 → 182,16 |
} else { |
if (order2 != null) |
throw new IllegalStateException("Order mismatch :\n" + order1 + " for " + l1 + " not coherent with\n" + order2 + " for " + l2); |
return l1.getState().getReq().order(l1.getRow(), l2.getRow()); |
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()); |
} |
} |
} |
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/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/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 |
144,10 → 148,12 |
return res instanceof SQLRowValues ? ((SQLRowValues) res).getID() : res; |
} |
}); |
} |
} |
}, true); |
this.debugCols.add(new DebugRow(getPrimaryTable())); |
if (moreDebugCols) |
this.debugCols.add(new DebugRow(getPrimaryTable())); |
final SQLField orderField = getPrimaryTable().getOrderField(); |
if (orderField != null) |
this.debugCols.add(new SQLTableModelColumnPath(Path.get(getPrimaryTable()), orderField.getName(), "Order")); |
/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,14 → 29,19 |
@Override |
public void run() { |
if (this.getTable() == this.getReq().getParent().getPrimaryTable()) { |
final Value<ListSQLLine> val = this.getReq().get(this.getID()); |
if (!val.hasValue()) |
return; |
updateLines(this.getAffectedPaths(), val); |
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; |
updateLines(this.getAffectedPaths(), val); |
} else { |
// eg CONTACT[3] has changed |
updateLines(this.getAffectedPaths()); |
} |
} else { |
// eg CONTACT[3] has changed |
updateLines(this.getAffectedPaths()); |
this.updateLines(this.getUpdateQ().getAffectedLines(this.getRow()), isPrimaryTable ? this.getID() : null); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/MoveQueue.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
82,7 → 82,7 |
SQLUtils.executeAtomic(getTable().getDBSystemRoot().getDataSource(), new ConnectionHandlerNoSetup<Object, Exception>() { |
@Override |
public Object handle(SQLDataSource ds) throws Exception { |
moveQuick(rows, after, destID.get().getRow().asRow()); |
moveQuick(rows, after, destID.get().getRowAccessor().asRow()); |
return null; |
} |
}); |
107,7 → 107,7 |
final int rowCount = this.tableModel.getRowCount(); |
final boolean after = rowIndex >= rowCount; |
final int index = after ? rowCount - 1 : rowIndex; |
final SQLRowValues line = this.tableModel.getRow(index).getRow(); |
final SQLRowAccessor line = this.tableModel.getRow(index).getRowAccessor(); |
assert line.isFrozen() : "row could change by the time move() is called"; |
return this.put(new Runnable() { |
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/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)) { |
// works for rowValues w/o any ID |
if (current != null && current.getID() == id) { |
put = true; |
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) { |
put = true; |
/* |
* 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/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,78 → 35,18 |
*/ |
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(); |
} |
} |
/** |
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/action/SQLRowValuesAction.java |
---|
New file |
0,0 → 1,157 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific |
* language governing permissions and limitations under the License. |
* |
* When distributing the software, include this License Header Notice in each file. |
*/ |
package org.openconcerto.sql.view.list.action; |
import org.openconcerto.utils.cc.IPredicate; |
import org.openconcerto.utils.i18n.TranslationManager; |
import java.util.ArrayList; |
import java.util.Arrays; |
import java.util.Collections; |
import java.util.List; |
import java.util.function.Consumer; |
/** |
* An action that act on rows of a list. Either {@link #enabledFor(ListEvent)} or |
* {@link #enabledFor(List)} must be overloaded. |
* |
* @author Sylvain CUAZ |
*/ |
public abstract class SQLRowValuesAction { |
public static class PredicateRowAction extends SQLRowValuesAction { |
private IPredicate<? super ListEvent> pred = null; |
public PredicateRowAction(boolean header, Consumer<? super ListEvent> action) { |
super(header, action); |
} |
public PredicateRowAction(boolean header, boolean popupMenu, Consumer<? super ListEvent> action) { |
super(header, popupMenu, action); |
} |
public PredicateRowAction(boolean header, final String id, Consumer<? super ListEvent> action) { |
super(header, id, action); |
} |
public PredicateRowAction(boolean header, boolean popupMenu, final String id, Consumer<? super ListEvent> action) { |
super(header, popupMenu, id, action); |
} |
public final PredicateRowAction setPredicate(IPredicate<? super ListEvent> pred) { |
if (pred == null) { |
throw new IllegalArgumentException("null predicate"); |
} |
this.pred = pred; |
return this; |
} |
public final IPredicate<? super ListEvent> getPredicate() { |
return this.pred; |
} |
@Override |
public boolean enabledFor(ListEvent evt) { |
if (this.pred == null) { |
throw new IllegalStateException("No predicate for " + this); |
} |
return this.pred.evaluateChecked(evt); |
} |
} |
private final Consumer<? super ListEvent> action; |
private final boolean header, popupMenu; |
private List<String> path; |
private final String id; |
private String name; |
public SQLRowValuesAction(boolean header, Consumer<? super ListEvent> action) { |
this(header, true, action); |
} |
public SQLRowValuesAction(boolean header, boolean popupMenu, Consumer<? super ListEvent> action) { |
this(header, popupMenu, null, action); |
} |
public SQLRowValuesAction(boolean header, final String id, Consumer<? super ListEvent> action) { |
this(header, true, id, action); |
} |
public SQLRowValuesAction(boolean header, boolean popupMenu, final String id, Consumer<? super ListEvent> action) { |
super(); |
this.action = action; |
this.header = header; |
this.popupMenu = popupMenu; |
this.setGroup(null); |
this.id = id; |
if (id != null) |
this.setName(TranslationManager.getInstance().getTranslationForAction(id)); |
} |
public final String getID() { |
return this.id; |
} |
public final SQLRowValuesAction setName(String name) { |
this.name = name; |
return this; |
} |
public final String getName() { |
return this.name; |
} |
public final Consumer<? super ListEvent> getAction() { |
return this.action; |
} |
public final boolean inHeader() { |
return this.header; |
} |
public final boolean inPopupMenu() { |
return this.popupMenu; |
} |
public final SQLRowValuesAction setGroup(String groupName) { |
this.path = Arrays.asList(groupName); |
return this; |
} |
public final SQLRowValuesAction setPath(List<String> path) { |
this.path = Collections.unmodifiableList(new ArrayList<String>(path)); |
return this; |
} |
public final List<String> getPath() { |
return this.path; |
} |
/** |
* Whether the action should be enabled in the header or in the popup. |
* |
* @param evt the state of the IListe. |
* @return <code>true</code> if the action can be performed. |
*/ |
public abstract boolean enabledFor(ListEvent evt); |
public Consumer<? super ListEvent> getDefaultAction(ListEvent evt) { |
return this.enabledFor(evt) ? this.getAction() : null; |
} |
@Override |
public String toString() { |
return this.getClass().getSimpleName() + (this.getID() == null ? "" : " ID '" + this.getID()) + "'"; |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/action/ListEvent.java |
---|
New file |
0,0 → 1,127 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific |
* language governing permissions and limitations under the License. |
* |
* When distributing the software, include this License Header Notice in each file. |
*/ |
package org.openconcerto.sql.view.list.action; |
import org.openconcerto.sql.element.SQLElement; |
import org.openconcerto.sql.model.SQLRowAccessor; |
import org.openconcerto.sql.model.SQLRowValues; |
import org.openconcerto.sql.model.SQLTable; |
import org.openconcerto.utils.CollectionUtils; |
import org.openconcerto.utils.cc.IPredicate; |
import java.util.ArrayList; |
import java.util.List; |
import java.util.Objects; |
public class ListEvent { |
static private final IPredicate<ListEvent> emptyTotalRowCountPredicate = createTotalRowCountPredicate(0, 0); |
static public final IPredicate<ListEvent> getEmptyListPredicate() { |
return emptyTotalRowCountPredicate; |
} |
static public final IPredicate<ListEvent> createTotalRowCountPredicate(final int min, final int max) { |
return new IPredicate<ListEvent>() { |
@Override |
public boolean evaluateChecked(ListEvent e) { |
return e.getTotalRowCount() >= min && e.getTotalRowCount() <= max; |
} |
}; |
} |
static private final IPredicate<ListEvent> singleSelectionPredicate = createSelectionCountPredicate(1, 1); |
static private final IPredicate<ListEvent> nonEmptySelectionPredicate = createNonEmptySelectionPredicate(Integer.MAX_VALUE); |
static public final IPredicate<ListEvent> getSingleSelectionPredicate() { |
return singleSelectionPredicate; |
} |
static public final IPredicate<ListEvent> getNonEmptySelectionPredicate() { |
return nonEmptySelectionPredicate; |
} |
static public final IPredicate<ListEvent> createNonEmptySelectionPredicate(final int max) { |
return createSelectionCountPredicate(1, max); |
} |
static public final IPredicate<ListEvent> createSelectionCountPredicate(final int min, final int max) { |
return new IPredicate<ListEvent>() { |
@Override |
public boolean evaluateChecked(ListEvent e) { |
// this is the fastest since it involves no object creation |
final List<?> selectedIDs = e.getSelectedRowAccessors(); |
return selectedIDs.size() >= min && selectedIDs.size() <= max; |
} |
}; |
} |
static public final ListEvent createFromRowValues(final Object source, final SQLElement elem, final int totalRowCount, final List<SQLRowValues> selection) { |
return new ListEvent(source, elem, totalRowCount, selection, selection); |
} |
static public final ListEvent createFromRowAccessors(final Object source, final SQLElement elem, final int totalRowCount, final List<? extends SQLRowAccessor> selection) { |
return new ListEvent(source, elem, totalRowCount, null, selection); |
} |
private final Object source; |
private final SQLElement elem; |
private final int totalRowCount; |
private final List<SQLRowValues> selection; |
private final List<? extends SQLRowAccessor> selectionAccessor; |
protected ListEvent(final Object source, final SQLElement elem, final int totalRowCount, final List<SQLRowValues> selection, final List<? extends SQLRowAccessor> selectionAccessor) { |
super(); |
this.source = source; |
this.elem = elem; |
this.totalRowCount = totalRowCount; |
assert selection == null || selection == selectionAccessor : "Wasting memory"; |
this.selection = selection; |
this.selectionAccessor = Objects.requireNonNull(selectionAccessor); |
} |
public final Object getSource() { |
return this.source; |
} |
public final SQLRowAccessor getSelectedRow() { |
return CollectionUtils.getFirst(this.getSelectedRowAccessors()); |
} |
public final List<SQLRowValues> getSelectedRows() { |
if (this.selection == null) |
throw new IllegalStateException("SQLRowValues unavailable, use getSelectedRowAccessors()"); |
return this.selection; |
} |
public final List<? extends SQLRowAccessor> getSelectedRowAccessors() { |
return this.selectionAccessor; |
} |
public final List<Number> getSelectedIDs() { |
return SQLRowAccessor.getIDs(this.getSelectedRowAccessors(), new ArrayList<>()); |
} |
public final int getTotalRowCount() { |
return this.totalRowCount; |
} |
public final SQLTable getTable() { |
return this.getElement().getTable(); |
} |
public final SQLElement getElement() { |
return this.elem; |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/sql/view/list/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/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/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")); |
Where wContains = new Where(this.tableArticle.getField("CODE"), "LIKE", "%" + aText + "%"); |
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,24 → 417,68 |
List<SQLRowValues> resultCodeF = fetcher.fetch(); |
resultList.add(2, resultCodeF); |
Map<String, Map<Number, SQLRowAccessor>> mapDecl = new HashMap<>(); |
for (List<? extends SQLRowAccessor> list : resultList) { |
for (SQLRowAccessor sqlRow : list) { |
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")) { |
SQLRowAccessor rArt = sqlRow.getForeign("ID_ARTICLE"); |
buf.append(sqlRow.getString("CODE") + " -- "); |
buf.append(rArt.getString("CODE") + " -- "); |
buf.append(rArt.getString("NOM")); |
result.add(new IComboSelectionItem(rArt, buf.toString())); |
} else { |
if (sqlRow.getString("CODE_BARRE") != null && sqlRow.getString("CODE_BARRE").trim().length() > 0) { |
buf.append(sqlRow.getString("CODE_BARRE") + " -- "); |
StringBuffer buf = new StringBuffer(); |
if (sqlRow.getTable().getName().equals("CODE_FOURNISSEUR")) { |
SQLRowAccessor rArt = sqlRow.getForeign("ID_ARTICLE"); |
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) { |
buf.append(sqlRow.getString("CODE_BARRE") + " -- "); |
} |
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())); |
} |
} |
buf.append(sqlRow.getString("CODE") + " -- "); |
buf.append(sqlRow.getString("NOM")); |
result.add(new IComboSelectionItem(sqlRow, buf.toString())); |
} |
} |
} |
502,8 → 580,19 |
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); |
} |
} |
} |
/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); |
addField(graphToFetch, getPrimaryTable().getCreationDateField()); |
addField(graphToFetch, getPrimaryTable().getCreationUserField()); |
addField(graphToFetch, getPrimaryTable().getModifDateField()); |
addField(graphToFetch, getPrimaryTable().getModifUserField()); |
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/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,7 → 163,10 |
sel.addSelectStar(t); |
else |
sel.addAllSelect(t, fields); |
sel.setWhere(new Where(t.getKey(), ids)); |
// deterministic |
sel.addOrder(t, false); |
if (ids != null) |
sel.setWhere(new Where(t.getKey(), ids)); |
return execute(sel); |
} |
194,24 → 199,6 |
return new SQLSelectHandlerBuilder(sel).setTableRef(t).execute(); |
} |
static IResultSetHandler createFromSelect(final SQLSelect sel, final Tuple2<SQLTable, List<String>> indexes, final boolean readCache, final boolean writeCache) { |
final Set<SQLTable> tables = new HashSet<SQLTable>(); |
// not just tables of the fields of the SELECT clause since inner joins can change the rows |
// returned |
for (final TableRef ref : sel.getTableRefs().values()) { |
tables.add(ref.getTable()); |
} |
// the SELECT requesting a lock means the caller expects the DB to be accessed |
final boolean acquireLock = sel.getLockStrength() != LockStrength.NONE; |
return new IResultSetHandler(create(indexes), readCache && !acquireLock, writeCache && !acquireLock) { |
@Override |
public Set<? extends SQLData> getCacheModifiers() { |
return tables; |
} |
}; |
} |
private final SQLTable t; |
private final boolean tableOnly; |
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSelectHandlerBuilder.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
13,10 → 13,13 |
package org.openconcerto.sql.model; |
import org.openconcerto.sql.model.SQLSelect.LockStrength; |
import org.openconcerto.utils.Tuple2; |
import org.openconcerto.utils.Value; |
import java.util.HashSet; |
import java.util.List; |
import java.util.Set; |
public final class SQLSelectHandlerBuilder { |
23,6 → 26,7 |
private final SQLSelect sel; |
private Value<TableRef> t; |
private boolean readCache, writeCache; |
private boolean immutableRows; |
public SQLSelectHandlerBuilder(final SQLSelect sel) { |
if (sel == null) |
30,6 → 34,7 |
this.sel = sel; |
this.t = Value.getNone(); |
this.setUseCache(true); |
this.immutableRows = true; |
} |
public SQLSelectHandlerBuilder setUseCache(final boolean b) { |
56,6 → 61,15 |
return this.writeCache; |
} |
public final SQLSelectHandlerBuilder setImmutableRows(boolean immutableRows) { |
this.immutableRows = immutableRows; |
return this; |
} |
public final boolean isImmutableRows() { |
return this.immutableRows; |
} |
/** |
* Set the table of the rows to be created. Must be used if the query has more than one table. |
* |
77,7 → 91,21 |
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") |
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSystem.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
16,6 → 16,7 |
import org.openconcerto.sql.utils.SQL_URL; |
import org.openconcerto.utils.EnumOrderedSet; |
import org.openconcerto.utils.Tuple2; |
import org.openconcerto.utils.Tuple2.List2; |
import org.openconcerto.utils.cc.ITransformer; |
import java.sql.Statement; |
23,8 → 24,10 |
import java.util.HashMap; |
import java.util.Iterator; |
import java.util.Map; |
import java.util.Objects; |
import java.util.Set; |
import org.h2.engine.ConnectionInfo; |
import org.h2.engine.Constants; |
import org.h2.util.StringUtils; |
135,7 → 138,7 |
H2("H2") { |
private static final String TCP_PREFIX = "tcp://"; |
private static final String SSL_PREFIX = "ssl://"; |
private static final String ARBITRARY_BASE_NAME = "foo"; |
ITransformer<String, String> getURLTransf(final SQLServer s) { |
if (s.getSQLSystem() != this) |
144,21 → 147,10 |
return new ITransformer<String, String>() { |
@Override |
public String transformChecked(String base) { |
final String sep; |
// allow one to use mem for server name |
// otherwise just cat name and base |
// (eg "tcp://127.0.0.1/" + "sample", "file:~/" + "sample" or "" + "sample" ) |
if (s.getName().equals("mem")) |
sep = ":"; |
else { |
// for file, pass either file:, or file:/someDir/ |
// jdbc:h2:~/test |
sep = ""; |
} |
// by default h2 convert database name to upper case (we used to work around it |
// with SQLSystem.getMDName() but in r2251 an equalsIgnoreCase() was replaced by |
// equals()) see http://code.google.com/p/h2database/issues/detail?id=204 |
return s.getName() + sep + base + ";DATABASE_TO_UPPER=false"; |
return s.getName() + base + ";DATABASE_TO_UPPER=false"; |
} |
}; |
} |
187,26 → 179,33 |
} |
@Override |
public String getHostname(final String server) { |
final String prefix; |
if (server.startsWith(TCP_PREFIX)) |
prefix = TCP_PREFIX; |
else if (server.startsWith(SSL_PREFIX)) |
prefix = SSL_PREFIX; |
else |
return null; |
public List2<String> getHostnameAndPath(final String server) { |
// append base name to server name to get a valid value |
final ConnectionInfo info = new ConnectionInfo(server + ARBITRARY_BASE_NAME); |
final String name = info.getName(); |
final String hostName; |
final int pathIndex; |
if (info.isRemote()) { |
// tcp:// or ssl://server/path |
assert name.startsWith("//"); |
final int slashIndex = name.indexOf('/', 2); |
hostName = name.substring(2, slashIndex); |
pathIndex = slashIndex + 1; |
} else { |
// mem: or file:/data/sample or ~/test |
hostName = null; |
pathIndex = 0; |
} |
// check that our name doesn't contain a path, otherwise we would loose it |
// eg dbserv:8084/~/sample |
final String hostAndPath = server.substring(prefix.length()); |
final int firstSlash = hostAndPath.indexOf('/'); |
if (firstSlash == hostAndPath.lastIndexOf('/')) { |
return hostAndPath.substring(0, firstSlash); |
} else |
return null; |
return new List2<>(hostName, name.substring(pathIndex, name.length() - ARBITRARY_BASE_NAME.length())); |
} |
@Override |
public boolean isPermanent(final String server) { |
return !server.startsWith(H2_IN_MEMORY); |
} |
@Override |
public Map<String, String> getConnectionInfo(final String url) { |
final Tuple2<String, Map<String, String>> settings = readSettingsFromURL(url); |
final Map<String, String> res = new HashMap<String, String>(); |
316,6 → 315,8 |
} |
} |
public static final String H2_IN_MEMORY = "mem:"; |
private final String label; |
private final EnumOrderedSet<HierarchyLevel> levels; |
422,15 → 423,37 |
/** |
* The host name for the passed server. |
* |
* @param server the name of an {@link SQLServer}. |
* @return its host or <code>null</code> if <code>server</code> has no host or is too complex |
* (eg tcp://127.0.0.1/a/b/c). |
* @param server the name of an {@link SQLServer}, e.g. tcp://127.0.0.1/dir/. |
* @return its host and its path, both can be <code>null</code> (but not at the same time), e.g. |
* [127.0.0.1, dir]. |
*/ |
public String getHostname(String server) { |
return server; |
public List2<String> getHostnameAndPath(String server) { |
Objects.requireNonNull(server, "Null server"); |
return new List2<>(server, null); |
} |
/** |
* Whether the passed server runs inside the VM. |
* |
* @param server a server, e.g. {@link SQLServer#getName()}. |
* @return <code>true</code> if <code>server</code> runs inside the VM. |
*/ |
public final boolean isEmbedded(final String server) { |
return getHostnameAndPath(server).get0() == null; |
} |
/** |
* Whether the passed server survives when all connections to it are closed. |
* |
* @param server a server, e.g. {@link SQLServer#getName()}. |
* @return <code>true</code> if <code>server</code> survives when all connections to it are |
* closed. |
*/ |
public boolean isPermanent(String server) { |
return true; |
} |
/** |
* Parse <code>url</code> to find info needed by {@link SQL_URL}. |
* |
* @param url a jdbc url, eg |
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLTable.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
20,6 → 20,7 |
import org.openconcerto.sql.model.SQLSelect.LockStrength; |
import org.openconcerto.sql.model.SQLSyntax.ConstraintType; |
import org.openconcerto.sql.model.SQLTableEvent.Mode; |
import org.openconcerto.sql.model.Where.RowComparison; |
import org.openconcerto.sql.model.graph.DatabaseGraph; |
import org.openconcerto.sql.model.graph.Link; |
import org.openconcerto.sql.model.graph.Link.Rule; |
30,6 → 31,7 |
import org.openconcerto.sql.utils.ChangeTable; |
import org.openconcerto.sql.utils.PartialUniqueTrigger; |
import org.openconcerto.sql.utils.SQLCreateMoveableTable; |
import org.openconcerto.sql.utils.SQLUtils; |
import org.openconcerto.sql.utils.UniqueConstraintCreatorHelper; |
import org.openconcerto.utils.CollectionUtils; |
import org.openconcerto.utils.CompareUtils; |
48,6 → 50,8 |
import java.sql.DatabaseMetaData; |
import java.sql.ResultSet; |
import java.sql.SQLException; |
import java.sql.Statement; |
import java.sql.Types; |
import java.util.ArrayList; |
import java.util.Arrays; |
import java.util.Collection; |
63,6 → 67,7 |
import java.util.ListIterator; |
import java.util.Map; |
import java.util.Map.Entry; |
import java.util.Objects; |
import java.util.Set; |
import java.util.regex.Matcher; |
import java.util.regex.Pattern; |
122,7 → 127,7 |
}; |
@SuppressWarnings("unchecked") |
public static final Map<String, Number> getUndefIDs(final SQLSchema schema) { |
private static final Map<String, Number> getUndefIDs(final SQLSchema schema) { |
assert Thread.holdsLock(UNDEFINED_IDs); |
if (!UNDEFINED_IDs.containsKey(schema)) { |
final Map<String, Number> r; |
255,12 → 260,51 |
} |
final int res = toInsert.size() + toUpdate.size(); |
if (res > 0) { |
undefT.fireTableModified(SQLRow.NONEXISTANT_ID); |
undefT.fireTableModified(); |
} |
return res; |
} |
} |
public static final boolean unsetUndefIDs(SQLSchema schema, Set<String> tableNames) throws SQLException { |
final boolean tableLoaded = schema.getTable(undefTable) != null; |
final boolean tableExists = unsetUndefIDs(schema.getDBSystemRoot(), schema.getDBRoot().getName(), tableNames); |
if (tableLoaded != tableExists) |
throw new IllegalStateException("Root not up to date, table loaded : " + tableLoaded + ", table exists : " + tableExists); |
return tableExists; |
} |
public static final boolean unsetUndefIDs(final DBSystemRoot sysRoot, final String rootName, Set<String> tableNames) throws SQLException { |
final SQLName undefSQLName = new SQLName(Objects.requireNonNull(rootName, "Missing root name"), undefTable); |
final int deletedCount; |
try { |
// If already in a transaction, don't risk aborting it if a table doesn't exist. |
// (it's not strictly required for H2 and MySQL, since the transaction is *not* |
// aborted) |
deletedCount = SQLUtils.executeAtomic(sysRoot.getDataSource(), (ds) -> { |
try (final Statement stmt = ds.getConnection().createStatement()) { |
final int res = stmt.executeUpdate("DELETE FROM " + undefSQLName + " WHERE " |
+ Where.getCompareValuesClause(SQLBase.quoteIdentifier(UNDEF_TABLE_TABLENAME_FIELD), RowComparison.IN, tableNames, SQLType.getFromSyntax(sysRoot.getSyntax(), Types.VARCHAR, 250))); |
assert res >= 0; |
return res; |
} |
}); |
} catch (SQLException e) { |
// nothing to unset |
if (sysRoot.getSyntax().isTableNotFoundException(e)) |
return false; |
throw e; |
} |
if (deletedCount > 0) { |
// rootName might exist and thus the above query might succeed, but the root might not |
// be loaded or up to date. |
final SQLTable undefT = sysRoot.getDescLenient(undefSQLName, SQLTable.class); |
if (undefT != null) |
undefT.fireTableModified(); |
} |
return true; |
} |
static private boolean AFTER_TX_DEFAULT = true; |
static public void setDefaultAfterTransaction(final boolean val) { |
799,14 → 843,42 |
} |
/** |
* Return the primary keys of this table. |
* Return the fields of the primary key. |
* |
* @return the fields (SQLField) which are the keys of this table, can be empty. |
* @return the fields of the primary key of this table, can be empty. |
*/ |
public synchronized Set<SQLField> getPrimaryKeys() { |
public synchronized Set<SQLField> getPrimaryKeyFields() { |
return this.primaryKeys; |
} |
@Deprecated |
public final Set<SQLField> getPrimaryKeys() { |
return this.getPrimaryKeyFields(); |
} |
public final List<String> getPKsNames() { |
return this.getPKsNames(new ArrayList<String>()); |
} |
public final <C extends Collection<String>> C getPKsNames(C pks) { |
for (final SQLField f : this.getPrimaryKeys()) { |
pks.add(f.getName()); |
} |
return pks; |
} |
public final RowRef createRowRef(final Object... pk) { |
return this.createRowRef(Arrays.asList(pk)); |
} |
public final RowRef createRowRef(final List<?> pk) { |
return new RowRef(this, pk); |
} |
public final RowRef createRowRef(final Number id) { |
return new RowRef(this, id); |
} |
public final Set<Link> getForeignLinks() { |
return this.getDBSystemRoot().getGraph().getForeignLinks(this); |
} |
1668,11 → 1740,14 |
this.removeTableModifiedListener(new BridgeListener(l)); |
} |
public final void fireTableModified() { |
this.fireTableModified(SQLRow.NONEXISTANT_ID); |
} |
/** |
* Previent tous les listeners de la table qu'il y a eu une modification ou ajout si modif de |
* d'une ligne particuliere. |
* Previent tous les listeners de la table qu'il y a eu une modification. |
* |
* @param id -1 signifie tout est modifié. |
* @param id which ID was modified, {@link SQLRow#NONEXISTANT_ID} meaning all rows. |
*/ |
public void fireTableModified(final int id) { |
this.fire(Mode.ROW_UPDATED, id); |
1915,7 → 1990,7 |
// are the closest possible. NOTE that otherSystem is not required to be the system of the other |
// table, it might be something else if the other table was loaded into a system different than |
// the one which created the dump. |
public synchronized String equalsDesc(SQLTable o, SQLSystem otherSystem, boolean compareName) { |
public synchronized String equalsDesc(SQLTable o, SQLSyntax otherSyntax, boolean compareName) { |
if (o == null) |
return "other table is null"; |
final boolean name = !compareName || this.getName().equals(o.getName()); |
1930,17 → 2005,17 |
// if (!this.getTriggers().keySet().equals(o.getTriggers().keySet())) |
// return "triggers names unequal : " + this.getTriggers() + " " + o.getTriggers(); |
// } |
final boolean checkComment = otherSystem == null || this.getServer().getSQLSystem().isTablesCommentSupported() && otherSystem.isTablesCommentSupported(); |
final boolean checkComment = otherSyntax == null || this.getServer().getSQLSystem().isTablesCommentSupported() && otherSyntax.getSystem().isTablesCommentSupported(); |
if (checkComment && !CompareUtils.equals(this.getComment(), o.getComment())) |
return "comment unequal : " + SQLBase.quoteStringStd(this.getComment()) + " != " + SQLBase.quoteStringStd(o.getComment()); |
return this.equalsChildren(o, otherSystem); |
return this.equalsChildren(o, otherSyntax); |
} |
private synchronized String equalsChildren(SQLTable o, SQLSystem otherSystem) { |
private synchronized String equalsChildren(SQLTable o, SQLSyntax otherSyntax) { |
if (!this.getChildrenNames().equals(o.getChildrenNames())) |
return "fields differences: " + this.getChildrenNames() + "\n" + o.getChildrenNames(); |
final String noLink = equalsChildrenNoLink(o, otherSystem); |
final String noLink = equalsChildrenNoLink(o, otherSyntax); |
if (noLink != null) |
return noLink; |
1950,6 → 2025,7 |
if (thisLinks.size() != oLinks.size()) |
return "different number of foreign keys " + thisLinks + " != " + oLinks; |
final SQLSystem thisSystem = this.getServer().getSQLSystem(); |
final SQLSystem otherSystem = otherSyntax == null ? null : otherSyntax.getSystem(); |
for (final Link l : thisLinks) { |
final Link ol = o.getDBSystemRoot().getGraph().getForeignLink(o, l.getCols()); |
if (ol == null) |
2037,7 → 2113,7 |
* @param otherSystem the system <code>o</code> originates from, can be <code>null</code>. |
* @return <code>null</code> if each fields of this exists in <code>o</code> and is equal to it. |
*/ |
public synchronized final String equalsChildrenNoLink(SQLTable o, SQLSystem otherSystem) { |
public synchronized final String equalsChildrenNoLink(SQLTable o, SQLSyntax otherSystem) { |
for (final SQLField f : this.getFields()) { |
final SQLField oField = o.getField(f.getName()); |
final boolean isPrimary = this.getPrimaryKeys().contains(f); |
2106,21 → 2182,6 |
return res; |
} |
public final List<String> getPKsNames() { |
return this.getPKsNames(new ArrayList<String>()); |
} |
public synchronized final <C extends Collection<String>> C getPKsNames(C pks) { |
for (final SQLField f : this.getPrimaryKeys()) { |
pks.add(f.getName()); |
} |
return pks; |
} |
public final String[] getPKsNamesArray() { |
return getPKsNames().toArray(new String[0]); |
} |
/** |
* Return the indexes mapped by column names. Ie a key will have as value every index that |
* mentions it, and a multi-column index will be in several entries. Note: this is not robust |
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowValuesCluster.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSyntaxPG.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
26,7 → 26,9 |
import org.openconcerto.utils.StringUtils; |
import org.openconcerto.utils.Tuple2; |
import org.openconcerto.utils.cc.ITransformer; |
import org.openconcerto.xml.XMLCodecUtils; |
import java.beans.DefaultPersistenceDelegate; |
import java.io.File; |
import java.io.FileInputStream; |
import java.io.FileOutputStream; |
67,7 → 69,7 |
* |
* @author Sylvain CUAZ |
*/ |
class SQLSyntaxPG extends SQLSyntax { |
public class SQLSyntaxPG extends SQLSyntax { |
// From http://www.postgresql.org/docs/9.0/interactive/multibyte.html |
static final short MAX_BYTES_PER_CHAR = 4; |
91,9 → 93,11 |
DATE_SPECS.put(DateProp.MINUTE, "MI"); |
DATE_SPECS.put(DateProp.SECOND, "SS"); |
DATE_SPECS.put(DateProp.MICROSECOND, "US"); |
XMLCodecUtils.register(SQLSyntaxPG.class, new DefaultPersistenceDelegate(new String[] {})); |
} |
SQLSyntaxPG() { |
public SQLSyntaxPG() { |
super(SQLSystem.POSTGRESQL, DATE_SPECS); |
this.typeNames.addAll(Boolean.class, "boolean", "bool", "bit"); |
this.typeNames.addAll(Short.class, "smallint", "int2"); |
295,6 → 299,22 |
return SQLUtils.findWithSQLState(exn).getSQLState().equals("40P01"); |
} |
@Override |
public boolean isTableNotFoundException(Exception exn) { |
return SQLUtils.findWithSQLState(exn).getSQLState().equals("42P01"); |
} |
@Override |
public String getSetLockTimeoutQuery(int millis) { |
return "SET lock_timeout to " + millis; |
} |
@Override |
public String getShowLockTimeoutQuery() { |
final String interval = cast("setting||unit", "INTERVAL"); |
return "select CAST( EXTRACT(milliseconds from " + interval + ") as int) from pg_settings where \"name\" = 'lock_timeout'"; |
} |
private static final Pattern NOW_PTRN = Pattern.compile("\\(?'now'::text\\)?(::timestamp)"); |
@Override |
564,4 → 584,74 |
public String getDropTrigger(Trigger t) { |
return "DROP TRIGGER " + SQLBase.quoteIdentifier(t.getName()) + " on " + t.getTable().getSQLName().quote(); |
} |
@Override |
public String getSessionIDExpression() { |
return "pg_backend_pid()"; |
} |
@Override |
public String getSessionsQuery(final DBSystemRoot sysRoot, final boolean includeSelf) { |
final String allRows = "SELECT pid as \"ID\", query as \"QUERY\", usename as \"USER_NAME\" FROM pg_stat_activity WHERE datname=" + quoteString(sysRoot.getName()); |
if (includeSelf) |
return allRows; |
return allRows + " and pid != " + this.getSessionIDExpression(); |
} |
@Override |
public String getVersionFunction() { |
// shorter than version() : "PostgreSQL 9.6.9 on x86_64-pc-linux-gnu (Ubuntu |
// 9.6.9-2.pgdg16.04+1), compiled by gcc (Ubuntu 5.4.0-6ubuntu1~16.04.9) 5.4.0 20160609, |
// 64-bit" |
return "current_setting('server_version')"; |
} |
@Override |
public String getAllowConnectionsQuery(String sysRootName, boolean allow) { |
// REVOKE CONNECT ON DATABASE "ControleKD" TO public : superusers can still connect |
// to recover if all DB (even postgres and templates) run postgres --single |
// (single user / standalone mode ignores datallowconn) |
return "UPDATE pg_database SET datallowconn=" + SQLType.getBoolean(this).toString(allow) + " WHERE datname=" + quoteString(sysRootName); |
// "ALTER DATABASE WITH ALLOW_CONNECTIONS" isn't allowed on our own base |
} |
@Override |
public boolean isConnectionDisallowedException(SQLException exn) { |
return "55000".equals(exn.getSQLState()); |
} |
@Override |
public String getSQLArray(final List<String> sqlExpressions, final String type) { |
if (type == null) |
return "ARRAY[" + CollectionUtils.join(sqlExpressions, ", ") + ']'; |
else |
return this.quoteString('{' + CollectionUtils.join(sqlExpressions, ", ") + '}') + "::" + type + "[]"; |
} |
@Override |
public String getSQLArrayContains(String arrayExpression, String itemExpression) { |
return itemExpression + " = ANY(" + arrayExpression + ")"; |
} |
@Override |
public String getSQLArrayLength(final String arrayExpression) { |
return "ARRAY_LENGTH(" + arrayExpression + ", 1)"; |
} |
@Override |
public String getSQLArrayConcat(final String arrayExpression, final String array2Expression) { |
return "array_cat(" + arrayExpression + ", " + array2Expression + ")"; |
} |
@Override |
public String getSQLArrayAppend(final String arrayExpression, final String itemExpression) { |
// don't use || as it doesn't handle varchar literal |
return "array_append(" + arrayExpression + ", " + itemExpression + ")"; |
} |
@Override |
public String getSQLArraySlice(final String arrayExpression, final String index1Expression, final String index2Expression) { |
return '(' + arrayExpression + ")[" + index1Expression + ":" + index2Expression + "]"; |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLSelect.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
22,6 → 22,7 |
import org.openconcerto.utils.cc.ITransformer; |
import java.util.ArrayList; |
import java.util.Arrays; |
import java.util.Collection; |
import java.util.Collections; |
import java.util.HashMap; |
47,18 → 48,6 |
public static final ArchiveMode ARCHIVED = ArchiveMode.ARCHIVED; |
public static final ArchiveMode BOTH = ArchiveMode.BOTH; |
/** |
* Quote %-escaped parameters. %% : %, %s : quoteString, %i : quote, %f : quote(getFullName()), |
* %n : quote(getName()). |
* |
* @param pattern a string with %, eg "SELECT * FROM %n where %f like '%%a%%'". |
* @param params the parameters, eg [ /TENSION/, |TENSION.LABEL| ]. |
* @return pattern with % replaced, eg SELECT * FROM "TENSION" where "TENSION.LABEL" like '%a%'. |
*/ |
public static final String quote(final String pattern, final Object... params) { |
return SQLBase.quoteStd(pattern, params); |
} |
// [String], eg : [SITE.ID_SITE, AVG(AGE)] |
private final List<String> select; |
// names of columns (explicit aliases and field names), e.g. [ID_SITE, null] |
511,6 → 500,10 |
return this.addSelect(f, null); |
} |
public final SQLSelect addAllSelect(final FieldRef... s) { |
return this.addAllSelect(Arrays.asList(s)); |
} |
/** |
* Permet d'ajouter plusieurs champs. |
* |
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLRowAccessor.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
14,6 → 14,7 |
package org.openconcerto.sql.model; |
import org.openconcerto.sql.Log; |
import org.openconcerto.sql.model.SQLTable.VirtualFields; |
import org.openconcerto.sql.model.graph.DatabaseGraph; |
import org.openconcerto.sql.model.graph.Link; |
import org.openconcerto.sql.model.graph.Link.Direction; |
26,6 → 27,7 |
import java.math.BigDecimal; |
import java.sql.Clob; |
import java.text.DateFormat; |
import java.util.ArrayList; |
import java.util.Calendar; |
import java.util.Collection; |
import java.util.Collections; |
196,6 → 198,25 |
public abstract Number getIDNumber(); |
/** |
* Get the row reference. |
* |
* @return the row reference, <code>null</code> if some fields of the |
* {@link SQLTable#getPrimaryKeyFields() primary key} are missing or <code>null</code>. |
*/ |
public final RowRef getRowRef() { |
final List<String> pkFields = this.getTable().getPKsNames(); |
final List<Object> pk = new ArrayList<>(pkFields.size()); |
for (final String f : pkFields) { |
final Object o = this.getObject(f); |
// Don't need contains() because a PK cannot contain NULL in the DB. |
if (o == null) |
return null; |
pk.add(o); |
} |
return new RowRef(this.getTable(), Collections.unmodifiableList(pk), true); |
} |
/** |
* Whether this row is the undefined row. Return <code>false</code> if both the |
* {@link #getIDNumber() ID} and {@link SQLTable#getUndefinedIDNumber()} are <code>null</code> |
* since no row can have <code>null</code> primary key in the database. IOW when |
231,21 → 252,69 |
return ((Number) archiveVal).intValue() > 0; |
} |
public abstract SQLRowAccessor toImmutable(); |
public abstract boolean isFrozen(); |
/** |
* Creates an SQLRow from these values, without any DB access. |
* |
* @return an SQLRow with the same values as this. |
*/ |
public abstract SQLRow asRow(); |
public final SQLRow asRow() { |
return this.asRow(null); |
} |
public abstract SQLRow asRow(final Boolean immutable); |
/** |
* Return an immutable SQLRow with only the passed fields retained. I.e. the returned instance |
* can consume less memory. |
* |
* @param fields which fields to retain. |
* @return a {@link #isFrozen() frozen} SQLRow. |
*/ |
public final SQLRow trimmedRow(final VirtualFields fields) { |
final Set<String> fieldsNames = this.getTable().getFieldsNames(fields); |
if (this instanceof SQLRow && this.isFrozen() && fieldsNames.containsAll(this.getFields())) |
return (SQLRow) this; |
return SQLRow.trim(this, SQLRowAccessor::getValues, fieldsNames); |
} |
public final SQLRow fetchNewRow() { |
return this.fetchNewRow(true); |
} |
/** |
* Return a new instance with up-to-date values. |
* |
* @param useCache <code>true</code> to use the {@link SQLDataSource#isCacheEnabled() cache}. |
* @return a new instance. |
*/ |
public final SQLRow fetchNewRow(final boolean useCache) { |
return new SQLRow(this.getTable(), this.getID()).fetchValues(useCache); |
} |
/** |
* Creates an SQLRowValues from these values, without any DB access. |
* |
* @return an SQLRowValues with the same values as this. |
*/ |
public abstract SQLRowValues asRowValues(); |
public final SQLRowValues asRowValues() { |
return this.asRowValues(null); |
} |
/** |
* Creates an SQLRowValues from these values, without any DB access. |
* |
* @param immutable <code>true</code> if the result must be |
* {@link SQLRowValuesCluster#isFrozen() frozen}, <code>false</code> if it must not, |
* <code>null</code> if the caller doesn't care and just wants the fastest result. |
* @return an SQLRowValues with the same values as this. |
*/ |
public abstract SQLRowValues asRowValues(final Boolean immutable); |
/** |
* Creates an SQLRowValues with just this ID, and no other values. |
* |
* @return an empty SQLRowValues. |
267,6 → 336,8 |
public abstract Object getObject(String fieldName); |
public abstract Object getObjectNoCheck(String fieldName); |
/** |
* Return the value for the passed field only if already present in this instance. |
* |
/trunk/OpenConcerto/src/org/openconcerto/sql/model/DBRoot.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
27,6 → 27,7 |
import org.openconcerto.sql.utils.SQLUtils; |
import org.openconcerto.sql.utils.SQL_URL; |
import org.openconcerto.utils.CollectionUtils; |
import org.openconcerto.utils.Tuple2.List2; |
import java.net.URISyntaxException; |
import java.sql.SQLException; |
335,7 → 336,7 |
return this.equalsDesc(o, null); |
} |
public final String equalsDesc(final DBRoot o, final SQLSystem otherSystem) { |
public final String equalsDesc(final DBRoot o, final SQLSyntax otherSystem) { |
if (this == o) |
return null; |
if (null == o) |
364,8 → 365,12 |
* jdbc:h2:file:/a/b/c). |
*/ |
public final SQL_URL getURL() { |
final String hostname = this.getServer().getHostname(); |
if (hostname == null) |
// check that our name doesn't contain a path, otherwise we would lose it |
// e.g. dbserv:8084/~/sample |
final List2<String> hostAndPath = this.getServer().getHostnameAndPath(); |
final String hostname = hostAndPath.get0(); |
// TODO return JDBCUrl |
if (hostname == null || hostAndPath.get1() != null) |
return null; |
final SQLSystem system = this.getServer().getSQLSystem(); |
String url = system.name().toLowerCase() + "://" + this.getDBSystemRoot().getDataSource().getUsername() + "@" + hostname + "/"; |
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLFieldRowProcessor.java |
---|
New file |
0,0 → 1,131 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific |
* language governing permissions and limitations under the License. |
* |
* When distributing the software, include this License Header Notice in each file. |
*/ |
package org.openconcerto.sql.model; |
import java.sql.ResultSet; |
import java.sql.SQLException; |
import java.util.ArrayList; |
import java.util.Collections; |
import java.util.HashMap; |
import java.util.HashSet; |
import java.util.List; |
import java.util.Map; |
import java.util.Objects; |
import java.util.function.Function; |
import org.apache.commons.dbutils.RowProcessor; |
import net.jcip.annotations.Immutable; |
@Immutable |
public final class SQLFieldRowProcessor implements RowProcessor { |
private final List<String> names; |
private final int rsOffset; |
private Boolean uniqueNames; |
public SQLFieldRowProcessor(final SQLTable table, final List<String> fields) { |
this(0, fields, table::getField); |
} |
public SQLFieldRowProcessor(final List<? extends FieldRef> fields) { |
this(0, fields); |
} |
public SQLFieldRowProcessor(final int rsOffset, final List<? extends FieldRef> fields) { |
this(rsOffset, fields, FieldRef::getField); |
} |
private <F> SQLFieldRowProcessor(final int rsOffset, final List<? extends F> fields, final Function<? super F, SQLField> func) { |
final List<String> names = new ArrayList<>(fields.size()); |
for (final F ref : fields) { |
if (ref == null) { |
names.add(null); |
} else { |
final SQLField f = func.apply(ref); |
names.add(f.getName()); |
} |
} |
this.names = Collections.unmodifiableList(names); |
this.rsOffset = rsOffset; |
this.uniqueNames = null; |
} |
public final List<String> getFieldNames() { |
return this.names; |
} |
public final boolean areNamesUnique() { |
if (this.uniqueNames == null) { |
this.uniqueNames = new HashSet<>(this.names).size() == this.names.size(); |
} |
return this.uniqueNames.booleanValue(); |
} |
public final Object getObject(final ResultSet rs, final int rsIndex) throws SQLException { |
return rs.getObject(rsIndex + this.rsOffset); |
} |
@Override |
public Object[] toArray(ResultSet rs) throws SQLException { |
final int count = this.names.size(); |
final Object[] result = new Object[count]; |
for (int i = 0; i < count; i++) { |
result[i] = getObject(rs, i + 1); |
} |
return result; |
} |
@SuppressWarnings("rawtypes") |
@Override |
public Object toBean(ResultSet rs, Class type) throws SQLException { |
throw new UnsupportedOperationException(); |
} |
@SuppressWarnings("rawtypes") |
@Override |
public List toBeanList(ResultSet rs, Class type) throws SQLException { |
throw new UnsupportedOperationException(); |
} |
@Override |
public Map<String, Object> toMap(ResultSet rs) throws SQLException { |
final int count = this.names.size(); |
final Map<String, Object> result = new HashMap<>(); |
for (int i = 0; i < count; i++) { |
final String name = this.names.get(i); |
if (name != null) { |
result.put(name, getObject(rs, i + 1)); |
} |
} |
return result; |
} |
@Override |
public int hashCode() { |
return Objects.hash(this.names, this.rsOffset); |
} |
@Override |
public boolean equals(Object obj) { |
if (this == obj) |
return true; |
if (obj == null) |
return false; |
if (getClass() != obj.getClass()) |
return false; |
final SQLFieldRowProcessor other = (SQLFieldRowProcessor) obj; |
return this.rsOffset == other.rsOffset && this.names.equals(other.names); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/sql/model/UndefinedRowValuesCache.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
57,6 → 57,10 |
return rv; |
} |
public void setDefaultRowValues(final SQLRowValues rowVals) { |
this.map.put(rowVals.getTable(), rowVals); |
} |
public void preload(List<SQLTable> tablesToCache) { |
if (tablesToCache.size() <= 0) { |
throw new IllegalArgumentException("Empty list"); |
/trunk/OpenConcerto/src/org/openconcerto/sql/model/SQLField.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
488,8 → 488,8 |
// compareDefault useful when fields' default are functions containing the name of the table (eg |
// serial) |
public String equalsDesc(SQLField o, SQLSystem otherSystem, boolean compareDefault) { |
final Map<Properties, String> res = getDiffMap(o, otherSystem, compareDefault); |
public String equalsDesc(SQLField o, SQLSyntax otherSyntax, boolean compareDefault) { |
final Map<Properties, String> res = getDiffMap(o, otherSyntax, compareDefault); |
if (res.size() == 0) |
return null; |
else |
504,7 → 504,7 |
* @param compareDefault <code>true</code> if defaults should be compared. |
* @return a map containing properties that differs and their values. |
*/ |
public synchronized Map<Properties, String> getDiffMap(SQLField o, SQLSystem otherSystem, boolean compareDefault) { |
public synchronized Map<Properties, String> getDiffMap(SQLField o, SQLSyntax otherSystem, boolean compareDefault) { |
if (o == null) |
return Collections.singletonMap(null, "other field is null"); |
final Map<Properties, String> res = new HashMap<Properties, String>(); |
/trunk/OpenConcerto/src/org/openconcerto/sql/model/Where.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
15,7 → 15,6 |
import org.openconcerto.utils.CollectionUtils; |
import org.openconcerto.utils.StringUtils; |
import org.openconcerto.utils.cc.ITransformer; |
import java.util.ArrayList; |
import java.util.Arrays; |
24,9 → 23,9 |
import java.util.List; |
import java.util.Map; |
import java.util.Map.Entry; |
import java.util.Objects; |
import java.util.stream.Collectors; |
import org.apache.commons.collections.functors.InstanceofPredicate; |
import net.jcip.annotations.Immutable; |
/** |
127,6 → 126,25 |
return subqueries(ref, false, subQueries); |
} |
static public Where inValues(final FieldRef ref, final Collection<?> values) { |
return compareValues(ref, RowComparison.IN, values); |
} |
static public Where notInValues(final FieldRef ref, final Collection<?> values) { |
return compareValues(ref, RowComparison.NOT_IN, values); |
} |
static public Where compareValues(final FieldRef ref, final RowComparison cmp, final Collection<?> values) { |
return compareValues(ref, cmp, NullValue.IS_FORBIDDEN, values); |
} |
static public Where compareValues(final FieldRef ref, final RowComparison cmp, final NullValue nullMode, final Collection<?> values) { |
if (values.isEmpty()) { |
return cmp == RowComparison.IN ? FALSE : TRUE; |
} |
return createRaw(getCompareValuesClause(ref.getFieldRef(), cmp, nullMode, values, ref.getField().getType()), ref); |
} |
/** |
* Create a Where for a field value contained or not contained in sub-queries. |
* |
153,7 → 171,8 |
/** |
* To create complex Where not possible with constructors. |
* |
* @param pattern a pattern to be passed to {@link SQLSelect#quote(String, Object...)}, eg |
* @param s the syntax to use. |
* @param pattern a pattern to be passed to {@link SQLBase#quoteStd(String, Object...)}, eg |
* "EXTRACT(YEAR FROM %n) = 3007". |
* @param params the params to be passed to <code>quote()</code>, eg [|MISSION.DATE_DBT|]. |
* @return a new Where with the result from <code>quote()</code> as its clause, and all |
160,9 → 179,14 |
* <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) { |
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,12 → 322,7 |
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()); |
} |
} |
/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() { |
return this; |
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) { |
679,7 → 709,34 |
public final Object getObject(String fieldName) { |
return this.values.get(fieldName); |
} |
@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); |
686,12 → 743,16 |
} |
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,17 → 763,44 |
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()); |
} else if (copyForeigns == ForeignCopyMode.COPY_NULL) { |
toAdd.put(e.getKey(), null); |
} else if (copyForeigns != ForeignCopyMode.NO_COPY) { |
final SQLRowValues foreign = (SQLRowValues) e.getValue(); |
if (foreign.hasID()) |
toAdd.put(e.getKey(), foreign.getIDNumber()); |
else if (copyForeigns == ForeignCopyMode.COPY_ID_OR_ROW) |
toAdd.put(e.getKey(), foreign); |
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) { |
val = null; |
} else { |
final SQLRowValues foreign = (SQLRowValues) e.getValue(); |
if (foreign.hasID()) |
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() { |
return this; |
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) { |
value = convert(value.getClass(), value, javaType); |
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/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/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/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/utils/Diff.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
24,6 → 24,7 |
import org.openconcerto.sql.model.SQLRow; |
import org.openconcerto.sql.model.SQLSchema; |
import org.openconcerto.sql.model.SQLServer; |
import org.openconcerto.sql.model.SQLSyntax; |
import org.openconcerto.sql.model.SQLSyntax.ConstraintType; |
import org.openconcerto.sql.model.SQLSystem; |
import org.openconcerto.sql.model.SQLTable; |
179,6 → 180,7 |
final AlterTable alterTable = new AlterTable(aT); |
final SQLSystem aSystem = aT.getServer().getSQLSystem(); |
final SQLSystem bSystem = bT.getServer().getSQLSystem(); |
final SQLSyntax bSyntax = bT.getDBSystemRoot().getSyntax(); |
{ |
final Set<String> aFields = aT.getFieldsName(); |
final Set<String> bFields = bT.getFieldsName(); |
191,7 → 193,7 |
for (final String common : CollectionUtils.inter(aFields, bFields)) { |
final SQLField aF = aT.getField(common); |
final SQLField bF = bT.getField(common); |
final Map<Properties, String> diff = aF.getDiffMap(bF, bSystem, true); |
final Map<Properties, String> diff = aF.getDiffMap(bF, bSyntax, true); |
alterTable.alterColumn(common, bF, diff.keySet()); |
} |
} |
259,7 → 261,7 |
if (alterTable.isEmpty()) { |
final String exactDiff = aT.equalsDesc(bT, null, true); |
assert exactDiff != null : "Why bother if exactly equals"; |
final String lenientDiff = aT.equalsDesc(bT, bT.getServer().getSQLSystem(), true); |
final String lenientDiff = aT.equalsDesc(bT, bSyntax, true); |
if (lenientDiff == null) |
Log.get().info("Tables " + aT.getSQLName() + " and " + bT.getSQLName() + " are not exactly equal, but due to diferring DB system features can't be :\n" + exactDiff); |
else |
/trunk/OpenConcerto/src/org/openconcerto/sql/utils/ChangeTable.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
279,6 → 279,10 |
return cat(cts, new ChangeRootNameTransformer(r), true).get(0); |
} |
static public String getForeignColumDefaultValue(final SQLTable foreignTable) { |
return foreignTable.getKey().getType().toString(foreignTable.getUndefinedIDNumber()); |
} |
// allow to factor column name from table and FCSpec |
public static final class ForeignColSpec { |
296,7 → 300,7 |
static public ForeignColSpec fromTable(SQLTable foreignTable, final boolean absolute) { |
if (foreignTable == null) |
throw new NullPointerException("null table"); |
final String defaultVal = foreignTable.getKey().getType().toString(foreignTable.getUndefinedIDNumber()); |
final String defaultVal = getForeignColumDefaultValue(foreignTable); |
final SQLName n = absolute ? foreignTable.getSQLName() : new SQLName(foreignTable.getName()); |
return new ForeignColSpec(null, n, foreignTable.getKey().getName(), defaultVal); |
} |
/trunk/OpenConcerto/src/org/openconcerto/sql/users/UserManager.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
67,6 → 67,10 |
public static final User getUser() { |
final UserManager mngr = getInstance(); |
return getCurrentUser(mngr); |
} |
public static User getCurrentUser(final UserManager mngr) { |
return mngr == null ? null : mngr.getCurrentUser(); |
} |
/trunk/OpenConcerto/src/org/openconcerto/sql/users/rights/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,6 → 153,38 |
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() { |
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/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/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/utils/OutlookEmail.vbs |
---|
File deleted |
/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); |
} |
// 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 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(); |
final int returnCode = process.waitFor(); |
if (returnCode != 0) |
throw new IllegalStateException("Non zero return code: " + returnCode); |
/trunk/OpenConcerto/src/org/openconcerto/utils/io/MailAccount.java |
---|
15,6 → 15,7 |
import org.openconcerto.utils.ExceptionUtils; |
import java.util.Objects; |
import java.util.Properties; |
import javax.mail.Address; |
23,6 → 24,8 |
import javax.mail.PasswordAuthentication; |
import javax.mail.SendFailedException; |
import javax.mail.Transport; |
import javax.mail.internet.AddressException; |
import javax.mail.internet.InternetAddress; |
/** |
* A mail account. |
107,6 → 110,20 |
return sb.toString(); |
} |
public static final MailAccount create(final String fromAddr, String smtpServer, String smtpLogin, String smtpPassword) throws AddressException { |
Objects.requireNonNull(fromAddr, "Missing 'From:' address"); |
final InternetAddress fromInetAddr = new InternetAddress(fromAddr, true); |
if (smtpServer == null) { |
smtpServer = "smtp." + fromInetAddr.getAddress().substring(fromInetAddr.getAddress().indexOf('@') + 1); |
} |
if (smtpLogin == null) { |
smtpLogin = fromInetAddr.getAddress().substring(0, fromInetAddr.getAddress().indexOf('@')); |
} |
final MailAccount res = new MailAccount(fromInetAddr.getPersonal(), fromInetAddr.getAddress(), smtpServer); |
res.setAuth(new PasswordAuthentication(smtpLogin, smtpPassword)); |
return res; |
} |
// nullable |
private final String name; |
private final String address; |
182,7 → 199,8 |
public final void send(final Message msg) throws MessagingException { |
final PasswordAuthentication auth = this.getAuth(); |
msg.getSession().getProperties().setProperty("mail." + getSMTPProtocol() + ".auth", auth == null ? "false" : "true"); |
final Properties props = msg.getSession().getProperties(); |
props.setProperty("mail." + getSMTPProtocol() + ".auth", auth == null ? "false" : "true"); |
try (final Transport mailTransport = msg.getSession().getTransport()) { |
if (auth == null) |
mailTransport.connect(); |
189,6 → 207,8 |
else |
mailTransport.connect(auth.getUserName(), auth.getPassword()); |
mailTransport.sendMessage(msg, msg.getAllRecipients()); |
} catch (MessagingException e) { |
throw new MessagingException("Couldn't send as " + auth.getUserName() + " using\n" + props, e); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/io/JSONConverter.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 |
73,7 → 73,11 |
} else if (param instanceof Calendar) { |
result = formatCalendar(((Calendar) param)); |
} else if (param instanceof Class<?>) { |
result = ((Class<?>) param).getName(); |
if (param == String.class) { |
result = "string"; |
} else { |
result = ((Class<?>) param).getName(); |
} |
} else if (param instanceof Iterable) { |
final Iterable<?> tmp = (Iterable<?>) param; |
final JSONArray jsonArray = new JSONArray(); |
82,14 → 86,11 |
} |
result = jsonArray; |
} else if (param instanceof Color) { |
if (param != null) { |
final Color paramColor = (Color) param; |
final JSONObject jsonColor = new JSONObject(); |
jsonColor.put("r", paramColor.getRed()); |
jsonColor.put("g", paramColor.getGreen()); |
jsonColor.put("b", paramColor.getBlue()); |
result = jsonColor; |
String hexString = Integer.toHexString(((Color) param).getRGB()); |
if (hexString.length() > 6) { |
hexString = hexString.substring(2, hexString.length()); |
} |
result = "#" + hexString; |
} else if (param instanceof BigDecimal) { |
result = ((BigDecimal) param).doubleValue(); |
} else { |
132,11 → 133,7 |
throw new IllegalArgumentException("object (" + o.getClass().getName() + ") is not assignable for '" + type + "', the format is not valid", e); |
} |
} else if (type.equals(Color.class)) { |
final JSONObject jsonColor = (JSONObject) o; |
final int r = JSONConverter.getParameterFromJSON(jsonColor, "r", Integer.class); |
final int g = JSONConverter.getParameterFromJSON(jsonColor, "g", Integer.class); |
final int b = JSONConverter.getParameterFromJSON(jsonColor, "b", Integer.class); |
result = type.cast(new Color(r, g, b)); |
result = type.cast(Color.decode(o.toString())); |
} else { |
result = type.cast(o); |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/SetMap.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
36,6 → 36,8 |
} |
static public <K, V> SetMapItf<K, V> unmodifiableMap(SetMapItf<K, V> map) { |
if (map.isEmpty()) |
return empty(); |
return new Unmodifiable<K, V>(map); |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/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/Action.java |
---|
New file |
0,0 → 1,34 |
/* |
* 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; |
public abstract class Action { |
private final String name; |
public Action(String name) { |
this.name = name; |
} |
public String getName() { |
return this.name; |
} |
@Override |
public String toString() { |
return this.name; |
} |
public abstract void run(Object source); |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/ExceptionUtils.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,9 |
import java.io.PrintWriter; |
import java.io.StringWriter; |
import java.lang.reflect.Constructor; |
import java.math.BigDecimal; |
import java.util.Objects; |
import java.util.function.BiPredicate; |
/** |
* Utilitaires pour les exceptions. |
92,4 → 95,27 |
Log.get().warning("Error while writing " + cause); |
return res.toString(); |
} |
static public void requireEquals(final BigDecimal a, final BigDecimal b, final String msg) { |
require((bd1, bd2) -> bd1.compareTo(bd2) == 0, a, b, msg); |
} |
static public void requireEquals(final Object a, final Object b, final String msg) { |
require(Objects::equals, a, b, msg); |
} |
static public <T> void require(final BiPredicate<T, T> pred, final T a, final T b, final String msg) { |
if (!pred.test(a, b)) |
throw new IllegalArgumentException(msg + ", expected " + a + " but got " + b); |
} |
static public void requireSame(final Object a, final Object b, final String msg) { |
if (a != b) |
throw new IllegalArgumentException(msg + ", expected same reference to " + a + " but got " + b); |
} |
static public void requireEquals(final int a, final int b, final String msg) { |
if (a != b) |
throw new IllegalArgumentException(msg + ", expected " + a + " but got " + b); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/sync/SimpleSyncClient.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
27,6 → 27,7 |
import java.io.IOException; |
import java.io.InputStream; |
import java.io.OutputStream; |
import java.io.Serializable; |
import java.nio.charset.StandardCharsets; |
import java.nio.file.Files; |
import java.nio.file.Path; |
52,15 → 53,15 |
public final class SimpleSyncClient extends HTTPClient { |
static protected final long getLong(final Object o) { |
protected static final long getLong(final Object o) { |
return ((Number) o).longValue(); |
} |
static protected final Instant getInstant(final Object o) { |
protected static final Instant getInstant(final Object o) { |
return Instant.ofEpochMilli(getLong(o)); |
} |
static protected abstract class BaseAttrs { |
public static abstract class BaseAttrs implements Serializable { |
private final String path; |
private final String name; |
private final Instant lastModified; |
90,8 → 91,8 |
} |
} |
static public final class DirAttrs extends BaseAttrs { |
static protected DirAttrs fromJSON(final String path, final JSONArray array) { |
public static final class DirAttrs extends BaseAttrs { |
protected static DirAttrs fromJSON(final String path, final JSONArray array) { |
return new DirAttrs(path, (String) array.get(0), getInstant(array.get(1))); |
} |
100,9 → 101,9 |
} |
} |
static public final class FileAttrs extends BaseAttrs { |
public static final class FileAttrs extends BaseAttrs { |
static protected FileAttrs fromJSON(final String path, final JSONArray array) { |
protected static FileAttrs fromJSON(final String path, final JSONArray array) { |
return new FileAttrs(path, (String) array.get(0), getInstant(array.get(2)), getLong(array.get(1)), (String) array.get(3)); |
} |
158,7 → 159,7 |
} |
} |
static public final class DirContent { |
public static final class DirContent { |
private final String path; |
private final JSONObject json; |
219,11 → 220,11 |
} |
@FunctionalInterface |
static public interface FileConsumer { |
public static interface FileConsumer { |
public void accept(FileAttrs attrs, InputStream fileStream) throws IOException; |
} |
static private final Set<Integer> GETFILE_OK_CODES = CollectionUtils.createSet(200, 404); |
private static final Set<Integer> GETFILE_OK_CODES = CollectionUtils.createSet(200, 404); |
public Response getFile(final String path, final String fileName, final FileConsumer fileConsumer) throws IOException { |
if (path == null) { |
246,6 → 247,26 |
return res; |
} |
public Integer getCounter(final String key) throws IOException { |
if (key == null) { |
throw new IllegalArgumentException("null key"); |
} |
final HttpsURLConnection con = openConnection("/getCounter"); |
send(con, NetUtils.urlEncode("key", key), false); |
final Response res = checkResponseCode(con, GETFILE_OK_CODES); |
if (res.getCode() == 200) { |
byte[] bytes = new byte[20]; |
try (final InputStream in = getInputStream(con)) { |
int r = in.read(bytes); |
String str = new String(bytes, 0, r); |
return Integer.parseInt(str); |
} |
} |
return null; |
} |
// ATTN contrary to other methods, the result isn't if the request was OK : it ignores |
// throwsException() and always throws. The return value is true if the file existed and was |
// saved. |
281,7 → 302,7 |
} |
public final Response renameFile(final String path, final String fileName, final String newFileName) throws IOException { |
return this.renameFile(path, fileName, null, newFileName); |
return this.renameFile(path, fileName, path, newFileName); |
} |
public final Response renameFile(final String path, final String fileName, final String newPath, final String newFileName) throws IOException { |
331,4 → 352,15 |
return checkResponseCode(send(con, ba.toByteArray(), true)); |
} |
public Response createDir(String path, String fileName) throws IOException { |
if (path == null) { |
throw new IllegalArgumentException("null path"); |
} |
if (fileName == null) { |
throw new IllegalArgumentException("null fileName"); |
} |
final HttpsURLConnection con = openConnection("/mkdir"); |
return checkResponseCode(send(con, NetUtils.urlEncode("rn", fileName, "rp", path), false)); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/sync/SyncClient.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
18,6 → 18,7 |
import java.io.BufferedInputStream; |
import java.io.BufferedOutputStream; |
import java.io.ByteArrayInputStream; |
import java.io.ByteArrayOutputStream; |
import java.io.DataInputStream; |
import java.io.DataOutputStream; |
529,7 → 530,16 |
wr.close(); |
// Get the response ASAP in order to not block the server while computing locally hash256 |
DataInputStream in = new DataInputStream(new BufferedInputStream(conn.getInputStream())); |
// Copy the entire data first because DataInputStream will fail on partial buffer (mainly |
// readUTF) |
InputStream ins = conn.getInputStream(); |
final ByteArrayOutputStream out = new ByteArrayOutputStream(); |
StreamUtils.copy(ins, out); |
ins.close(); |
DataInputStream in = new DataInputStream(new ByteArrayInputStream(out.toByteArray())); |
int fileCount = in.readInt(); |
this.byteReceived += 4; |
ArrayList<FileProperty> list = new ArrayList<FileProperty>(); |
875,4 → 885,6 |
public void setVerifyHost(boolean verify) { |
this.verifyHost = verify; |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/translation/messages_sp.properties |
---|
File deleted |
\ No newline at end of file |
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/translation/messages_pl.properties |
---|
27,3 → 27,17 |
cut=Wytnij |
copy=Kopi\u0119 |
paste=Wklej |
# search |
all=All |
toReverse=Reverse |
contains=Contains |
contains.exactly=Contains exactly |
isLessThan=Is less than |
isLessThanOrEqualTo=Is less than or equal to |
isEqualTo=Is equal to |
isExactlyEqualTo=Is exactly equal to |
isGreaterThan=Is greater than |
isGreaterThanOrEqualTo=Is greater than or equal to |
isEmpty=Is empty |
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/translation/messages_en.properties |
---|
6,6 → 6,7 |
linkOpenError=Error while opening {0} |
memory=memory |
memory.used=memory used |
megabytes={0} MB |
processors={0, plural, one { # processor } other { # processors } } |
os=Operating system |
12,6 → 13,8 |
javaVersion=Version <b>{0}</b> of {1} |
javaHome=installation directory |
no.laf=No look and feel |
properties.all=All properties |
env.all=Whole environment |
user=User |
home.dir=home directory |
26,4 → 29,18 |
cut=Cut |
copy=Copy |
paste=Paste |
paste=Paste |
# search |
all=All |
toReverse=Reverse |
contains=Contains |
contains.exactly=Contains exactly |
isLessThan=Is less than |
isLessThanOrEqualTo=Is less than or equal to |
isEqualTo=Is equal to |
isExactlyEqualTo=Is exactly equal to |
isGreaterThan=Is greater than |
isGreaterThanOrEqualTo=Is greater than or equal to |
isEmpty=Is empty |
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/translation/messages_es.properties |
---|
New file |
0,0 → 1,22 |
true_key=verdadero |
false_key=falso |
yes_key=sí |
no_key=no |
cut=Cortar |
copy=Copiar |
paste=Pegar |
# search |
all=Todo |
toReverse=Revertir |
contains=Contiene |
contains.exactly=Contiene exactamente |
isLessThan=Es menos de |
isLessThanOrEqualTo=Es menor o igual a |
isEqualTo=Es igual a |
isExactlyEqualTo=Es exactamente igual a |
isGreaterThan=Es más de |
isGreaterThanOrEqualTo=Es más o igual a |
isEmpty=Esta vacio |
/trunk/OpenConcerto/src/org/openconcerto/utils/i18n/translation/messages_fr.properties |
---|
6,6 → 6,7 |
linkOpenError=Impossible d''ouvrir {0} |
memory=mémoire |
memory.used=mémoire utilisée |
megabytes={0} Mo |
processors={0, plural, one { # processeur } other { # processeurs } } |
os=Système d''exploitation |
12,6 → 13,8 |
javaVersion=Version <b>{0}</b> de {1} |
javaHome=dossier d'installation |
no.laf=Aucun thème |
properties.all=Toutes les propriétés |
env.all=Tout l'environnement |
user=Utilisateur |
home.dir=dossier utilisateur |
27,3 → 30,20 |
cut=Couper |
copy=Copier |
paste=Coller |
# search |
all=Tout |
toReverse=inverser |
contains=Contient |
contains.exactly=Contient exactement |
startsWith=Commence par |
endsWith=Finit par |
isLessThan=Est inférieur à |
isLessThanOrEqualTo=Est inférieur ou égal à |
isEqualTo=Est égal à |
isExactlyEqualTo=Est exactement égal à |
isGreaterThan=Est supérieur à |
isGreaterThanOrEqualTo=Est supérieur ou égal à |
isEmpty=Est vide |
matchRegexp=Correspond à l'expression régulière |
/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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 |
24XJ3/7t3x4dHd3c3NzZ2fnWt751xvllJxt729veVvihjMc+vfmL/PrXv36m+/sy3/zmN2/cuGHO |
ac5c8oE97kjc81auSlOOnRtywZ3SpdIpeg7Lqc9AIXMl5uXBXKq5YPM9MpdkP8LXvDDYgHmR+IVf |
+IUz3Y9+mZiYMDd3d3fNF2Junul+mq67z1h+SeX3dk5iDZSPnMg5C79N+Vl5Jd/9/l1YL8cu6V5C |
JuYvtXxcwopVz1m4MPKz8mpZsflZLvfekpez/JmrX3DcGcqFtCkvZJZ7JV/5ylfOdN9vuP+Eq4IO |
qufML8h8phAddEfizlAupE15x84KudRO6fe3U/Qc5tHu8u9++ciJnLPw25SflVfy3e/fhfXSithQ |
yF9BXj5jv+zXvOY1P/7xj7PBv/iLv7DJvMCAe1w44v09jMKM9clPftJNZn81JAt4N/MjJY+Vl83q |
dH9x5e1vf7v9263mLb17V6fPT0LeW9/6Vm9Py473mpX98Q570/trK2++/2cjj3168xd5cHDwO7/z |
O+5gdlf2cNn0/EjE81ZObYorZO6xF9w5bqnkn8NCbsbl57pK7rIePsf/eeyxx37wgx9kmR/96Edm |
Jfih7uIxLyTOyYrPdqb38rO86dnNY59S9zhwpPo5C79N7ogrm9U57rvfpwvrJWRJFwqZ6F2Ae1Uu |
Z8YDJXdZD5/j/7Bis3x+pHBhuCOubFanphXrjrjyZ3j4/v+VvZwVnrn6BUecoVxIm/JCZuWvKi/7 |
lDWPvdcfdTx8mv9DB7N8fqRwQbojrmxWhw7mRiLOUC6kTXkhs4691M5x39/C59DjBly0O3Ck+jkL |
v03uiCub1Tnuu9+nC+uldWobCob9j9LmnaQ7ODU1Za7YLKxXvvKVr3/969///vf/1V/91cWLFwMD |
+QfyRn784x+bx33d615nf3KjMGOZ972f+tSnTPK1r33tpz/96Z2dHS+Wn+WNlDxWXjbL+vu//3s7 |
/tWvftW7q9PPJ8F6/PHHzck/8pGPfOtb38p+Ayfjnm1+fv4Tn/jEW97yFjPlne9855e//OW9+x8G |
a2N379790z/9U/NYr3rVq37v935vbW3N3nvs01t4kQcHB1/72tfe+973msL87M/+7Ic+9KHp6Wl7 |
lze9cER93sqpTXEFzi2/YKtkqRQ+h3l2ep6f6yq5y3r++ec///nPf+ADHzCPay7bXLy5Bu+jZTvd |
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" /> |
</BODY> |
</HTML> |
/trunk/OpenConcerto/src/org/openconcerto/utils/cc/IdentityHashSet.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,6 → 29,8 |
*/ |
public final class IdentityHashSet<E> extends AbstractSet<E> implements IdentitySet<E>, Cloneable { |
static private final Object VALUE = new Object(); |
private final IdentityHashMap<E, Object> map; |
public IdentityHashSet() { |
46,12 → 48,7 |
@Override |
public boolean add(E e) { |
if (this.contains(e)) |
return false; |
else { |
this.map.put(e, null); |
return true; |
} |
return this.map.put(e, VALUE) != VALUE; |
} |
@Override |
82,7 → 79,7 |
@Override |
public boolean contains(Object o) { |
return this.map.keySet().contains(o); |
return this.map.containsKey(o); |
} |
@Override |
92,7 → 89,7 |
@Override |
public boolean remove(Object o) { |
return this.map.keySet().remove(o); |
return this.map.remove(o) == VALUE; |
} |
@Override |
115,6 → 112,14 |
return this.map.keySet().toArray(a); |
} |
// use System.identityHashCode() |
@Override |
public int hashCode() { |
return this.map.keySet().hashCode(); |
} |
// equals() uses containsAll which is correct |
/** |
* Returns a shallow copy of this <tt>HashSet</tt> instance: the elements themselves are not |
* cloned. |
/trunk/OpenConcerto/src/org/openconcerto/utils/cc/CopyOnWriteList.java |
---|
New file |
0,0 → 1,239 |
/* |
* 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.cc; |
import java.util.AbstractList; |
import java.util.ArrayList; |
import java.util.Arrays; |
import java.util.Collection; |
import java.util.Collections; |
import java.util.Comparator; |
import java.util.Iterator; |
import java.util.List; |
import java.util.ListIterator; |
import java.util.function.UnaryOperator; |
import net.jcip.annotations.ThreadSafe; |
/** |
* A thread-safe list in which all mutative operations (set, remove, and so on) are implemented by |
* making a fresh copy of the underlying list. The instance of the underlying list can be customized |
* with {@link #copy(Collection)} and {@link #create(int)}. The underlying immutable list is |
* available with {@link #getImmutable()}. |
* |
* @author Sylvain |
* |
* @param <T> the type of elements in this list |
*/ |
@ThreadSafe |
public class CopyOnWriteList<T> extends AbstractList<T> { |
private List<T> immutable; |
public CopyOnWriteList() { |
this.immutable = Collections.emptyList(); |
} |
public CopyOnWriteList(final T single) { |
this.immutable = Collections.singletonList(single); |
} |
public CopyOnWriteList(final Collection<? extends T> m) { |
this.immutable = Collections.unmodifiableList(copy(m)); |
} |
/** |
* Create a copy of the passed collection. |
* |
* @param src the collection to copy, not <code>null</code>. |
* @return a shallow copy of <code>src</code>. |
*/ |
protected List<T> copy(final Collection<? extends T> src) { |
return new ArrayList<>(src); |
} |
protected List<T> create(int capacity) { |
return new ArrayList<>(capacity); |
} |
// Like CopyOnWriteArrayList, iterate on immutable (otherwise would have to at least increment |
// this.modCount). |
@Override |
public Iterator<T> iterator() { |
return this.getImmutable().iterator(); |
} |
@Override |
public ListIterator<T> listIterator(int index) { |
return this.getImmutable().listIterator(index); |
} |
// write |
private void rangeCheck(final int index, final boolean forAdd, final int size) { |
if (index < 0 || index > size || (!forAdd && index == size)) |
throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); |
} |
private String outOfBoundsMsg(int index) { |
return "Index: " + index + ", Size: " + size(); |
} |
@Override |
public synchronized T set(int index, T element) { |
final List<T> copy = copy(this.immutable); |
final T res = copy.set(index, element); |
this.immutable = Collections.unmodifiableList(copy); |
return res; |
} |
@Override |
public synchronized boolean add(T e) { |
return super.add(e); |
} |
@Override |
public void add(int index, T element) { |
this.addAll(index, Collections.singletonList(element)); |
} |
@Override |
public synchronized boolean addAll(Collection<? extends T> c) { |
return this.addAll(this.size(), c); |
} |
@Override |
public synchronized boolean addAll(int index, Collection<? extends T> c) { |
final int size = this.size(); |
this.rangeCheck(index, true, size); |
final int cSize = c.size(); |
if (cSize == 0) |
return false; |
final List<T> copy = create(size + cSize); |
for (int i = 0; i <= size; i++) { |
if (i == index) |
copy.addAll(c); |
if (i < size) |
copy.add(this.get(i)); |
} |
assert copy.size() == size + cSize; |
this.immutable = Collections.unmodifiableList(copy); |
return true; |
} |
@Override |
public synchronized T remove(final int index) { |
final int size = this.size(); |
this.rangeCheck(index, false, size); |
final List<T> copy = create(size - 1); |
T res = null; |
for (int i = 0; i < size; i++) { |
if (i == index) |
res = this.get(i); |
else |
copy.add(this.get(i)); |
} |
assert copy.size() == size - 1; |
this.immutable = Collections.unmodifiableList(copy); |
return res; |
} |
@Override |
public synchronized boolean remove(Object o) { |
final int indexOf = this.indexOf(o); |
if (indexOf < 0) |
return false; |
this.remove(indexOf); |
return true; |
} |
@Override |
public synchronized boolean removeAll(Collection<?> c) { |
final List<T> copy = copy(this.immutable); |
final boolean res = copy.removeAll(c); |
this.immutable = Collections.unmodifiableList(copy); |
return res; |
} |
@Override |
public synchronized void clear() { |
this.immutable = Collections.emptyList(); |
} |
@Override |
public synchronized boolean retainAll(Collection<?> c) { |
final List<T> copy = copy(this.immutable); |
final boolean res = copy.retainAll(c); |
this.immutable = Collections.unmodifiableList(copy); |
return res; |
} |
@Override |
public synchronized void replaceAll(UnaryOperator<T> operator) { |
final List<T> copy = copy(this.immutable); |
copy.replaceAll(operator); |
this.immutable = Collections.unmodifiableList(copy); |
} |
@Override |
@SuppressWarnings({ "unchecked", "rawtypes" }) |
public synchronized void sort(Comparator<? super T> c) { |
Object[] a = this.toArray(); |
Arrays.sort(a, (Comparator) c); |
final List<T> copy = create(this.size()); |
for (Object e : a) { |
copy.add((T) e); |
} |
this.immutable = Collections.unmodifiableList(copy); |
} |
// read |
public synchronized final List<T> getImmutable() { |
return this.immutable; |
} |
@Override |
public T get(int index) { |
return this.getImmutable().get(index); |
} |
@Override |
public int size() { |
return this.getImmutable().size(); |
} |
@Override |
public boolean isEmpty() { |
return this.getImmutable().isEmpty(); |
} |
@Override |
public boolean contains(Object o) { |
return this.getImmutable().contains(o); |
} |
// equals |
@Override |
public boolean equals(final Object o) { |
return this.getImmutable().equals(o); |
} |
@Override |
public int hashCode() { |
return this.getImmutable().hashCode(); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/cc/CopyOnWriteMap.java |
---|
96,28 → 96,28 |
} |
@Override |
public synchronized int size() { |
return this.immutable.size(); |
public int size() { |
return this.getImmutable().size(); |
} |
@Override |
public synchronized boolean isEmpty() { |
return this.immutable.isEmpty(); |
public boolean isEmpty() { |
return this.getImmutable().isEmpty(); |
} |
@Override |
public synchronized boolean containsKey(final Object key) { |
return this.immutable.containsKey(key); |
public boolean containsKey(final Object key) { |
return this.getImmutable().containsKey(key); |
} |
@Override |
public synchronized boolean containsValue(final Object value) { |
return this.immutable.containsValue(value); |
public boolean containsValue(final Object value) { |
return this.getImmutable().containsValue(value); |
} |
@Override |
public synchronized V get(final Object key) { |
return this.immutable.get(key); |
public V get(final Object key) { |
return this.getImmutable().get(key); |
} |
@Override |
177,12 → 177,12 |
// equals |
@Override |
public synchronized boolean equals(final Object o) { |
return this.immutable.equals(o); |
public boolean equals(final Object o) { |
return this.getImmutable().equals(o); |
} |
@Override |
public synchronized int hashCode() { |
return this.immutable.hashCode(); |
public int hashCode() { |
return this.getImmutable().hashCode(); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/cc/Cookies.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
21,6 → 21,7 |
import java.util.Map.Entry; |
import java.util.Objects; |
import java.util.Set; |
import java.util.function.Function; |
import com.google.gson.reflect.TypeToken; |
186,7 → 187,22 |
final Collection<T> res = (Collection<T>) object; |
return res; |
} |
public final <T> Collection<T> computeCollectionIfAbsent(Object k, Class<T> clazz, Function<Object, ? extends Collection<T>> mappingFunction) { |
Collection<T> res; |
synchronized (this) { |
res = this.getCollection(k, clazz); |
// same algorithm as java.util.Map |
if (res == null) { |
res = mappingFunction.apply(k); |
if (res != null) |
this.putCollection(k, res, clazz); |
} |
} |
return res; |
} |
public final <T> List<T> getList(Object k, Class<T> clazz) { |
return (List<T>) this.getCollection(k, clazz); |
} |
202,6 → 218,20 |
return res; |
} |
public final <K, V> Map<K, V> computeMapIfAbsent(Object k, Class<K> keyClass, Class<V> valueClass, Function<Object, ? extends Map<K, V>> mappingFunction) { |
Map<K, V> res; |
synchronized (this) { |
res = this.getMap(k, keyClass, valueClass); |
// same algorithm as java.util.Map |
if (res == null) { |
res = mappingFunction.apply(k); |
if (res != null) |
this.putMap(k, res, keyClass, valueClass); |
} |
} |
return res; |
} |
public final <T> T getGeneric(Object k, TypeToken<T> clazz) { |
final Object object = checkType(k, clazz, "putGeneric()"); |
@SuppressWarnings("unchecked") |
209,6 → 239,20 |
return res; |
} |
public final <T> T computeIfAbsent(Object k, TypeToken<T> clazz, Function<Object, ? extends T> mappingFunction) { |
T res; |
synchronized (this) { |
res = this.getGeneric(k, clazz); |
// same algorithm as java.util.Map |
if (res == null) { |
res = mappingFunction.apply(k); |
if (res != null) |
this.putGeneric(k, res, clazz); |
} |
} |
return res; |
} |
@Override |
public synchronized String toString() { |
return this.getClass().getSimpleName() + " of " + this.map.keySet(); |
/trunk/OpenConcerto/src/org/openconcerto/utils/cc/LinkedIdentitySet.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,12 → 13,8 |
package org.openconcerto.utils.cc; |
import java.util.AbstractSet; |
import java.util.Collection; |
import java.util.Collections; |
import java.util.Iterator; |
import java.util.LinkedList; |
import java.util.List; |
/** |
* An IdentitySet maintaining insertion order. |
26,119 → 22,25 |
* @param <E> the type of elements maintained by this set |
* @see IdentityHashSet |
*/ |
public final class LinkedIdentitySet<E> extends AbstractSet<E> implements IdentitySet<E>, Cloneable { |
public final class LinkedIdentitySet<E> extends ListIdentitySet<E> implements Cloneable { |
private final LinkedList<E> list; |
public LinkedIdentitySet() { |
this.list = new LinkedList<E>(); |
super(); |
} |
public LinkedIdentitySet(Collection<? extends E> c) { |
this.list = new LinkedList<E>(); |
this.addAll(c); |
public LinkedIdentitySet(int expectedSize) { |
super(expectedSize); |
} |
public final List<E> getList() { |
return Collections.unmodifiableList(this.list); |
public LinkedIdentitySet(Collection<? extends E> c) { |
super(c); |
} |
@Override |
public boolean add(E e) { |
if (this.contains(e)) |
return false; |
else { |
this.list.add(e); |
return true; |
} |
protected LinkedList<E> newList(int expectedSize) { |
return new LinkedList<>(); |
} |
@Override |
public boolean addAll(Collection<? extends E> c) { |
// let the super which calls add() since we don't want to build a map to use Map.addAll() |
return super.addAll(c); |
} |
@Override |
public void clear() { |
this.list.clear(); |
} |
@Override |
public int size() { |
return this.list.size(); |
} |
@Override |
public boolean isEmpty() { |
return this.list.isEmpty(); |
} |
@Override |
public Iterator<E> iterator() { |
return this.list.iterator(); |
} |
@Override |
public boolean contains(Object o) { |
Iterator<E> e = iterator(); |
while (e.hasNext()) |
if (o == e.next()) |
return true; |
return false; |
} |
@Override |
public boolean containsAll(Collection<?> c) { |
// let the super which calls contains() |
return super.containsAll(c); |
} |
@Override |
public boolean remove(Object o) { |
Iterator<E> e = iterator(); |
while (e.hasNext()) { |
if (o == e.next()) { |
e.remove(); |
return true; |
} |
} |
return false; |
} |
/* |
* (From IdentityHashMap) Must revert from AbstractSet's impl to AbstractCollection's, as the |
* former contains an optimization that results in incorrect behavior when c is a smaller |
* "normal" (non-identity-based) Set. |
*/ |
@Override |
public boolean removeAll(Collection<?> c) { |
boolean modified = false; |
for (Iterator<E> i = iterator(); i.hasNext();) { |
if (c.contains(i.next())) { |
i.remove(); |
modified = true; |
} |
} |
return modified; |
} |
@Override |
public boolean retainAll(Collection<?> c) { |
// let the super which calls remove() |
return super.retainAll(c); |
} |
@Override |
public Object[] toArray() { |
return this.list.toArray(); |
} |
@Override |
public <T> T[] toArray(T[] a) { |
return this.list.toArray(a); |
} |
/** |
* Returns a shallow copy of this <tt>HashSet</tt> instance: the elements themselves are not |
* cloned. |
145,6 → 47,7 |
* |
* @return a shallow copy of this set |
*/ |
@Override |
public Object clone() { |
return new LinkedIdentitySet<E>(this); |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/cc/ListIdentitySet.java |
---|
New file |
0,0 → 1,224 |
/* |
* 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.cc; |
import org.openconcerto.utils.CollectionUtils; |
import org.openconcerto.utils.IntHashSet; |
import java.util.AbstractSet; |
import java.util.Collection; |
import java.util.Collections; |
import java.util.Iterator; |
import java.util.List; |
/** |
* An IdentitySet maintaining insertion order. |
* |
* @param <E> the type of elements maintained by this set |
* @see IdentityHashSet |
*/ |
public abstract class ListIdentitySet<E> extends AbstractSet<E> implements IdentitySet<E> { |
static final int THRESHOLD = 16; |
private final List<E> list; |
private IntHashSet identityHashCodes; |
protected ListIdentitySet(final Collection<? extends E> c) { |
this(c.size()); |
if (c instanceof ListIdentitySet) { |
final ListIdentitySet<? extends E> s = (ListIdentitySet<? extends E>) c; |
this.list.addAll(c); |
this.identityHashCodes = s.identityHashCodes; |
} else { |
this.addAll(c); |
} |
} |
protected ListIdentitySet() { |
this(16); |
} |
protected ListIdentitySet(final int expectedSize) { |
this.list = this.newList(expectedSize); |
this.identityHashCodes = null; |
} |
public final boolean hasHashCodes() { |
return this.identityHashCodes != null; |
} |
public final void initHashCodes() { |
if (this.identityHashCodes == null) { |
this.identityHashCodes = new IntHashSet(this.size()); |
for (final E i : this.list) { |
this.identityHashCodes.add(System.identityHashCode(i)); |
} |
} |
} |
final boolean sameSize() { |
return this.identityHashCodes == null || this.identityHashCodes.size() == this.list.size(); |
} |
protected abstract List<E> newList(int expectedSize); |
public final List<E> getList() { |
return Collections.unmodifiableList(this.list); |
} |
@Override |
public final boolean add(E e) { |
if (this.identityHashCodes != null) { |
final boolean added = this.identityHashCodes.add(System.identityHashCode(e)); |
if (added) { |
this.list.add(e); |
} |
assert sameSize(); |
return added; |
} else { |
final boolean contains = this.contains(e); |
if (!contains) { |
this.list.add(e); |
if (this.size() > THRESHOLD) { |
initHashCodes(); |
} |
} |
assert sameSize(); |
return !contains; |
} |
} |
@Override |
public final boolean addAll(Collection<? extends E> c) { |
// let the super which calls add() since we don't want to build a map to use Map.addAll() |
return super.addAll(c); |
} |
@Override |
public final void clear() { |
this.list.clear(); |
this.identityHashCodes = null; |
} |
@Override |
public final int size() { |
return this.list.size(); |
} |
@Override |
public final boolean isEmpty() { |
return this.list.isEmpty(); |
} |
@Override |
public final Iterator<E> iterator() { |
if (this.identityHashCodes == null) |
return this.list.iterator(); |
return new Iterator<E>() { |
private final Iterator<E> iter = ListIdentitySet.this.list.iterator(); |
private E lastReturned = null; |
@Override |
public boolean hasNext() { |
return this.iter.hasNext(); |
} |
@Override |
public E next() { |
this.lastReturned = this.iter.next(); |
return this.lastReturned; |
} |
@Override |
public void remove() { |
this.iter.remove(); |
ListIdentitySet.this.identityHashCodes.remove(System.identityHashCode(this.lastReturned)); |
assert sameSize(); |
} |
}; |
} |
@Override |
public boolean contains(Object o) { |
if (this.identityHashCodes != null) |
return this.identityHashCodes.contains(System.identityHashCode(o)); |
else |
return CollectionUtils.identityContains(this.list, o); |
} |
@Override |
public final boolean containsAll(Collection<?> c) { |
// let the super which calls contains() |
return super.containsAll(c); |
} |
@Override |
public boolean remove(Object o) { |
final boolean res; |
if (this.identityHashCodes != null) { |
res = this.identityHashCodes.remove(System.identityHashCode(o)); |
if (res) |
CollectionUtils.identityRemove(this.list, o); |
} else { |
res = CollectionUtils.identityRemove(this.list, o); |
} |
assert sameSize(); |
return res; |
} |
/* |
* (From IdentityHashMap) Must revert from AbstractSet's impl to AbstractCollection's, as the |
* former contains an optimization that results in incorrect behavior when c is a smaller |
* "normal" (non-identity-based) Set. |
*/ |
@Override |
public final boolean removeAll(Collection<?> c) { |
boolean modified = false; |
for (Iterator<E> i = iterator(); i.hasNext();) { |
if (c.contains(i.next())) { |
i.remove(); |
modified = true; |
} |
} |
assert sameSize(); |
return modified; |
} |
@Override |
public final boolean retainAll(Collection<?> c) { |
// let the super which calls remove() |
return super.retainAll(c); |
} |
@Override |
public final Object[] toArray() { |
return this.list.toArray(); |
} |
@Override |
public final <T> T[] toArray(T[] a) { |
return this.list.toArray(a); |
} |
@Override |
public final int hashCode() { |
int result = 0; |
for (E key : this.list) |
result += System.identityHashCode(key); |
return result; |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/cc/ArrayIdentitySet.java |
---|
New file |
0,0 → 1,53 |
/* |
* 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.cc; |
import java.util.ArrayList; |
import java.util.Collection; |
/** |
* This class implements the <tt>Set</tt> interface with a list, using reference-equality in place |
* of object-equality when comparing items. |
* |
* @param <E> the type of elements maintained by this set |
*/ |
public final class ArrayIdentitySet<E> extends ListIdentitySet<E> implements Cloneable { |
public ArrayIdentitySet() { |
super(); |
} |
public ArrayIdentitySet(int expectedMaxSize) { |
super(expectedMaxSize); |
} |
public ArrayIdentitySet(Collection<? extends E> c) { |
super(c); |
} |
@Override |
protected ArrayList<E> newList(int expectedSize) { |
return new ArrayList<>(expectedSize); |
} |
/** |
* Returns a shallow copy of this instance: the elements themselves are not cloned. |
* |
* @return a shallow copy of this set |
*/ |
@Override |
public Object clone() { |
return new ArrayIdentitySet<E>(this); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/cc/CustomEquals.java |
---|
31,6 → 31,15 |
*/ |
public class CustomEquals { |
static public final <T> boolean nullSafeEquals(final T o1, final T o2, final BiPredicate<? super T, ? super T> equal) { |
if (o1 == null) |
return o2 == null; |
else if (o2 == null) |
return false; |
else |
return equal.test(o1, o2); |
} |
static private final HashingStrategy<Object> DEFAULT = new HashingStrategy<Object>() { |
@Override |
public boolean equals(Object object1, Object object2) { |
/trunk/OpenConcerto/src/org/openconcerto/utils/cc/IExnRunnable.java |
---|
New file |
0,0 → 1,20 |
/* |
* 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.cc; |
public interface IExnRunnable<X extends Exception> { |
public abstract void run() throws X; |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/cc/IncludeExclude.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 |
50,13 → 50,40 |
@SafeVarargs |
public static <T> IncludeExclude<T> getNormalized(final T... includes) { |
return getNormalized(Arrays.asList(includes)); |
return getNormalizedInclude(Arrays.asList(includes)); |
} |
@Deprecated |
public static <T> IncludeExclude<T> getNormalized(final Collection<? extends T> includes) { |
return getNormalizedInclude(includes); |
} |
/** |
* Return the normalized version including only the passed values. E.g. if <code>includes</code> |
* is empty or <code>null</code>, this won't allocate any memory and {@link #getEmpty()} or |
* {@link #getFull()} will be returned. |
* |
* @param <T> type of items |
* @param includes which values to include. |
* @return the normalized version. |
*/ |
public static <T> IncludeExclude<T> getNormalizedInclude(final Collection<? extends T> includes) { |
return getNormalized(includes, Collections.<T> emptySet()); |
} |
/** |
* Return the normalized version excluding only the passed values. E.g. if <code>excludes</code> |
* is empty or <code>null</code>, this won't allocate any memory and {@link #getFull()} or |
* {@link #getEmpty()} will be returned. |
* |
* @param <T> type of items |
* @param excludes which values to exclude. |
* @return the normalized version. |
*/ |
public static <T> IncludeExclude<T> getNormalizedExclude(final Collection<? extends T> excludes) { |
return getNormalized(null, excludes); |
} |
public static <T> IncludeExclude<T> getNormalized(final Collection<? extends T> includes, final Collection<? extends T> excludes) { |
return new IncludeExclude<T>(includes, excludes).normal; |
} |
74,6 → 101,7 |
* |
* @param includes which objects to include, <code>null</code> meaning all. |
* @param excludes which objects to exclude, <code>null</code> meaning all. |
* @see #isIncluded(Object) |
*/ |
public IncludeExclude(final Collection<? extends T> includes, final Collection<? extends T> excludes) { |
this(includes, excludes, false); |
/trunk/OpenConcerto/src/org/openconcerto/utils/FormatGroup.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,7 |
*/ |
public class FormatGroup extends Format { |
private final List<? extends Format> formats; |
private final List<Format> formats; |
private int formatIndex; |
public FormatGroup(Format... formats) { |
44,11 → 44,11 |
public FormatGroup(final List<? extends Format> formats) { |
if (formats.size() == 0) |
throw new IllegalArgumentException("formats must not be empty"); |
this.formats = formats; |
this.formats = CollectionUtils.toImmutableList(formats); |
this.formatIndex = 0; |
} |
public final List<? e |