Dépôt officiel du code source de l'ERP OpenConcerto
/trunk/OpenConcerto/lib/mysql-connector-java-5.1.40-bin.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/poi-3.17.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/poi-4.1.0.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/flatlaf-0.41.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/fontbox-2.0.19.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/pdfbox-2.0.19.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/jOpenDocument-1.4rc2.badSecurity.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/jOpenDocument-1.4rc2.badSecurity.jar |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/OpenConcerto/lib/pdfbox2d.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/pdfbox2d.jar |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/OpenConcerto/lib/fontbox-2.0.22.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/fontbox-2.0.22.jar |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/OpenConcerto/lib/pdfbox-2.0.22.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/pdfbox-2.0.22.jar |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/OpenConcerto/lib/jOpenCalendar.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/flatlaf-1.2.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/flatlaf-1.2.jar |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/OpenConcerto/lib/mysql-connector-java-5.1.45-bin.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/lib/mysql-connector-java-5.1.45-bin.jar |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/OpenConcerto/lib/jOpenDocument-1.4rc2.jar |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/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/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/VenteFactureSituation.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/EtatVentes.xml |
---|
124,4 → 124,28 |
</element> |
</table3> |
<element4 location="A4" type="Value" ValueName="DATE"> |
</element4> |
<table4 firstLine="7" endLine="52" endPageLine="54" lastColumn="F" base="Societe" table="SAISIE_VENTE_FACTURE_ELEMENT"> |
<element location="A" type="fill"> |
<field base="Societe" name="CODE" /> |
</element> |
<element location="B" type="fill"> |
<field base="Societe" name="NOM" /> |
</element> |
<element location="D" type="fill"> |
<field base="Societe" name="QTE" /> |
</element> |
<element location="E" type="fill"> |
<field base="Societe" name="QTE_REEL" /> |
</element> |
<element location="F" type="fill"> |
<field base="Societe" name="QTE_MIN" /> |
</element> |
</table4> |
</contentDocument> |
/trunk/OpenConcerto/Configuration/Template/Default/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/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/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/CommandeClient.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/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/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/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/Courrier.odt |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/Commande.xml |
---|
25,7 → 25,7 |
</element> |
<element location="B6" type="fill"> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIREN "/> |
<field base="Common" table="SOCIETE_COMMON" name="NUM_SIRET" prefix="N° de SIRET "/> |
</element> |
<element location="B7" type="replace" replacePattern="_"> |
/trunk/OpenConcerto/Configuration/Template/Default/JournauxMois.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/FichePayeSimplifiee.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/EtatChargesPaye.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/FicheArticle.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/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/Relance1.odt |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/VenteFacture.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/GrandLivre.ods |
---|
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/DemandePrix.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/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/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/Journaux.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/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/ExportArticle.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/Balance.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/SituationCompte.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/SituationCompte.ods |
---|
New file |
Property changes: |
Added: svn:mime-type |
+application/octet-stream |
\ No newline at end of property |
/trunk/OpenConcerto/Configuration/Template/Default/VentilationAnalytique.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/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/BonLivraison.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/FicheRelance.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/DepotCheque.xml |
---|
New file |
0,0 → 1,60 |
<?xml version="1.0" encoding="UTF-8" ?> |
<contentDocument> |
<element location="H1" type="fill"> |
<field base="Societe" name="DATE"> |
</field> |
</element> |
<element location="K56" type="fill"> |
<field base="Societe" name="MONTANT" type="devise"> |
</field> |
</element> |
<element location="A55" type="fill"> |
<field base="Societe" name="NOM"> |
</field> |
</element> |
<element location="A56" type="fill"> |
<field base="Societe" name="ID_BANQUE"> |
<field base="Societe" name="NOM" prefix ="Dépôt sur la banque "/> |
</field> |
</element> |
<table endPageLine="56" firstLine="5" endLine="53" lastColumn="L" base="Societe" table="DEPOT_CHEQUE_ELEMENT"> |
<element location="A" type="fill"> |
<field name="BANQUE" /> |
</element> |
<element location="B" type="fill"> |
<field name="DATE" /> |
</element> |
<element location="C" type="fill"> |
<field name="NUMERO" /> |
</element> |
<element location="E" type="fill"> |
<field name="ID_CLIENT" > |
<field name="NOM" /> |
</field> |
</element> |
<element location="J" type="fill"> |
<field name="PIECE" /> |
</element> |
<element location="K" type="fill"> |
<field name="MONTANT" type="devise"/> |
</element> |
<element location="L" type="LineReference"> |
<field name="MONTANT" type="LineReference"/> |
</element> |
</table> |
</contentDocument> |
/trunk/OpenConcerto/Configuration/Template/Default/AvoirF.ods |
---|
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/FichePaye.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/RepartitionAnalytique.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/FactureFournisseur.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/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/JournauxAnalytique.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/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/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/EtatVentes.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/BonReception.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/ListeFacture.ods |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/LivrePaye.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/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/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/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/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/Avoir.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/Relance2.odt |
---|
Cannot display: file marked as a binary type. |
svn:mime-type = application/octet-stream |
/trunk/OpenConcerto/Configuration/Template/Default/CommandeClient.odsp |
---|
1,6 → 1,6 |
<odsp> |
<spliteveryrow> |
<sheet number="0">65</sheet> |
<sheet number="0">66</sheet> |
</spliteveryrow> |
<offset x="40" y ="20"/> |
<resize percent="85"/> |
/trunk/OpenConcerto/Configuration/Template/Default/SituationCompte.xml |
---|
New file |
0,0 → 1,57 |
<?xml version="1.0" encoding="UTF-8" ?> |
<contentDocument> |
<element0 location="A1" type="Value" ValueName="SOCIETE_NOM"> |
</element0> |
<element0 location="A2" type="Value" ValueName="SOCIETE_ADRESSE"> |
</element0> |
<element0 location="A4" type="Value" ValueName="SOCIETE_VILLE"> |
</element0> |
<element0 location="A5" type="Value" prefix="Tél:" ValueName="SOCIETE_TEL"> |
</element0> |
<element0 location="D6" type="Value" ValueName="NOM_CLIENT"> |
</element0> |
<element0 location="F8" type="Value" ValueName="DATE"> |
</element0> |
<element0 location="G51" type="Value" ValueName="TOTAL_ECHEANCE"> |
</element0> |
<element0 location="G52" type="Value" ValueName="TOTAL_FUTUR"> |
</element0> |
<element0 location="G53" type="Value" ValueName="TOTAL_PASSE"> |
</element0> |
<table0 firstLine="17" endLine="50" endPageLine="58" lastColumn="G" base="Societe" table="ECHEANCE_CLIENT" pageRef="F1"> |
<element location="A" type="fill" useOOFormat="false"> |
<field base="Societe" name="DATE"/> |
</element> |
<element location="B" type="fill" useOOFormat="false"> |
<field base="Societe" name="PIECE"/> |
</element> |
<element location="C" type="fill" useOOFormat="false" maxChar="25"> |
<field base="Societe" name="LIBELLE"/> |
</element> |
<element location="D" type="fill" useOOFormat="false"> |
<field base="Societe" name="DATE_ECHEANCE"/> |
</element> |
<element location="E" type="fill" > |
<field base="Societe" name="DU"/> |
</element> |
<element location="F" type="fill" > |
<field base="Societe" name="REGLE"/> |
</element> |
<element location="G" type="fill" > |
<field base="Societe" name="SOLDE"/> |
</element> |
</table0> |
</contentDocument> |
/trunk/OpenConcerto/Configuration/Template/Default/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/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/ReportingTaxeComplementaire.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/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/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/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/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.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/src/product.properties |
---|
1,5 → 1,5 |
NAME=OpenConcerto |
VERSION=1.7.0 |
VERSION=1.7.1 |
ORGANIZATION_NAME=OpenConcerto |
ORGANIZATION_ID=org.openconcerto |
/trunk/OpenConcerto/src/com/zimbra/common/util/BEncoding.java |
---|
New file |
0,0 → 1,140 |
/* |
* |
*/ |
/* |
* Created on Jul 7, 2005 |
*/ |
package com.zimbra.common.util; |
import java.util.ArrayList; |
import java.util.HashMap; |
import java.util.Iterator; |
import java.util.List; |
import java.util.Map; |
import java.util.SortedMap; |
import java.util.TreeMap; |
/** |
* @author dkarp |
*/ |
public class BEncoding { |
public static final class BEncodingException extends Exception { |
BEncodingException(String msg) { super(msg); } |
BEncodingException(Exception e) { super(e); } |
} |
public static String encode(Map object) { |
return encode(new StringBuffer(), object).toString(); |
} |
public static String encode(List object) { |
return encode(new StringBuffer(), object).toString(); |
} |
public static Object decode(String data) throws BEncodingException { |
if (data == null) |
return null; |
try { |
Offset offset = new Offset(); |
Object result = decode(data.toCharArray(), offset); |
if (offset.offset != data.length()) |
throw new BEncodingException("extra characters at end of encoded string"); |
return result; |
} catch (BEncodingException e) { |
throw e; |
} catch (Exception e) { |
throw new BEncodingException(e); |
} |
} |
private static StringBuffer encode(StringBuffer sb, Object object) { |
if (object instanceof Map) { |
SortedMap tree = (object instanceof SortedMap ? (SortedMap) object : new TreeMap((Map) object)); |
sb.append('d'); |
if (!tree.isEmpty()) |
for (Iterator it = tree.entrySet().iterator(); it.hasNext(); ) { |
Map.Entry entry = (Map.Entry) it.next(); |
if (entry.getKey() != null && entry.getValue() != null) { |
encode(sb, entry.getKey().toString()); |
encode(sb, entry.getValue()); |
} |
} |
sb.append('e'); |
} else if (object instanceof List) { |
Object value; |
sb.append('l'); |
for (Iterator it = ((List) object).iterator(); it.hasNext(); ) |
if ((value = it.next()) != null) |
encode(sb, value); |
sb.append('e'); |
} else if (object instanceof Long || object instanceof Integer || object instanceof Short || object instanceof Byte) { |
sb.append('i').append(object).append('e'); |
} else if (object != null) { |
String value = object.toString(); |
sb.append(value.length()).append(':').append(value); |
} |
return sb; |
} |
private static final class Offset { |
int offset; |
} |
private static Object decode(char[] buffer, Offset offset) throws BEncodingException { |
Object key, value; |
char c = buffer[offset.offset++]; |
switch (c) { |
case 'd': |
Map map = new HashMap(); |
while ((key = decode(buffer, offset)) != null) { |
if ((value = decode(buffer, offset)) == null) |
throw new BEncodingException("missing dictionary value for key " + key.toString()); |
map.put(key.toString(), value); |
} |
return map; |
case 'l': |
List list = new ArrayList(); |
while ((key = decode(buffer, offset)) != null) |
list.add(key); |
return list; |
case 'e': |
return null; |
case 'i': |
return new Long(readLong(buffer, offset, 'e')); |
default: |
offset.offset--; |
long length = readLong(buffer, offset, ':'); |
int start = offset.offset; |
offset.offset += length; |
return new String(buffer, start, (int) length); |
} |
} |
private static long readLong(char[] buffer, Offset offset, char terminator) { |
int start = offset.offset; |
while (buffer[offset.offset++] != terminator) ; |
return Long.parseLong(new String(buffer, start, offset.offset - start - 1)); |
} |
public static void main(String[] args) throws BEncodingException { |
List list = new ArrayList(); |
list.add(new Integer(654)); |
list.add("hwhergk"); |
list.add(new StringBuffer("74x")); |
Map map = new HashMap(); |
map.put("testing", new Long(5)); |
map.put("foo2", "bar"); |
map.put("herp", list); |
map.put("Foo", new Float(6.7)); |
map.put("yy", new TreeMap()); |
String encoded = encode(map); |
System.out.println(encoded); |
System.out.println(decode(encoded)); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/OutlookEmail.vbs |
---|
File deleted |
/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/system/Powershell.java |
---|
New file |
0,0 → 1,78 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific |
* language governing permissions and limitations under the License. |
* |
* When distributing the software, include this License Header Notice in each file. |
*/ |
package org.openconcerto.utils.system; |
import java.nio.charset.StandardCharsets; |
import java.util.Iterator; |
import java.util.List; |
import java.util.regex.Matcher; |
import java.util.regex.Pattern; |
public final class Powershell { |
private static final Powershell INSTANCE = new Powershell(); |
// No static methods to future-proof if the syntax changes in the future |
static public final Powershell getInstance() { |
return INSTANCE; |
} |
static private final Pattern PS_CHARS = Pattern.compile("\\R+|'"); |
private Powershell() { |
} |
public final String quote(String s) { |
final StringBuffer sb = new StringBuffer((int) (s.length() * 1.1)); |
quote(s, sb); |
return sb.toString(); |
} |
public final void quote(String s, final StringBuffer sb) { |
sb.append("'"); |
final Matcher m = PS_CHARS.matcher(s); |
while (m.find()) { |
final String replacement; |
if (m.group().equals("'")) { |
replacement = "''"; |
} else { |
// `n isn't expanded in single quoted strings and it's complicated to handle all |
// needed escaping in double quoted strings, so just concatenate a single newline. |
replacement = "' + \"`n\" + '"; |
} |
m.appendReplacement(sb, replacement); |
} |
m.appendTail(sb); |
sb.append("'"); |
} |
// @("C:\Users\ILM\Documents", "2") |
public final String quoteArray(List<String> l) { |
final StringBuffer sb = new StringBuffer(l.size() * 64); |
sb.append("@("); |
final Iterator<String> iter = l.iterator(); |
while (iter.hasNext()) { |
final String s = iter.next(); |
quote(s, sb); |
if (iter.hasNext()) |
sb.append(", "); |
} |
sb.append(")"); |
return sb.toString(); |
} |
public final String getEncodedCommand(final String s) { |
return java.util.Base64.getEncoder().encodeToString(s.getBytes(StandardCharsets.UTF_16LE)); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/system/package-info.java |
---|
New file |
0,0 → 1,17 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific |
* language governing permissions and limitations under the License. |
* |
* When distributing the software, include this License Header Notice in each file. |
*/ |
/** |
* Contains class to interact with the OS outside the VM. |
*/ |
package org.openconcerto.utils.system; |
/trunk/OpenConcerto/src/org/openconcerto/utils/cache/ICacheSupport.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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,12 |
package org.openconcerto.utils.cache; |
import org.openconcerto.utils.CollectionUtils; |
import org.openconcerto.utils.IScheduledFutureTask; |
import java.util.Collections; |
import java.util.HashMap; |
import java.util.Iterator; |
import java.util.LinkedHashMap; |
import java.util.List; |
import java.util.Map; |
import java.util.Map.Entry; |
178,7 → 178,7 |
} |
final synchronized Map<D, CacheWatcher<? super D>> watch(Set<? extends D> data, final CacheItem<?, ?, D> item) { |
final Map<D, CacheWatcher<? super D>> res = new LinkedHashMap<D, CacheWatcher<? super D>>(data.size(), 1.0f); |
final Map<D, CacheWatcher<? super D>> res = CollectionUtils.newLinkedHashMap(data.size()); |
for (final D d : data) { |
final CacheWatcher<? super D> watcher = this.watch(d, item); |
if (watcher != null) |
/trunk/OpenConcerto/src/org/openconcerto/utils/cache/ICache.java |
---|
32,6 → 32,8 |
import java.util.concurrent.TimeUnit; |
import java.util.logging.Level; |
import net.jcip.annotations.GuardedBy; |
/** |
* To keep results computed from some data. The results will be automatically invalidated after some |
* period of time or when the data is modified. |
52,7 → 54,9 |
private final ICacheSupport<D> supp; |
// linked to fifo, ATTN the values in this map can be invalid since clear() is called without |
// the lock on CacheValue |
@GuardedBy("this") |
private final LinkedHashMap<K, CacheItem<K, V, D>> cache; |
@GuardedBy("this") |
private final Map<K, CacheItem<K, V, D>> running; |
private final int delay; |
private final int size; |
224,17 → 228,21 |
removeRunning(getRunningValFromRes(res)); |
} |
private final synchronized void removeRunning(final CacheItem<K, V, D> val) { |
private final synchronized boolean removeRunning(final CacheItem<K, V, D> val) { |
if (val == null) |
return; |
return false; |
final K key = val.getKey(); |
if (this.running.get(key) == val) |
final boolean removed; |
if (this.running.get(key) == val) { |
this.removeRunning(key); |
else |
removed = true; |
} else { |
// either val wasn't created in this cache or another value was already put in this |
// cache |
val.setRemovalType(RemovalType.EXPLICIT); |
removed = val.setRemovalType(RemovalType.EXPLICIT); |
} |
assert val.getRemovalType() != null; |
return removed; |
} |
private final synchronized void removeRunning(K key) { |
427,10 → 435,10 |
final boolean clear(final CacheItem<K, V, D> val) { |
if (val.getRemovalType() == null) |
throw new IllegalStateException("Not yet removed : " + val); |
final boolean toBeRemoved; |
final boolean removedFromRunning, toBeRemoved; |
synchronized (this) { |
log("clear", val); |
this.removeRunning(val); |
removedFromRunning = this.removeRunning(val); |
toBeRemoved = this.cache.get(val.getKey()) == val; |
if (toBeRemoved) { |
this.cache.remove(val.getKey()); |
437,18 → 445,28 |
} |
} |
// NOTE these events are often fired with our monitor since this method is called with it |
if (removedFromRunning || toBeRemoved) { |
this.propSupp.firePropertyChange(new Event<K, V, D>(this, ITEMS_CHANGED, null, null)); |
this.propSupp.firePropertyChange(this.createItemEvent(ITEM_REMOVED, val, null)); |
} |
return toBeRemoved; |
} |
public final synchronized void clear() { |
for (final CacheItem<K, V, D> val : new ArrayList<CacheItem<K, V, D>>(this.cache.values())) |
val.setRemovalType(RemovalType.EXPLICIT); |
assert this.size() == 0; |
for (final CacheItem<K, V, D> val : new ArrayList<CacheItem<K, V, D>>(this.cache.values())) { |
// We have our monitor so if val is still in us but setRemovalType() was already called, |
// then it means another thread is waiting on our monitor in clear(CacheItem). In that |
// case, just call it now so that this is empty at the end of this method. |
if (!val.setRemovalType(RemovalType.EXPLICIT)) { |
final boolean removed = this.clear(val); |
assert removed; |
} |
} |
assert this.size() == 0 : this + " expected to be empty but contains : " + this.cache.keySet(); |
for (final CacheItem<K, V, D> val : new ArrayList<CacheItem<K, V, D>>(this.running.values())) |
val.setRemovalType(RemovalType.EXPLICIT); |
assert this.running.size() == 0; |
assert this.running.size() == 0 : this + " expected to have no running but contains : " + this.running.keySet(); |
} |
private final void log(String msg, Object subject) { |
/trunk/OpenConcerto/src/org/openconcerto/utils/BaseDirs.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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,18 |
super(info, subdir); |
} |
private String getHomePath() { |
// ATTN on FreeBSD man 8 service : "service command sets HOME to /" thus one should |
// probably not use this class (perhaps Portable). |
return StringUtils.coalesce(System.getenv("HOME"), System.getProperty("user.home")); |
} |
private String getInHomePath(final String subPath) { |
final String homePath = getHomePath(); |
// If no home, use current working directory |
return homePath == null ? subPath : homePath + '/' + subPath; |
} |
@Override |
protected File _getAppDataFolder() { |
/* |
42,7 → 54,7 |
* should be stored. If $XDG_DATA_HOME is either not set or empty, a default equal to |
* $HOME/.local/share should be used. |
*/ |
return new File(StringUtils.coalesce(System.getenv("XDG_DATA_HOME"), System.getenv("HOME") + "/.local/share"), this.getAppID()); |
return new File(StringUtils.coalesce(System.getenv("XDG_DATA_HOME"), getInHomePath(".local/share")), this.getAppID()); |
} |
@Override |
52,7 → 64,7 |
* configuration files should be stored. If $XDG_CONFIG_HOME is either not set or empty, |
* a default equal to $HOME/.config should be used. |
*/ |
return new File(StringUtils.coalesce(System.getenv("XDG_CONFIG_HOME"), System.getenv("HOME") + "/.config"), this.getAppID()); |
return new File(StringUtils.coalesce(System.getenv("XDG_CONFIG_HOME"), getInHomePath(".config")), this.getAppID()); |
} |
@Override |
62,7 → 74,7 |
* non-essential data files should be stored. If $XDG_CACHE_HOME is either not set or |
* empty, a default equal to $HOME/.cache should be used. |
*/ |
return new File(StringUtils.coalesce(System.getenv("XDG_CACHE_HOME"), System.getenv("HOME") + "/.cache"), this.getAppID()); |
return new File(StringUtils.coalesce(System.getenv("XDG_CACHE_HOME"), getInHomePath(".cache")), this.getAppID()); |
} |
} |
211,7 → 223,8 |
return res != null ? res : this.getAppID(); |
} |
protected File getFolderToWrite(final File dir) throws IOException { |
static public final File getFolderToWrite(final File dir) throws IOException { |
// MAYBE test for symlink |
if (dir.isDirectory() && dir.canWrite()) |
return dir; |
if (dir.exists()) |
246,7 → 259,7 |
} |
protected File _getPreferencesFolder() { |
return this.getAppDataFolder(); |
return this._getAppDataFolder(); |
} |
// where to write configuration |
271,6 → 284,23 |
return getFolderToWrite(this.getCacheFolder()); |
} |
protected File _getStateFolder() { |
return this._getCacheFolder(); |
} |
// where to write data that is non-essential but cannot be recreated |
// - logfiles |
// - state of application windows on exit |
// - recently opened files |
// See STATE directory in https://wiki.debian.org/XDGBaseDirectorySpecification |
public final File getStateFolder() { |
return getSubDir(_getStateFolder()); |
} |
public final File getStateFolderToWrite() throws IOException { |
return getFolderToWrite(this.getStateFolder()); |
} |
@Override |
public String toString() { |
return BaseDirs.class.getSimpleName() + " " + this.getClass().getSimpleName(); |
/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<? extends Format> getFormats() { |
public final List<Format> getFormats() { |
return this.formats; |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/ProcessStreams.java |
---|
21,10 → 21,14 |
import java.io.PrintStream; |
import java.lang.ProcessBuilder.Redirect; |
import java.util.concurrent.Callable; |
import java.util.concurrent.CountDownLatch; |
import java.util.concurrent.CancellationException; |
import java.util.concurrent.CompletableFuture; |
import java.util.concurrent.ExecutionException; |
import java.util.concurrent.ExecutorService; |
import java.util.concurrent.Executors; |
import java.util.concurrent.Future; |
import java.util.concurrent.TimeUnit; |
import java.util.concurrent.TimeoutException; |
import java.util.function.Supplier; |
/** |
32,55 → 36,41 |
* |
* @author Sylvain |
*/ |
public class ProcessStreams { |
public class ProcessStreams implements AutoCloseable { |
static public enum Action { |
/** |
* Redirect process streams to ours. |
* |
* @deprecated use {@link ProcessStreams#redirect(ProcessBuilder)} (or |
* {@link Redirect#INHERIT} directly) as it makes sure that |
* {@link Process#waitFor()} only returns once all streams are flushed. |
*/ |
REDIRECT, |
/** |
* Consume streams. |
*/ |
CONSUME, |
/** |
* Close process streams. NOTE : some programs might fail (e.g. route on FreeBSD), in those |
* cases use {@link #CONSUME}. |
*/ |
CLOSE, |
/** |
* Do nothing, which is dangerous as the process will hang until its output is read. |
*/ |
DO_NOTHING |
} |
// Don't set too low, this is a maximum, it would only fail on slow computers. |
private static final int MAX_SHUTTING_DOWN_DELAY = 500; |
// Added to Java 9 |
public static final Redirect DISCARD = Redirect.to(StreamUtils.NULL_FILE); |
static public final ProcessBuilder redirect(final ProcessBuilder pb) { |
return pb.redirectErrorStream(true).redirectOutput(Redirect.INHERIT); |
// ATTN don't use redirectErrorStream(true) as this would merge the error and output of pb |
// into the VM output. |
return pb.redirectError(Redirect.INHERIT).redirectOutput(Redirect.INHERIT); |
} |
static public final Process handle(final Process p, final Action action) throws IOException { |
if (action == Action.CLOSE) { |
p.getInputStream().close(); |
p.getErrorStream().close(); |
} else if (action == Action.REDIRECT) { |
new ProcessStreams(p, System.out, System.err); |
} else if (action == Action.CONSUME) { |
new ProcessStreams(p, StreamUtils.NULL_OS, StreamUtils.NULL_OS); |
/** |
* Consume all streams. Needed since some programs might fail (e.g. route on FreeBSD) if the |
* streams are just closed. |
* |
* @param proc the process. |
* @return the passed process. |
*/ |
static public final Process consume(final Process proc) { |
try (final ProcessStreams streams = new ProcessStreams(proc)) { |
streams.start(StreamUtils.NULL_OS, StreamUtils.NULL_OS); |
streams.awaitTermination(); |
} catch (Exception e) { |
throw new IllegalStateException("Couldn't consume output of " + proc, e); |
} |
return p; |
return proc; |
} |
private final ExecutorService exec = Executors.newFixedThreadPool(2); |
private final CountDownLatch latch; |
private final Future<?> out; |
private final Future<?> err; |
private final Process process; |
private Future<?> out; |
private Future<?> err; |
/** |
* Create a new instance and start reading from the passed process. If a passed |
92,23 → 82,35 |
* @param out where to write the {@link Process#getInputStream() standard output}. |
* @param err where to write the {@link Process#getErrorStream() standard error}. |
*/ |
public ProcessStreams(final Process p, final OutputStream out, final OutputStream err) { |
this.latch = new CountDownLatch(2); |
this.out = writeToAsync(p::getInputStream, out); |
this.err = writeToAsync(p::getErrorStream, err); |
public ProcessStreams(final Process p) { |
this.process = p; |
} |
public final ProcessStreams start(final OutputStream out, final OutputStream err) { |
if (this.out != null) |
throw new IllegalStateException("Already started"); |
this.out = writeToAsync(this.process::getInputStream, out); |
this.err = writeToAsync(this.process::getErrorStream, err); |
assert this.out != null && this.err != null; |
this.exec.submit(new Runnable() { |
@Override |
public void run() { |
try { |
ProcessStreams.this.latch.await(); |
} catch (final InterruptedException e) { |
// ne rien faire |
e.printStackTrace(); |
// OK even if the future is cancelled before having started |
try { |
ProcessStreams.this.out.get(); |
} catch (Exception e) { |
} |
try { |
ProcessStreams.this.err.get(); |
} catch (Exception e) { |
} |
} finally { |
ProcessStreams.this.exec.shutdown(); |
} |
} |
}); |
return this; |
} |
protected final void stopOut() { |
121,16 → 123,42 |
private final void stop(final Future<?> f) { |
if (f == null) |
return; |
// TODO |
// ATTN don't interrupt, hangs in readLine() |
f.cancel(false); |
throw new IllegalStateException("Not started"); |
// ATTN Process returns InputStream which are not interruptible. |
f.cancel(true); |
} |
// From AutoCloseable : close() shouldn't throw InterruptedException |
@Override |
public void close() { |
this.exec.shutdownNow(); |
} |
public final void awaitTermination() throws InterruptedException, ExecutionException { |
this.out.get(); |
this.err.get(); |
if (!this.exec.awaitTermination(MAX_SHUTTING_DOWN_DELAY, TimeUnit.MILLISECONDS)) |
throw new IllegalStateException("Executor still not terminated after " + MAX_SHUTTING_DOWN_DELAY); |
} |
public final void awaitTermination(final long timeout, final TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { |
try { |
this.out.get(timeout, unit); |
} catch (CancellationException e) { |
// OK |
} |
try { |
this.err.get(timeout, unit); |
} catch (CancellationException e) { |
// OK |
} |
if (!this.exec.awaitTermination(MAX_SHUTTING_DOWN_DELAY, TimeUnit.MILLISECONDS)) |
throw new TimeoutException("Executor still not terminated after " + MAX_SHUTTING_DOWN_DELAY); |
} |
private final Future<?> writeToAsync(final Supplier<InputStream> insSupplier, final Object outs) { |
if (outs == null) { |
this.latch.countDown(); |
return null; |
return CompletableFuture.completedFuture(null); |
} |
return this.exec.submit(new Callable<Object>() { |
@Override |
142,8 → 170,6 |
else |
StreamUtils.copy(ins, (OutputStream) outs); |
return null; |
} finally { |
ProcessStreams.this.latch.countDown(); |
} |
} |
}); |
/trunk/OpenConcerto/src/org/openconcerto/utils/TableSorter.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
468,8 → 468,13 |
} else if (o2 == null) { |
comparison = 1; |
} else { |
comparison = getComparator(column).compare(o1, o2); |
final Comparator comparator = getComparator(column); |
try { |
comparison = comparator.compare(o1, o2); |
} catch (Exception e) { |
throw new IllegalStateException("Couldn't compare column " + column + " using " + comparator + " for objects " + o1 + " and " + o2, e); |
} |
} |
if (comparison != 0) { |
return directive.direction == DESCENDING ? -comparison : comparison; |
} |
645,6 → 650,11 |
@Override |
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { |
if (column < 0) { |
Log.get().severe("out of bound renderer, column :" + column); |
return new JLabel(""); |
} |
Component c = tableCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); |
if (c instanceof JLabel) { |
JLabel l = (JLabel) c; |
/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<?>) { |
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/text/DateTimeFormatBuilder.java |
---|
New file |
0,0 → 1,82 |
/* |
* 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.text; |
import org.openconcerto.utils.CollectionUtils; |
import org.openconcerto.utils.text.DateTimeFormat.Literal; |
import java.util.ArrayList; |
import java.util.Collections; |
import java.util.List; |
import java.util.Objects; |
public final class DateTimeFormatBuilder { |
static public final DateTimeFormat build(final Object... components) { |
final DateTimeFormatBuilder res = new DateTimeFormatBuilder(components.length); |
for (final Object o : components) { |
if (o instanceof DateTimeFormatComponent) |
res.add((DateTimeFormatComponent) o); |
else |
res.addLiteral(o.toString()); |
} |
return res.build(); |
} |
static public final DateTimeSkeleton buildSkeleton(final DateProp... components) { |
return new DateTimeSkeleton(CollectionUtils.toImmutableList(components)); |
} |
private final List<DateTimeFormatComponent> components; |
private boolean onlyDateProp = true; |
public DateTimeFormatBuilder() { |
this(8); |
} |
public DateTimeFormatBuilder(int initialCapacity) { |
super(); |
this.components = new ArrayList<>(initialCapacity); |
} |
public final boolean isEmpty() { |
return this.components.isEmpty(); |
} |
public final void clear() { |
this.components.clear(); |
this.onlyDateProp = true; |
} |
public final DateTimeFormatBuilder add(DateTimeFormatComponent comp) { |
this.components.add(comp); |
return this; |
} |
public final DateTimeFormatBuilder addLiteral(final String s) { |
this.components.add(new Literal(s)); |
this.onlyDateProp = false; |
return this; |
} |
public final DateTimeFormat build() { |
return new DateTimeFormat(this.components); |
} |
public final DateTimeSkeleton buildSkeleton() { |
if (!this.onlyDateProp) |
throw new IllegalStateException("Not only DateProp"); |
return new DateTimeSkeleton(Collections.unmodifiableList(CollectionUtils.castList(Objects.requireNonNull(this.components), DateProp.class))); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/text/DateProp.java |
---|
New file |
0,0 → 1,244 |
/* |
* 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.text; |
import org.openconcerto.utils.CollectionUtils; |
import org.openconcerto.utils.StringUtils; |
import org.openconcerto.utils.text.DateTimeFormat.Literal; |
import java.text.SimpleDateFormat; |
import java.time.format.DateTimeFormatterBuilder; |
import java.time.format.SignStyle; |
import java.time.format.TextStyle; |
import java.time.temporal.ChronoField; |
import java.util.Arrays; |
import java.util.Collections; |
import java.util.HashMap; |
import java.util.HashSet; |
import java.util.List; |
import java.util.Locale; |
import java.util.Map; |
import java.util.Objects; |
import java.util.Set; |
import java.util.SortedMap; |
import java.util.TreeMap; |
import java.util.function.Function; |
import java.util.regex.Matcher; |
import java.util.regex.Pattern; |
import com.ibm.icu.text.DateTimePatternGenerator; |
public final class DateProp extends DateTimeFormatComponent { |
static public final DateProp YEAR = new DateProp("year with four digits"); |
static public final DateProp MONTH_NAME = new DateProp("full name of the month"); |
static public final DateProp MONTH_NUMBER = new DateProp("2 digits number of the month (starting at 1)"); |
static public final DateProp DAY_IN_MONTH = new DateProp("2 digits day number in the month"); |
static public final DateProp DAY_NAME_IN_WEEK = new DateProp("full name of day"); |
static public final DateProp HOUR = new DateProp("hour in day (00-23)"); |
static public final DateProp MINUTE = new DateProp("minute in hour"); |
static public final DateProp SECOND = new DateProp("second in minute"); |
static public final DateProp MICROSECOND = new DateProp("microseconds (000000-999999)"); |
static public final Set<DateProp> ALL_INSTANCES = Collections |
.unmodifiableSet(new HashSet<>(Arrays.asList(YEAR, MONTH_NAME, MONTH_NUMBER, DAY_IN_MONTH, DAY_NAME_IN_WEEK, HOUR, MINUTE, SECOND, MICROSECOND))); |
static public final Set<DateProp> LOCALE_SENSITIVE_INSTANCES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(MONTH_NAME, DAY_NAME_IN_WEEK))); |
static public final DateTimeSkeleton TIME_SKELETON = DateTimeFormatBuilder.buildSkeleton(HOUR, MINUTE, SECOND); |
static public final DateTimeSkeleton SHORT_DATE_SKELETON = DateTimeFormatBuilder.buildSkeleton(DAY_IN_MONTH, MONTH_NUMBER, YEAR); |
static public final DateTimeSkeleton LONG_DATE_SKELETON = DateTimeFormatBuilder.buildSkeleton(DAY_NAME_IN_WEEK, DAY_IN_MONTH, MONTH_NAME, YEAR); |
static public final DateTimeSkeleton SHORT_DATETIME_SKELETON = DateTimeFormatBuilder.buildSkeleton(DAY_IN_MONTH, MONTH_NUMBER, YEAR, HOUR, MINUTE); |
static public final DateTimeSkeleton LONG_DATETIME_SKELETON = DateTimeFormatBuilder.buildSkeleton(DAY_NAME_IN_WEEK, DAY_IN_MONTH, MONTH_NAME, YEAR, HOUR, MINUTE, SECOND); |
// pure format (i.e. no literal string) |
static private final Map<DateProp, String> JAVA_DATE_SPECS_PURE; |
static public final Map<DateProp, String> DATE_PROP_TO_JAVA; |
static private final SortedMap<String, DateProp> REVERSE_JAVA_SPEC; |
static private final Pattern REVERSE_SPEC_PATTERN; |
static { |
JAVA_DATE_SPECS_PURE = new HashMap<DateProp, String>(); |
JAVA_DATE_SPECS_PURE.put(YEAR, "yyyy"); |
JAVA_DATE_SPECS_PURE.put(MONTH_NAME, "MMMM"); |
JAVA_DATE_SPECS_PURE.put(MONTH_NUMBER, "MM"); |
JAVA_DATE_SPECS_PURE.put(DAY_IN_MONTH, "dd"); |
JAVA_DATE_SPECS_PURE.put(DAY_NAME_IN_WEEK, "EEEE"); |
JAVA_DATE_SPECS_PURE.put(HOUR, "HH"); |
JAVA_DATE_SPECS_PURE.put(MINUTE, "mm"); |
JAVA_DATE_SPECS_PURE.put(SECOND, "ss"); |
final Map<DateProp, String> tmp = new HashMap<>(DateProp.JAVA_DATE_SPECS_PURE); |
tmp.put(DateProp.MICROSECOND, "SSS000"); |
DATE_PROP_TO_JAVA = Collections.unmodifiableMap(tmp); |
assert DATE_PROP_TO_JAVA.keySet().equals(ALL_INSTANCES); |
// reverse, so longer strings come first (e.g. MMMM|MM to match the longer one) |
final SortedMap<String, DateProp> m = new TreeMap<>(Collections.reverseOrder()); |
REVERSE_JAVA_SPEC = CollectionUtils.invertMap(m, JAVA_DATE_SPECS_PURE); |
assert REVERSE_JAVA_SPEC.size() == JAVA_DATE_SPECS_PURE.size() : "Duplicate values"; |
assert !JAVA_DATE_SPECS_PURE.containsKey(null) : "Null spec"; |
assert !JAVA_DATE_SPECS_PURE.containsValue(null) : "Null value"; |
REVERSE_SPEC_PATTERN = Pattern.compile(CollectionUtils.join(REVERSE_JAVA_SPEC.keySet(), "|")); |
} |
/** |
* Convert the passed pattern to a {@link SimpleDateFormat} pattern. |
* |
* @param simpleFormat either {@link #ALL_INSTANCES} or arbitrary literal string that will be |
* quoted. |
* @return the {@link SimpleDateFormat java} pattern. |
*/ |
public static String toJavaPattern(final List<? extends DateTimeFormatComponent> simpleFormat) { |
return toStringPattern(simpleFormat, DATE_PROP_TO_JAVA::get, StringUtils::singleQuote); |
} |
public static final String toStringPattern(final List<? extends DateTimeFormatComponent> simpleFormat, final Function<? super DateProp, String> datePropToString, |
final Function<String, String> literalToString) { |
final StringBuilder sb = new StringBuilder(simpleFormat.size() * 6); |
// needed because if there's 2 consecutive literal text then 'text1''text2' which evaluates |
// to text1'text2. |
final StringBuilder literalText = new StringBuilder(64); |
for (final DateTimeFormatComponent p : simpleFormat) { |
if (p instanceof Literal) { |
literalText.append(((Literal) p).getValue()); |
} else { |
final String javaComp = Objects.requireNonNull(datePropToString.apply((DateProp) p)); |
if (literalText.length() > 0) { |
sb.append(literalToString.apply(literalText.toString())); |
literalText.setLength(0); |
} |
sb.append(javaComp); |
} |
} |
if (literalText.length() > 0) { |
sb.append(literalToString.apply(literalText.toString())); |
} |
return sb.toString(); |
} |
public static DateTimeFormatterBuilder createJavaFormatterBuilder(final List<? extends DateTimeFormatComponent> simpleFormat) { |
final DateTimeFormatterBuilder res = new DateTimeFormatterBuilder(); |
for (final DateTimeFormatComponent p : simpleFormat) { |
if (p instanceof Literal) { |
res.appendLiteral(((Literal) p).getValue()); |
} else { |
// from DateTimeFormatterBuilder.appendPattern() |
if (p == YEAR) |
res.appendValue(ChronoField.YEAR, 4, 19, SignStyle.EXCEEDS_PAD); |
else if (p == MONTH_NAME) |
res.appendText(ChronoField.MONTH_OF_YEAR, TextStyle.FULL); |
else if (p == MONTH_NUMBER) |
res.appendValue(ChronoField.MONTH_OF_YEAR, 2); |
else if (p == DAY_IN_MONTH) |
res.appendValue(ChronoField.DAY_OF_MONTH, 2); |
else if (p == DAY_NAME_IN_WEEK) |
res.appendText(ChronoField.DAY_OF_WEEK, TextStyle.FULL); |
else if (p == HOUR) |
res.appendValue(ChronoField.HOUR_OF_DAY, 2); |
else if (p == MINUTE) |
res.appendValue(ChronoField.MINUTE_OF_HOUR, 2); |
else if (p == SECOND) |
res.appendValue(ChronoField.SECOND_OF_MINUTE, 2); |
else if (p == MICROSECOND) |
res.appendValue(ChronoField.MICRO_OF_SECOND, 6); |
else |
throw new IllegalArgumentException("Unknown " + p); |
} |
} |
return res; |
} |
/** |
* Return the best pattern matching the input skeleton. |
* |
* @param simpleFormat the fields needed, e.g. [YEAR, DAY_IN_MONTH, MONTH_NUMBER]. |
* @param l the locale needed. |
* @return the best match, e.g. "dd/MM/yyyy" for {@link Locale#FRANCE} , "MM/dd/yyyy" for |
* {@link Locale#US}. |
*/ |
public static String getBestJavaPattern(final List<DateProp> simpleFormat, final Locale l) { |
final StringBuilder sb = new StringBuilder(128); |
for (final DateProp p : simpleFormat) { |
final String javaComp = JAVA_DATE_SPECS_PURE.get(p); |
if (javaComp != null) |
sb.append(javaComp); |
else |
throw new IllegalArgumentException("Unsupported spec : " + p); |
} |
// needs same length so our pattern works |
return DateTimePatternGenerator.getInstance(l).getBestPattern(sb.toString(), DateTimePatternGenerator.MATCH_ALL_FIELDS_LENGTH); |
} |
/** |
* Return the best pattern matching the input skeleton. |
* |
* @param simpleFormat the fields needed, e.g. [YEAR, DAY_IN_MONTH, MONTH_NUMBER]. |
* @param l the locale needed. |
* @return the best match, e.g. [DAY_IN_MONTH, "/", MONTH_NUMBER, "/", YEAR] for |
* {@link Locale#FRANCE} , [MONTH_NUMBER, "/", DAY_IN_MONTH, "/", YEAR] for |
* {@link Locale#US}. |
*/ |
public static DateTimeFormat getBestPattern(final List<DateProp> simpleFormat, final Locale l) { |
return parseJavaPattern(getBestJavaPattern(simpleFormat, l)); |
} |
static DateTimeFormat parseJavaPattern(final String bestPattern) { |
final Matcher matcher = REVERSE_SPEC_PATTERN.matcher(bestPattern); |
final Matcher quotedMatcher = StringUtils.SINGLE_QUOTED_PATTERN.matcher(bestPattern); |
final DateTimeFormatBuilder res = new DateTimeFormatBuilder(); |
int index = 0; |
while (index < bestPattern.length()) { |
final int quoteIndex = bestPattern.indexOf('\'', index); |
final int endSansQuote = quoteIndex < 0 ? bestPattern.length() : quoteIndex; |
// parse quote-free string |
matcher.region(index, endSansQuote); |
while (matcher.find()) { |
if (index < matcher.start()) |
res.addLiteral(bestPattern.substring(index, matcher.start())); |
res.add(REVERSE_JAVA_SPEC.get(matcher.group())); |
index = matcher.end(); |
} |
assert index <= endSansQuote : "region() failed"; |
if (index < endSansQuote) |
res.addLiteral(bestPattern.substring(index, endSansQuote)); |
index = endSansQuote; |
// parse quoted string |
if (index < bestPattern.length()) { |
quotedMatcher.region(index, bestPattern.length()); |
if (!quotedMatcher.find() || quotedMatcher.start() != quotedMatcher.regionStart()) |
throw new IllegalStateException("Quoted string error : " + bestPattern.substring(quoteIndex)); |
res.addLiteral(StringUtils.unSingleQuote(quotedMatcher)); |
index = quotedMatcher.end(); |
} |
} |
return res.build(); |
} |
private final String name; |
private DateProp(String name) { |
this.name = name; |
} |
public final String getName() { |
return this.name; |
} |
@Override |
public String toString() { |
return this.getClass().getSimpleName() + ' ' + this.getName(); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/text/BooleanFormat.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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,9 → 18,13 |
import java.text.FieldPosition; |
import java.text.Format; |
import java.text.ParsePosition; |
import java.util.Arrays; |
import java.util.Locale; |
import java.util.ResourceBundle; |
import net.jcip.annotations.Immutable; |
@Immutable |
public class BooleanFormat extends Format { |
static private final boolean[] BOOLEAN_VALUES = new boolean[] { false, true }; |
static private final BooleanFormat NUMBER_INSTANCE = new BooleanFormat("0", "1"); |
39,10 → 43,18 |
return new BooleanFormat(getFormattedBooleans(l, true)); |
} |
static public BooleanFormat createTrueFalse(final Locale l) { |
return new BooleanFormat(getFormattedBooleans(l, false)); |
} |
static public final BooleanFormat getNumberInstance() { |
return NUMBER_INSTANCE; |
} |
static public final BooleanFormat[] getAll(final Locale l) { |
return new BooleanFormat[] { createTrueFalse(l), createYesNo(l), getNumberInstance() }; |
} |
private final String[] formattedValues; |
public BooleanFormat() { |
53,6 → 65,7 |
this(new String[] { falseValue, trueValue }); |
} |
// not public because parameter isn't copied |
private BooleanFormat(final String[] formattedValues) { |
this.formattedValues = formattedValues; |
} |
88,4 → 101,29 |
pos.setErrorIndex(pos.getIndex()); |
return null; |
} |
@Override |
public int hashCode() { |
final int prime = 31; |
int result = 1; |
result = prime * result + Arrays.hashCode(this.formattedValues); |
return result; |
} |
@Override |
public boolean equals(Object obj) { |
if (this == obj) |
return true; |
if (obj == null) |
return false; |
if (getClass() != obj.getClass()) |
return false; |
final BooleanFormat other = (BooleanFormat) obj; |
return Arrays.equals(this.formattedValues, other.formattedValues); |
} |
@Override |
public String toString() { |
return this.getClass().getSimpleName() + " with " + Arrays.asList(this.formattedValues); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/text/DateTimeFormatComponent.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.text; |
public abstract class DateTimeFormatComponent { |
DateTimeFormatComponent() { |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/text/TypesFormats.java |
---|
New file |
0,0 → 1,51 |
/* |
* 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.text; |
import org.openconcerto.utils.CollectionUtils; |
import org.openconcerto.utils.i18n.I18nUtils; |
import java.util.List; |
import net.jcip.annotations.Immutable; |
@Immutable |
public final class TypesFormats { |
private final boolean yesNoBoolean; |
private final List<DateTimeSkeleton> dateSkeletons, timeSkeletons, datetimeSkeletons; |
public TypesFormats(boolean yesNoBoolean, List<DateTimeSkeleton> dateSkeletons, List<DateTimeSkeleton> timeSkeletons, List<DateTimeSkeleton> datetimeSkeletons) { |
super(); |
this.yesNoBoolean = yesNoBoolean; |
this.dateSkeletons = CollectionUtils.toImmutableList(dateSkeletons); |
this.timeSkeletons = CollectionUtils.toImmutableList(timeSkeletons); |
this.datetimeSkeletons = CollectionUtils.toImmutableList(datetimeSkeletons); |
} |
public final String getBooleanKey(final boolean b) { |
return this.yesNoBoolean ? I18nUtils.getYesNoKey(b) : I18nUtils.getBooleanKey(b); |
} |
public final List<DateTimeSkeleton> getDateSkeletons() { |
return this.dateSkeletons; |
} |
public final List<DateTimeSkeleton> getTimeSkeletons() { |
return this.timeSkeletons; |
} |
public final List<DateTimeSkeleton> getDatetimeSkeletons() { |
return this.datetimeSkeletons; |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/text/DateTimeSkeleton.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.text; |
import java.text.SimpleDateFormat; |
import java.time.format.DateTimeFormatter; |
import java.util.List; |
import java.util.Locale; |
import net.jcip.annotations.Immutable; |
@Immutable |
public final class DateTimeSkeleton extends DateTimeComponents { |
private final List<DateProp> components; |
DateTimeSkeleton(List<DateProp> components) { |
super(); |
this.components = components; |
} |
@Override |
public final List<DateProp> getComponents() { |
return this.components; |
} |
public final String getBestJavaPattern(Locale l) { |
return DateProp.getBestJavaPattern(this.getComponents(), l); |
} |
public final SimpleDateFormat createBestJavaFormat(Locale l) { |
return new SimpleDateFormat(this.getBestJavaPattern(l), l); |
} |
public final DateTimeFormat getBestPattern(Locale l) { |
return DateProp.getBestPattern(this.getComponents(), l); |
} |
public final DateTimeFormatter createBestJavaFormatter(Locale l) { |
return this.getBestPattern(l).createJavaFormatter(l); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/text/DateTimeComponents.java |
---|
New file |
0,0 → 1,49 |
/* |
* 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.text; |
import java.text.SimpleDateFormat; |
import java.util.List; |
import java.util.Objects; |
abstract class DateTimeComponents { |
public abstract List<? extends DateTimeFormatComponent> getComponents(); |
/** |
* Convert this pattern to a {@link SimpleDateFormat} pattern. |
* |
* @return the {@link SimpleDateFormat java} pattern. |
*/ |
public final String toJavaPattern() { |
return DateProp.toJavaPattern(this.getComponents()); |
} |
@Override |
public int hashCode() { |
return Objects.hash(this.getComponents()); |
} |
@Override |
public boolean equals(Object obj) { |
if (this == obj) |
return true; |
if (obj == null) |
return false; |
if (getClass() != obj.getClass()) |
return false; |
final DateTimeComponents other = (DateTimeComponents) obj; |
return this.getComponents().equals(other.getComponents()); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/text/DateTimeFormat.java |
---|
New file |
0,0 → 1,83 |
/* |
* 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.text; |
import org.openconcerto.utils.CollectionUtils; |
import java.time.format.DateTimeFormatter; |
import java.time.format.DateTimeFormatterBuilder; |
import java.util.List; |
import java.util.Locale; |
import java.util.Objects; |
import net.jcip.annotations.Immutable; |
@Immutable |
public final class DateTimeFormat extends DateTimeComponents { |
public static final class Literal extends DateTimeFormatComponent { |
private final String value; |
Literal(String value) { |
super(); |
this.value = Objects.requireNonNull(value); |
} |
public final String getValue() { |
return this.value; |
} |
@Override |
public int hashCode() { |
return Objects.hash(this.value); |
} |
@Override |
public boolean equals(Object obj) { |
if (this == obj) |
return true; |
if (obj == null) |
return false; |
if (getClass() != obj.getClass()) |
return false; |
Literal other = (Literal) obj; |
return this.value.equals(other.value); |
} |
@Override |
public String toString() { |
return this.getClass().getSimpleName() + ' ' + this.getValue(); |
} |
} |
private final List<DateTimeFormatComponent> components; |
DateTimeFormat(List<? extends DateTimeFormatComponent> components) { |
super(); |
this.components = CollectionUtils.toImmutableList(Objects.requireNonNull(components)); |
} |
@Override |
public final List<DateTimeFormatComponent> getComponents() { |
return this.components; |
} |
public final DateTimeFormatterBuilder createJavaFormatterBuilder() { |
return DateProp.createJavaFormatterBuilder(this.getComponents()); |
} |
public final DateTimeFormatter createJavaFormatter(final Locale locale) { |
return this.createJavaFormatterBuilder().toFormatter(locale); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/change/ListChangeRecorder.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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,7 → 21,7 |
/** |
* A class that wraps a list, to detect every change made to it. The changes are available with |
* {@link #getRecipe()}. |
* {@link #getRecipe()}. Thread-safe if the delegate is itself thread-safe and not keeping history. |
* |
* @author Sylvain |
* |
32,11 → 32,11 |
private final List<E> delegate; |
private final ListChangeRecipe<E> recipe; |
public ListChangeRecorder(List<E> delegate) { |
public ListChangeRecorder(final List<E> delegate) { |
this(delegate, false); |
} |
public ListChangeRecorder(List<E> delegate, final boolean keepHistory) { |
public ListChangeRecorder(final List<E> delegate, final boolean keepHistory) { |
super(); |
this.delegate = delegate; |
this.recipe = new ListChangeRecipe<E>(keepHistory); |
52,47 → 52,58 |
// ** read only |
public E get(int index) { |
@Override |
public E get(final int index) { |
return this.delegate.get(index); |
} |
@Override |
public int size() { |
return this.delegate.size(); |
} |
@Override |
public Object[] toArray() { |
return this.delegate.toArray(); |
} |
public <T> T[] toArray(T[] a) { |
@Override |
public <T> T[] toArray(final T[] a) { |
return this.delegate.toArray(a); |
} |
public boolean contains(Object o) { |
@Override |
public boolean contains(final Object o) { |
return this.delegate.contains(o); |
} |
public boolean containsAll(Collection<?> c) { |
@Override |
public boolean containsAll(final Collection<?> c) { |
return this.delegate.containsAll(c); |
} |
public boolean equals(Object o) { |
@Override |
public boolean equals(final Object o) { |
return this.delegate.equals(o); |
} |
@Override |
public int hashCode() { |
return this.delegate.hashCode(); |
} |
public int indexOf(Object o) { |
@Override |
public int indexOf(final Object o) { |
return this.delegate.indexOf(o); |
} |
@Override |
public boolean isEmpty() { |
return this.delegate.isEmpty(); |
} |
public int lastIndexOf(Object o) { |
@Override |
public int lastIndexOf(final Object o) { |
return this.delegate.lastIndexOf(o); |
} |
99,7 → 110,8 |
// ** write, always change the delegate before notifying the recipe |
// otherwise the listeners will be told of the changes before they even happened |
public boolean add(E e) { |
@Override |
public synchronized boolean add(final E e) { |
final boolean res = this.delegate.add(e); |
// -1 since this just grew by one |
this.recipe.add(this.size() - 1, Collections.singleton(e)); |
106,12 → 118,14 |
return res; |
} |
public void add(int index, E e) { |
@Override |
public synchronized void add(final int index, final E e) { |
this.delegate.add(index, e); |
this.recipe.add(index, Collections.singleton(e)); |
} |
public boolean addAll(Collection<? extends E> c) { |
@Override |
public synchronized boolean addAll(final Collection<? extends E> c) { |
final int size = this.size(); |
final boolean res = this.delegate.addAll(c); |
this.recipe.add(size, c); |
118,25 → 132,29 |
return res; |
} |
public boolean addAll(int index, Collection<? extends E> c) { |
@Override |
public synchronized boolean addAll(final int index, final Collection<? extends E> c) { |
final boolean res = this.delegate.addAll(index, c); |
this.recipe.add(index, c); |
return res; |
} |
public void clear() { |
@Override |
public synchronized void clear() { |
final List<E> copy = (List<E>) ListChangeIndex.copy(this); |
this.delegate.clear(); |
this.recipe.remove(0, copy.size() - 1, copy); |
} |
public E set(int index, E element) { |
@Override |
public synchronized E set(final int index, final E element) { |
final E res = this.delegate.set(index, element); |
this.recipe.set(index, res, element); |
return res; |
} |
public E remove(int index) { |
@Override |
public synchronized E remove(final int index) { |
final E res = this.delegate.remove(index); |
this.recipe.remove(index, index, Collections.singletonList(res)); |
return res; |
144,7 → 162,8 |
// objects |
public boolean remove(Object o) { |
@Override |
public synchronized boolean remove(final Object o) { |
final int index = this.indexOf(o); |
if (index < 0) |
return false; |
154,17 → 173,19 |
} |
} |
public boolean removeAll(Collection<?> c) { |
@Override |
public boolean removeAll(final Collection<?> c) { |
return this.changeAll(c, true); |
} |
public boolean retainAll(Collection<?> c) { |
@Override |
public boolean retainAll(final Collection<?> c) { |
return this.changeAll(c, false); |
} |
private boolean changeAll(Collection<?> c, boolean remove) { |
private synchronized boolean changeAll(final Collection<?> c, final boolean remove) { |
boolean modified = false; |
Iterator<?> e = iterator(); |
final Iterator<?> e = iterator(); |
while (e.hasNext()) { |
if (c.contains(e.next()) == remove) { |
e.remove(); |
/trunk/OpenConcerto/src/org/openconcerto/utils/change/ListChangeIndex.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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,12 → 23,12 |
public abstract class ListChangeIndex<T> implements ListChange<T> { |
protected final static <T> Collection<T> copy(Collection<T> col) { |
protected final static <T> Collection<T> copy(final Collection<T> col) { |
Collection<T> res; |
try { |
// tries to keep the same class |
res = CopyUtils.copy(col); |
} catch (RuntimeException e) { |
} catch (final RuntimeException e) { |
// but this doesn't always work (see sublist()) |
// so just use a plain ArrayList |
res = new ArrayList<T>(col); |
39,7 → 39,7 |
private final int index0; |
private final int index1; |
public ListChangeIndex(int index0, int index1) { |
public ListChangeIndex(final int index0, final int index1) { |
super(); |
this.index0 = index0; |
this.index1 = index1; |
61,7 → 61,7 |
private final List<T> removed; |
public Rm(int index0, int index1, final List<T> removed) { |
public Rm(final int index0, final int index1, final List<T> removed) { |
super(index0, index1); |
// ok to cast : either it copies it : List<T>, either it uses an arrayList which |
// implements List<T> |
68,15 → 68,18 |
this.removed = (List<T>) copy(removed); |
} |
public <U> void apply(List<U> l, ITransformer<T, U> transf) { |
@Override |
public <U> void apply(final List<U> l, final ITransformer<T, U> transf) { |
// sublist exclusive |
l.subList(this.getIndex0(), this.getIndex1() + 1).clear(); |
} |
@Override |
public List<? extends T> getItemsAdded() { |
return Collections.emptyList(); |
} |
@Override |
public List<T> getItemsRemoved() { |
return this.removed; |
} |
91,12 → 94,13 |
private final Collection<? extends T> added; |
public Add(int index0, Collection<? extends T> added) { |
public Add(final int index0, final Collection<? extends T> added) { |
super(index0, index0); |
this.added = copy(added); |
} |
public <U> void apply(List<U> l, ITransformer<T, U> transf) { |
@Override |
public <U> void apply(final List<U> l, final ITransformer<T, U> transf) { |
final List<U> toAdd = new ArrayList<U>(); |
for (final T t : this.added) { |
toAdd.add(transf.transformChecked(t)); |
104,10 → 108,12 |
l.addAll(this.getIndex0(), toAdd); |
} |
@Override |
public Collection<? extends T> getItemsAdded() { |
return this.added; |
} |
@Override |
public List<? extends T> getItemsRemoved() { |
return Collections.emptyList(); |
} |
123,20 → 129,23 |
private final T removed; |
private final T added; |
public Set(int index, T removed, T added) { |
public Set(final int index, final T removed, final T added) { |
super(index, index); |
this.removed = removed; |
this.added = added; |
} |
public <U> void apply(List<U> l, ITransformer<T, U> transf) { |
@Override |
public <U> void apply(final List<U> l, final ITransformer<T, U> transf) { |
l.set(this.getIndex0(), transf.transformChecked(this.added)); |
} |
@Override |
public List<? extends T> getItemsAdded() { |
return Collections.singletonList(this.added); |
} |
@Override |
public List<? extends T> getItemsRemoved() { |
return Collections.singletonList(this.removed); |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/change/ListChangeRecipe.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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,7 → 22,10 |
import java.util.IdentityHashMap; |
import java.util.List; |
import java.util.Map; |
import java.util.concurrent.CopyOnWriteArrayList; |
import net.jcip.annotations.GuardedBy; |
/** |
* Allow to propagate ListChange to listeners and bound lists. Can also store all changes that it |
* was notified and replay them with {@link #apply(List, ITransformer)}. |
39,10 → 42,12 |
// don't use PropertyChangeSupport since it isn't type safe and we're only interested in the |
// last change (and not the whole property, i.e. the whole list) |
private final List<IClosure<? super ListChangeIndex<T>>> listeners; |
@GuardedBy("this") |
private final Map<List<?>, Pair<?>> boundLists; |
/** |
* Create a new instance. Recording is only necessary for {@link #apply(List, ITransformer)}. |
* Thread-safe only if not recording. |
* |
* @param record <code>true</code> if all changes should be kept (this will leak memory until |
* {@link #clear()} is called). |
50,7 → 55,7 |
public ListChangeRecipe(final boolean record) { |
super(); |
this.changes = record ? new ArrayList<ListChangeIndex<T>>() : null; |
this.listeners = new ArrayList<IClosure<? super ListChangeIndex<T>>>(); |
this.listeners = new CopyOnWriteArrayList<>(); |
// need IdentityHashMap since List.equals() depend on its items |
// which will change |
this.boundLists = new IdentityHashMap<List<?>, Pair<?>>(); |
60,6 → 65,11 |
return this.changes != null; |
} |
/** |
* The list of changes since the last {@link #clear()}. Not thread-safe. |
* |
* @return the list of changes |
*/ |
public final List<ListChangeIndex<T>> getChanges() { |
if (!this.recordChanges()) |
throw new IllegalStateException("This instance wasn't created to record changes"); |
66,15 → 76,15 |
return this.changes; |
} |
public void addListener(IClosure<? super ListChangeIndex<T>> l) { |
public void addListener(final IClosure<? super ListChangeIndex<T>> l) { |
this.listeners.add(l); |
} |
public void rmListener(IClosure<? super ListChangeIndex<T>> l) { |
public void rmListener(final IClosure<? super ListChangeIndex<T>> l) { |
this.listeners.remove(l); |
} |
public void bind(List<T> l) { |
public void bind(final List<T> l) { |
this.bind(l, Transformer.<T> nopTransformer()); |
} |
85,18 → 95,19 |
* @param l the list to keep in sync. |
* @param transf the transformer. |
*/ |
public <U> void bind(List<U> l, ITransformer<T, U> transf) { |
public synchronized <U> void bind(final List<U> l, final ITransformer<T, U> transf) { |
this.boundLists.put(l, new Pair<U>(l, transf)); |
} |
public <U> void unbind(List<U> l) { |
public synchronized <U> void unbind(final List<U> l) { |
this.boundLists.remove(l); |
} |
private final void add(ListChangeIndex<T> change) { |
private final void add(final ListChangeIndex<T> change) { |
if (this.recordChanges()) |
this.changes.add(change); |
// must change bounded lists first, otherwise listeners couldn't access them |
synchronized (this) { |
for (final Pair<?> p : this.boundLists.values()) { |
p.apply(change); |
} |
103,16 → 114,17 |
for (final IClosure<? super ListChangeIndex<T>> l : this.listeners) |
l.executeChecked(change); |
} |
} |
public void add(int index0, Collection<? extends T> c) { |
public void add(final int index0, final Collection<? extends T> c) { |
this.add(new ListChangeIndex.Add<T>(index0, c)); |
} |
public void remove(int index0, int index1, List<T> removed) { |
public void remove(final int index0, final int index1, final List<T> removed) { |
this.add(new ListChangeIndex.Rm<T>(index0, index1, removed)); |
} |
public void set(int index0, T old, T newItem) { |
public void set(final int index0, final T old, final T newItem) { |
this.add(new ListChangeIndex.Set<T>(index0, old, newItem)); |
} |
134,7 → 146,7 |
* changes}. |
*/ |
@Override |
public <U> void apply(List<U> l, ITransformer<T, U> transf) throws IllegalStateException { |
public <U> void apply(final List<U> l, final ITransformer<T, U> transf) throws IllegalStateException { |
for (final ListChange<T> change : this.getChanges()) { |
change.apply(l, transf); |
} |
145,13 → 157,13 |
private final List<U> l; |
private final ITransformer<T, U> transf; |
public Pair(List<U> l, ITransformer<T, U> transf) { |
public Pair(final List<U> l, final ITransformer<T, U> transf) { |
super(); |
this.l = l; |
this.transf = transf; |
} |
void apply(ListChange<T> change) { |
void apply(final ListChange<T> change) { |
change.apply(this.l, this.transf); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/ListMap.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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> ListMapItf<K, V> unmodifiableMap(ListMapItf<K, V> map) { |
if (map.isEmpty()) |
return empty(); |
return new Unmodifiable<K, V>(map); |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/ReentrantEventDispatcher.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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,12 |
/** |
* Allow to maintain the dispatching of events in order when a listener itself fires an event. |
* <p> |
* If each notification simply goes through a list of listeners, the events are delivered out of |
* order : <img src="doc-files/reentrantEventsNaive.png" /> |
* <p> |
* This class adds new notifications at the end of a list, but resume notifying from the start of |
* the list. <img src="doc-files/reentrantEventsIn-order.png" /> |
* |
* @author sylvain |
* |
30,12 → 36,30 |
*/ |
public final class ReentrantEventDispatcher<L, E, X extends Exception> { |
private final class DispatchingState extends Tuple3<Iterator<L>, BiConsumerExn<L, E, X>, E> { |
public DispatchingState(final Iterator<L> iter, BiConsumerExn<L, E, X> callback, final E evt) { |
super(Objects.requireNonNull(iter, "Missing iterator"), Objects.requireNonNull(callback, "Missing callback"), evt); |
public final class DispatchingState { |
private final Iterator<L> iter; |
private final BiConsumerExn<L, E, X> callback; |
private final E evt; |
private DispatchingState(final Iterator<L> iter, BiConsumerExn<L, E, X> callback, final E evt) { |
this.iter = Objects.requireNonNull(iter, "Missing iterator"); |
this.callback = Objects.requireNonNull(callback, "Missing callback"); |
this.evt = evt; |
} |
private final Iterator<L> getIterator() { |
return this.iter; |
} |
private final BiConsumerExn<L, E, X> getCallback() { |
return this.callback; |
} |
public final E getEvt() { |
return this.evt; |
} |
} |
private final ThreadLocal<LinkedList<DispatchingState>> events = new ThreadLocal<LinkedList<DispatchingState>>() { |
@Override |
protected LinkedList<DispatchingState> initialValue() { |
55,14 → 79,22 |
} |
public final void fire(final Iterator<L> iter, final E evt) throws X { |
this.fire(iter, this.callback, evt); |
this.fire(this.createDispatchingState(iter, evt)); |
} |
public final void fire(final Iterator<L> iter, final BiConsumerExn<L, E, X> callback, final E evt) throws X { |
this.fire(new DispatchingState(iter, callback, evt)); |
this.fire(this.createDispatchingState(iter, callback, evt)); |
} |
private final void fire(final DispatchingState newTuple) throws X { |
public final DispatchingState createDispatchingState(final Iterator<L> iter, final E evt) { |
return this.createDispatchingState(iter, this.callback, evt); |
} |
public final DispatchingState createDispatchingState(final Iterator<L> iter, final BiConsumerExn<L, E, X> callback, final E evt) { |
return new DispatchingState(iter, callback, evt); |
} |
public final void fire(final DispatchingState newTuple) throws X { |
final LinkedList<DispatchingState> linkedList = this.events.get(); |
// add new event |
linkedList.addLast(newTuple); |
69,16 → 101,34 |
// process all pending events |
DispatchingState currentTuple; |
while ((currentTuple = linkedList.peekFirst()) != null) { |
final Iterator<L> currentIter = currentTuple.get0(); |
final BiConsumerExn<L, E, X> currentCallback = currentTuple.get1(); |
final E currentEvt = currentTuple.get2(); |
final Iterator<L> currentIter = currentTuple.getIterator(); |
final BiConsumerExn<L, E, X> currentCallback = currentTuple.getCallback(); |
final E currentEvt = currentTuple.getEvt(); |
while (currentIter.hasNext()) { |
final L l = currentIter.next(); |
currentCallback.accept(l, currentEvt); |
} |
/* |
* It isn't because one callback failed that the event itself didn't happen or should be |
* reverted : we should still notify the other callbacks. So don't use a finally block |
* to remove currentTuple. But if the event should indeed be reverted (e.g. the callback |
* was a check), #remove(DispatchingState) should be called. |
*/ |
// not removeFirst() since the item might have been already removed |
linkedList.pollFirst(); |
} |
} |
/** |
* Remove a dispatching state. Useful because if there's an exception while notifying the |
* listeners, then the next {@link #fire(DispatchingState)} in the same thread will by default |
* resume notifying the listeners. That behaviour is not valid for a reverted DB transaction. |
* |
* @param dispatchingState what to remove. |
* @return {@code true} if the item was actually removed. |
* @see #createDispatchingState(Iterator, Object) |
*/ |
public final boolean remove(DispatchingState dispatchingState) { |
return this.events.get().remove(dispatchingState); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/doml/DOMLStreamReader.java |
---|
New file |
0,0 → 1,138 |
/* |
* 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.doml; |
import java.io.BufferedReader; |
import java.io.IOException; |
public class DOMLStreamReader { |
public static final int EOF = 0; |
public static final int TAG_START = 1; |
public static final int TAG_END = 2; |
public static final int TAG_END_COMPACT = 3; |
public static final int NO_TAG_CHARACTER = 4; |
private final PushBackReader br; |
final StringBuilder bElementName = new StringBuilder(2048); |
public DOMLStreamReader(BufferedReader r) { |
this.br = new PushBackReader(r, 2); |
} |
public final int readNextToken() throws IOException { |
char c = (char) this.br.read(); |
while (c > 0) { |
if (c == ' ' || c == '\t' || c == '\n') { |
c = (char) this.br.read(); |
} else { |
if (c == '<') { |
return TAG_START; |
} else if (c == '>') { |
return TAG_END; |
} else if (c == '/') { |
c = (char) this.br.read(); |
if (c == '>') { |
return TAG_END_COMPACT; |
} else { |
unread(c); |
} |
} else { |
this.br.unread(c); |
return NO_TAG_CHARACTER; |
} |
} |
} |
return EOF; |
} |
public final String readTagName() throws IOException { |
this.bElementName.setLength(0); |
char c = (char) this.br.read(); |
while (c > 0) { |
if (c == '>') { |
this.br.unread('>'); |
return this.bElementName.toString(); |
} else if (c == ' ' || c == '\t') { |
return this.bElementName.toString(); |
} else { |
this.bElementName.append(c); |
} |
c = (char) this.br.read(); |
} |
return this.bElementName.toString(); |
} |
public final void unread(char c) throws IOException { |
this.br.unread(c); |
} |
public final char readNextChar() throws IOException { |
return (char) this.br.read(); |
} |
public final String readAttributeName() throws IOException { |
this.bElementName.setLength(0); |
char c = (char) this.br.read(); |
while (c > 0) { |
if (c == '>') { |
this.br.unread('>'); |
return this.bElementName.toString(); |
} else if (c == '=' || c == ' ' || c == '\t' || c == '\n') { |
return this.bElementName.toString(); |
} else { |
this.bElementName.append(c); |
} |
c = (char) this.br.read(); |
} |
return this.bElementName.toString(); |
} |
public final String readAttributeValue() throws IOException { |
this.bElementName.setLength(0); |
boolean open = false; |
char c = (char) this.br.read(); |
if (c != '\"') { |
throw new IllegalStateException("missing \""); |
} |
while (c > 0) { |
if (c == '\\') { |
c = (char) this.br.read(); |
if (c == '\\' || c == '"') { |
this.bElementName.append(c); |
} else if (c == 'r') { |
this.bElementName.append('\r'); |
} else if (c == 't') { |
this.bElementName.append('\t'); |
} else if (c == 'n') { |
this.bElementName.append('\n'); |
} else { |
throw new IllegalStateException("invalid escaped character :" + c); |
} |
} else if (c == '"') { |
if (open) { |
return this.bElementName.toString(); |
} |
open = true; |
} else if (c < 32) { |
continue; |
} else { |
this.bElementName.append(c); |
} |
c = (char) this.br.read(); |
} |
return this.bElementName.toString(); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/doml/Document.java |
---|
New file |
0,0 → 1,286 |
/* |
* 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.doml; |
import java.io.BufferedInputStream; |
import java.io.BufferedOutputStream; |
import java.io.BufferedReader; |
import java.io.BufferedWriter; |
import java.io.ByteArrayInputStream; |
import java.io.ByteArrayOutputStream; |
import java.io.File; |
import java.io.FileInputStream; |
import java.io.FileOutputStream; |
import java.io.IOException; |
import java.io.OutputStreamWriter; |
import java.nio.charset.StandardCharsets; |
public class Document { |
private Element root; |
private byte[] buffer = new byte[64]; |
public Document() { |
} |
public Document(String rootName) { |
this.root = new Element(rootName); |
} |
public Document(Element root) { |
this.root = root; |
} |
public void loadFrom(BufferedReader br) throws IOException { |
this.root = readElement(new DOMLStreamReader(br), 0); |
} |
private Element readElement(DOMLStreamReader r, int depth) throws IOException { |
int t = r.readNextToken(); |
if (t == DOMLStreamReader.EOF || t == DOMLStreamReader.NO_TAG_CHARACTER) { |
return null; |
} |
if (t != DOMLStreamReader.TAG_START) { |
throw new IOException("not element start found (" + t + ")"); |
} |
final String tagName = r.readTagName(); |
final Element current = new Element(tagName); |
t = r.readNextToken(); |
if (t == DOMLStreamReader.TAG_END_COMPACT) { |
return current; |
} else if (t == DOMLStreamReader.TAG_END) { |
readChildren(r, depth, tagName, current); |
return current; |
} else if (t == DOMLStreamReader.NO_TAG_CHARACTER) { |
while (t == DOMLStreamReader.NO_TAG_CHARACTER) { |
final String name = r.readAttributeName(); |
final String value = r.readAttributeValue(); |
current.setAttribute(name, value); |
t = r.readNextToken(); |
} |
if (t == DOMLStreamReader.TAG_END) { |
readChildren(r, depth, tagName, current); |
} |
return current; |
} |
return null; |
} |
private void readChildren(DOMLStreamReader r, int depth, String tagName, Element current) throws IOException { |
int t; |
boolean tryReadNextChild = false; |
do { |
char n1 = r.readNextChar(); |
while (n1 < 33) { |
// Optimization for : n1 == ' ' || n1 == '\n' || n1 == '\r' || n1 == '\t' |
n1 = r.readNextChar(); |
} |
if (n1 == 65535) { |
// EOF |
return; |
} |
tryReadNextChild = false; |
if (n1 == '<') { |
char n2 = r.readNextChar(); |
if (n2 != '/') { |
tryReadNextChild = true; |
r.unread(n2); |
r.unread(n1); |
Element e = readElement(r, depth + 1); |
if (e != null) { |
current.addChild(e); |
} |
} else { |
r.unread(n2); |
r.unread(n1); |
} |
} |
} while (tryReadNextChild); |
// end tag |
t = r.readNextToken(); |
if (t != DOMLStreamReader.TAG_START) { |
throw new IOException("not closing element start found for " + tagName + " (" + t + ") : '" + r.readNextChar()); |
} |
final String ctagName = r.readTagName(); |
t = r.readNextToken(); |
if (t != DOMLStreamReader.TAG_END) { |
throw new IOException("closing element end tag for '" + ctagName + "' is missing " + t); |
} |
} |
public void writeTo(BufferedWriter writer) throws IOException { |
if (this.root == null) |
return; |
writeTo(writer, 0, this.root); |
writer.flush(); |
} |
private Element readElement(BufferedInputStream in) throws IOException { |
final String name = readUTF(in); |
final int attributeCount = readInt(in); |
final int childrenCount = readInt(in); |
final Element e = new Element(name, attributeCount, childrenCount); |
for (int i = 0; i < attributeCount; i++) { |
final String n = readUTF(in); |
final String v = readUTF(in); |
e.addAttributeNoCheck(n, v); |
} |
for (int i = 0; i < childrenCount; i++) { |
e.addChildNoCheck(readElement(in)); |
} |
return e; |
} |
public void loadFromBinary(BufferedInputStream in) throws IOException { |
this.root = readElement(in); |
} |
public void loadFromBinary(File file) throws IOException { |
try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(file))) { |
this.root = readElement(in); |
} |
} |
public void loadFromBinary(byte[] bytes) throws IOException { |
try (BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(bytes))) { |
this.root = readElement(in); |
} |
} |
public void writeToBinary(BufferedOutputStream out) throws IOException { |
if (this.root == null) { |
return; |
} |
writeTo(out, this.root); |
out.flush(); |
} |
public void writeToBinary(File file) throws IOException { |
try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file))) { |
this.writeToBinary(out); |
} |
} |
private void writeTo(BufferedOutputStream out, Element e) throws IOException { |
// ObjectStream are not used because readUTF allocates too much char[] |
writeUTF(out, e.getName()); |
final int attributeCount = e.getAttributeCount(); |
writeInt(out, attributeCount); |
writeInt(out, e.getChildrenCount()); |
// write attributes |
if (attributeCount > 0) { |
e.writeAttributes(out); |
} |
// write children |
for (Element c : e.getChildren()) { |
writeTo(out, c); |
} |
} |
public byte[] toByteArray() throws IOException { |
if (this.root == null) { |
return new byte[0]; |
} |
final ByteArrayOutputStream out2 = new ByteArrayOutputStream(1024 + this.root.getChildrenCount() * 32); |
try (BufferedOutputStream out = new BufferedOutputStream(out2)) { |
this.writeToBinary(out); |
} |
return out2.toByteArray(); |
} |
private static final void writeInt(BufferedOutputStream out, int v) throws IOException { |
out.write((v >>> 24) & 0xFF); |
out.write((v >>> 16) & 0xFF); |
out.write((v >>> 8) & 0xFF); |
out.write((v) & 0xFF); |
} |
static final void writeUTF(BufferedOutputStream out, String str) throws IOException { |
final byte[] buf = str.getBytes(StandardCharsets.UTF_8); |
writeInt(out, buf.length); |
out.write(buf); |
} |
private final int readInt(BufferedInputStream in) throws IOException { |
final int ch1 = in.read(); |
final int ch2 = in.read(); |
final int ch3 = in.read(); |
final int ch4 = in.read(); |
return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + ch4); |
} |
private final String readUTF(BufferedInputStream in) throws IOException { |
final int size = readInt(in); |
if (this.buffer.length < size) { |
this.buffer = new byte[size + 32]; |
} |
final int s = in.read(this.buffer, 0, size); |
return new String(this.buffer, 0, s, StandardCharsets.UTF_8); |
} |
@Override |
public String toString() { |
final ByteArrayOutputStream out = new ByteArrayOutputStream(); |
try { |
writeTo(new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8))); |
return out.toString(StandardCharsets.UTF_8.name()); |
} catch (IOException e) { |
return e.getMessage(); |
} |
} |
private void writeTo(final BufferedWriter writer, int depth, Element e) throws IOException { |
if (e.getChildrenCount() == 0) { |
for (int i = 0; i < depth; i++) { |
writer.append(' '); |
} |
writer.append('<'); |
writer.append(e.getName()); |
e.appendAttributes(writer); |
writer.append(" />\n"); |
} else { |
for (int i = 0; i < depth; i++) { |
writer.append(' '); |
} |
writer.append('<'); |
writer.append(e.getName()); |
e.appendAttributes(writer); |
writer.append(">\n"); |
int d = depth + 1; |
for (Element child : e.getChildren()) { |
writeTo(writer, d, child); |
} |
for (int i = 0; i < depth; i++) { |
writer.append(' '); |
} |
writer.append("</"); |
writer.append(e.getName()); |
writer.append(">\n"); |
} |
} |
public Element getRoot() { |
return this.root; |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/doml/PushBackReader.java |
---|
New file |
0,0 → 1,130 |
/* |
* 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.doml; |
import java.io.IOException; |
import java.io.Reader; |
public class PushBackReader extends Reader { |
/** |
* The underlying character-input stream. |
*/ |
protected Reader in; |
/** Pushback buffer */ |
private char[] buf; |
/** Current position in buffer */ |
private int pos; |
/** |
* Creates a new pushback reader with a pushback buffer of the given size. |
* |
* @param in The reader from which characters will be read |
* @param size The size of the pushback buffer |
* @exception IllegalArgumentException if {@code size <= 0} |
*/ |
public PushBackReader(Reader in, int size) { |
this.in = in; |
if (size <= 0) { |
throw new IllegalArgumentException("size <= 0"); |
} |
this.buf = new char[size]; |
this.pos = size; |
} |
/** |
* Creates a new pushback reader with a one-character pushback buffer. |
* |
* @param in The reader from which characters will be read |
*/ |
public PushBackReader(Reader in) { |
this(in, 1); |
} |
/** |
* Reads a single character. |
* |
* @return The character read, or -1 if the end of the stream has been reached |
* |
* @exception IOException If an I/O error occurs |
*/ |
@Override |
public int read() throws IOException { |
if (pos < this.buf.length) |
return this.buf[pos++]; |
else |
return this.in.read(); |
} |
/** |
* Pushes back a single character by copying it to the front of the pushback buffer. After this |
* method returns, the next character to be read will have the value <code>(char)c</code>. |
* |
* @param c The int value representing a character to be pushed back |
* |
* @exception IOException If the pushback buffer is full, or if some other I/O error occurs |
*/ |
public void unread(int c) throws IOException { |
this.buf[--pos] = (char) c; |
} |
/** |
* Marks the present position in the stream. The <code>mark</code> for class |
* <code>PushbackReader</code> always throws an exception. |
* |
* @exception IOException Always, since mark is not supported |
*/ |
@Override |
public void mark(int readAheadLimit) throws IOException { |
throw new IOException("mark/reset not supported"); |
} |
/** |
* Resets the stream. The <code>reset</code> method of <code>PushbackReader</code> always throws |
* an exception. |
* |
* @exception IOException Always, since reset is not supported |
*/ |
@Override |
public void reset() throws IOException { |
throw new IOException("mark/reset not supported"); |
} |
/** |
* Tells whether this stream supports the mark() operation, which it does not. |
*/ |
@Override |
public boolean markSupported() { |
return false; |
} |
/** |
* Closes the stream and releases any system resources associated with it. Once the stream has |
* been closed, further read(), unread(), ready(), or skip() invocations will throw an |
* IOException. Closing a previously closed stream has no effect. |
* |
* @exception IOException If an I/O error occurs |
*/ |
@Override |
public void close() throws IOException { |
this.in.close(); |
this.buf = null; |
} |
@Override |
public int read(char[] cbuf, int off, int len) throws IOException { |
throw new IOException("read not supported"); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/doml/Element.java |
---|
New file |
0,0 → 1,586 |
/* |
* 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.doml; |
import java.io.BufferedOutputStream; |
import java.io.BufferedWriter; |
import java.io.IOException; |
import java.math.BigDecimal; |
import java.text.DecimalFormat; |
import java.text.DecimalFormatSymbols; |
import java.text.ParseException; |
import java.text.SimpleDateFormat; |
import java.time.Instant; |
import java.util.ArrayList; |
import java.util.Collections; |
import java.util.Date; |
import java.util.List; |
import java.util.Locale; |
import java.util.TimeZone; |
public class Element { |
private String name; |
private List<String> attributes; |
private List<Element> children; |
private static final ThreadLocal<SimpleDateFormat> DATE_FORMAT = ThreadLocal.withInitial(() -> { |
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); |
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); |
return dateFormat; |
}); |
private static final ThreadLocal<DecimalFormat> DOUBLE_FORMAT = ThreadLocal.withInitial(() -> { |
final DecimalFormat f = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); |
f.setMaximumFractionDigits(340); // 340 = DecimalFormat.DOUBLE_FRACTION_DIGITS |
return f; |
}); |
private static final ThreadLocal<DecimalFormat> FLOAT_FORMAT = ThreadLocal.withInitial(() -> { |
final DecimalFormat f = new DecimalFormat("0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); |
f.setMaximumFractionDigits(170); |
return f; |
}); |
/** |
* Element names are case-sensitive |
* |
* Element names can contain letters, digits,colons, hyphens, underscores, and periods. |
* |
* Element names cannot contain spaces |
*/ |
public Element(String name) { |
if (name == null) { |
throw new IllegalArgumentException("null name"); |
} |
this.name = name; |
} |
public Element(String name, int expectedAttributesCount) { |
if (name == null) { |
throw new IllegalArgumentException("null name"); |
} |
this.name = name; |
if (expectedAttributesCount > 0) { |
this.attributes = new ArrayList<>(expectedAttributesCount * 2); |
} |
} |
public Element(String name, int expectedAttributesCount, int expecteChildrenCount) { |
if (name == null) { |
throw new IllegalArgumentException("null name"); |
} |
this.name = name; |
if (expectedAttributesCount > 0) { |
this.attributes = new ArrayList<>(expectedAttributesCount * 2); |
} |
if (expecteChildrenCount > 0) { |
this.children = new ArrayList<>(expecteChildrenCount); |
} |
} |
public String getName() { |
return this.name; |
} |
public void setName(String name) { |
if (name == null) { |
throw new IllegalArgumentException("null name"); |
} |
this.name = name; |
} |
public int getAttributeCount() { |
if (this.attributes == null) |
return 0; |
return this.attributes.size() / 2; |
} |
// String |
public String getAttribute(String name) { |
if (name == null) { |
throw new IllegalArgumentException("null name"); |
} |
if (this.attributes == null) { |
return null; |
} |
final int size = this.attributes.size(); |
for (int i = 0; i < size; i += 2) { |
if (this.attributes.get(i).equals(name)) { |
return this.attributes.get(i + 1); |
} |
} |
return null; |
} |
public String getAttribute(String name, String defaultValue) { |
final String v = getAttribute(name); |
if (v == null) { |
return defaultValue; |
} |
return v; |
} |
public void setAttribute(String name, String value) { |
if (name == null) { |
throw new IllegalArgumentException("null name"); |
} |
if (value == null) { |
throw new IllegalArgumentException("null value for attribute " + name); |
} |
if (this.attributes == null) { |
this.attributes = new ArrayList<>(); |
} else { |
final int size = this.attributes.size(); |
for (int i = 0; i < size; i += 2) { |
if (this.attributes.get(i).equals(name)) { |
this.attributes.set(i + 1, value); |
return; |
} |
} |
} |
this.attributes.add(name); |
this.attributes.add(value); |
} |
// Integer |
public Integer getAttributeAsInteger(String name) { |
final String v = getAttribute(name); |
if (v == null) { |
return null; |
} |
return Integer.parseInt(v); |
} |
public int getAttributeAsInteger(String name, int defaultValue) { |
final String v = getAttribute(name); |
if (v == null) { |
return defaultValue; |
} |
return Integer.parseInt(v); |
} |
public void setAttribute(String name, int value) { |
setAttribute(name, String.valueOf(value)); |
} |
// Long |
public Long getAttributeAsLong(String name) { |
final String v = getAttribute(name); |
if (v == null) { |
return null; |
} |
return Long.parseLong(v); |
} |
public long getAttributeAsLong(String name, long defaultValue) { |
final String v = getAttribute(name); |
if (v == null) { |
return defaultValue; |
} |
return Long.parseLong(v); |
} |
public void setAttribute(String name, long value) { |
setAttribute(name, String.valueOf(value)); |
} |
// Float |
public Float getAttributeAsFloat(String name) { |
final String v = getAttribute(name); |
if (v == null) { |
return null; |
} |
return parseFloat(v); |
} |
public float getAttributeAsFloat(String name, float defaultValue) { |
final String v = getAttribute(name); |
if (v == null) { |
return defaultValue; |
} |
return parseFloat(v); |
} |
public void setAttribute(String name, float value) { |
setAttribute(name, floatToString(value)); |
} |
// Double |
public Double getAttributeAsDouble(String name) { |
final String v = getAttribute(name); |
if (v == null) { |
return null; |
} |
return parseDouble(name); |
} |
public double getAttributeAsLong(String name, double defaultValue) { |
final String v = getAttribute(name); |
if (v == null) { |
return defaultValue; |
} |
return parseDouble(name); |
} |
public void setAttribute(String name, double value) { |
setAttribute(name, doubleToString(value)); |
} |
public static String doubleToString(final double d) { |
if (StrictMath.rint(d) == d && Double.isFinite(d)) { |
return Long.toString((long) d); |
} else { |
return DOUBLE_FORMAT.get().format(d); |
} |
} |
private static double parseDouble(String v) { |
boolean neg = false; |
int start = 0; |
if (v.charAt(0) == '-') { |
neg = true; |
start = 1; |
} |
double result = 0; |
int length = v.length(); |
int pointIndex = length; |
for (int i = start; i < length; i++) { |
char c = v.charAt(i); |
if (c == '.') { |
pointIndex = i; |
break; |
} |
} |
for (int i = start; i < pointIndex; i++) { |
result = 10 * result + (v.charAt(i) - '0'); |
} |
int n = 0; |
int div = 1; |
for (int i = pointIndex + 1; i < length; i++) { |
n = 10 * n + (v.charAt(i) - '0'); |
div = div * 10; |
} |
result += ((double) n) / div; |
if (neg) { |
result = (result * -1f); |
} |
return result; |
} |
public static String floatToString(final double d) { |
if (StrictMath.rint(d) == d && Double.isFinite(d)) { |
return Long.toString((long) d); |
} else { |
return FLOAT_FORMAT.get().format(d); |
} |
} |
private static float parseFloat(String v) { |
boolean neg = false; |
int start = 0; |
if (v.charAt(0) == '-') { |
neg = true; |
start = 1; |
} |
float result = 0; |
int length = v.length(); |
int pointIndex = length; |
for (int i = start; i < length; i++) { |
char c = v.charAt(i); |
if (c == '.') { |
pointIndex = i; |
break; |
} |
} |
for (int i = start; i < pointIndex; i++) { |
result = 10 * result + (v.charAt(i) - '0'); |
} |
int n = 0; |
int div = 1; |
for (int i = pointIndex + 1; i < length; i++) { |
n = 10 * n + (v.charAt(i) - '0'); |
div = div * 10; |
} |
result += ((float) n) / div; |
if (neg) { |
result = (result * -1f); |
} |
return result; |
} |
// BigDecimal |
public BigDecimal getAttributeAsBigDecimal(String name) { |
final String v = getAttribute(name); |
if (v == null) { |
return null; |
} |
return new BigDecimal(v); |
} |
public BigDecimal getAttributeAsBigDecimal(String name, BigDecimal defaultValue) { |
final String v = getAttribute(name); |
if (v == null) { |
return defaultValue; |
} |
return new BigDecimal(v); |
} |
public void setAttribute(String name, BigDecimal value) { |
setAttribute(name, String.valueOf(value)); |
} |
// Boolean |
public Boolean getAttributeAsBoolean(String name) { |
final String v = getAttribute(name); |
if (v == null) { |
return null; |
} |
if (name.equals("true")) { |
return Boolean.TRUE; |
} |
return Boolean.FALSE; |
} |
public boolean getAttributeAsBoolean(String name, boolean defaultValue) { |
final String v = getAttribute(name); |
if (v == null) { |
return defaultValue; |
} |
return name.equals("true"); |
} |
public void setAttribute(String name, boolean value) { |
setAttribute(name, value ? "true" : "false"); |
} |
// Date |
public Date getAttributeAsDate(String name) { |
final String v = getAttribute(name); |
if (v == null) |
return null; |
try { |
return DATE_FORMAT.get().parse(v); |
} catch (ParseException e) { |
throw new IllegalStateException("cannot parse date : '" + v + "' : " + e); |
} |
} |
public Date getAttributeAsDate(String name, Date defaultValue) { |
final Date v = getAttributeAsDate(name); |
if (v == null) |
return defaultValue; |
return v; |
} |
public void setAttribute(String name, Date value) { |
setAttribute(name, value.toInstant().toString()); |
} |
// Instant |
public Instant getAttributeAsInstant(String name) { |
String v = getAttribute(name); |
if (v == null) |
return null; |
return Instant.parse(v); |
} |
public void setAttribute(String name, Instant value) { |
setAttribute(name, value.toString()); |
} |
public void addChild(Element child) { |
if (child == null) { |
throw new IllegalArgumentException("null child"); |
} |
if (this.children == null) { |
this.children = new ArrayList<>(); |
} |
this.children.add(child); |
} |
public void addChildren(List<Element> children) { |
if (children == null) { |
throw new IllegalArgumentException("null children"); |
} |
if (this.children == null) { |
this.children = new ArrayList<>(children.size() * 2); |
} |
this.children.addAll(children); |
} |
/** |
* This returns the first child element within this element with the given name. If no elements |
* exist for the specified name and namespace, null is returned. |
* |
* @param name of child element to match |
* @return the first matching child element, or null if not found |
*/ |
public Element getChild(String name) { |
if (name == null) { |
throw new IllegalArgumentException("null name"); |
} |
if (this.children == null) { |
return null; |
} |
for (Element e : this.children) { |
if (e.getName().equals(name)) { |
return e; |
} |
} |
return null; |
} |
public List<Element> getChildren(String name) { |
if (name == null) { |
throw new IllegalArgumentException("null name"); |
} |
if (this.children == null) { |
return Collections.emptyList(); |
} |
final List<Element> result = new ArrayList<>(); |
for (Element e : this.children) { |
if (e.getName().equals(name)) { |
result.add(e); |
} |
} |
return result; |
} |
/** |
* This returns a <code>List</code> of all the child elements nested directly (one level deep) |
* within this element, as <code>Element</code> objects. If this target element has no nested |
* elements, an empty List is returned. The returned list is unmodifiable. |
* |
* No recursion is performed. |
* |
* @return list of child <code>XXMLElement</code> objects for this element |
*/ |
public List<Element> getChildren() { |
if (this.children == null) { |
return Collections.emptyList(); |
} |
return Collections.unmodifiableList(children); |
} |
/** |
* <p> |
* This removes the first child element (one level deep) with the given name. Returns true if a |
* child was removed. |
* </p> |
* |
* @param name of child element to remove |
* @return whether deletion occurred |
*/ |
public boolean removeChild(final String name) { |
if (name == null) { |
throw new IllegalArgumentException("null name"); |
} |
if (this.children == null) { |
return false; |
} |
final int size = this.children.size(); |
for (int i = 0; i < size; i++) { |
if (this.children.get(i).getName().equals(name)) { |
this.children.remove(i); |
return true; |
} |
} |
return false; |
} |
public int getChildrenCount() { |
if (this.children == null) { |
return 0; |
} |
return this.children.size(); |
} |
void appendAttributes(BufferedWriter writer) throws IOException { |
if (this.attributes == null) { |
return; |
} |
final int size = this.attributes.size(); |
for (int i = 0; i < size; i += 2) { |
writer.append(' '); |
writer.append(this.attributes.get(i)); |
writer.append('='); |
writer.append('\"'); |
writer.append(escapeAttributeValue(this.attributes.get(i + 1))); |
writer.append('\"'); |
} |
} |
void writeAttributes(BufferedOutputStream out) throws IOException { |
final int size = this.attributes.size(); |
for (int i = 0; i < size; i += 2) { |
Document.writeUTF(out, this.attributes.get(i)); |
Document.writeUTF(out, this.attributes.get(i + 1)); |
} |
} |
private String escapeAttributeValue(String string) { |
StringBuilder b = new StringBuilder(string.length()); |
int length = string.length(); |
for (int i = 0; i < length; i++) { |
char c = string.charAt(i); |
if (c == '\n') { |
b.append('\\'); |
b.append('n'); |
} else if (c == '\r') { |
b.append('\\'); |
b.append('r'); |
} else if (c == '\t') { |
b.append('\\'); |
b.append('t'); |
} else if (c == '\\') { |
b.append('\\'); |
b.append('\\'); |
} else if (c == '"') { |
b.append('\\'); |
b.append('"'); |
} else { |
b.append(c); |
} |
} |
return b.toString(); |
} |
void addAttributeNoCheck(String name, String value) { |
this.attributes.add(name); |
this.attributes.add(value); |
} |
void addChildNoCheck(Element child) { |
if (child == null) { |
throw new IllegalArgumentException("null child"); |
} |
this.children.add(child); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/BloomFilter.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
41,7 → 41,7 |
public BloomFilter(int bitArraySize, int expectedElements) { |
this.bitArraySize = bitArraySize; |
this.expectedElements = expectedElements; |
this.k = (int) Math.ceil((bitArraySize / expectedElements) * Math.log(2.0)); |
this.k = (int) Math.ceil(((double) bitArraySize / expectedElements) * Math.log(2.0)); |
bitSet = new BitSet(bitArraySize); |
} |
148,7 → 148,7 |
} |
public static void main(String[] args) { |
BloomFilter<String> set = new BloomFilter<String>(100, 5); |
BloomFilter<String> set = new BloomFilter<>(100, 5); |
// Add some things to the bloom filter |
set.add("dog"); |
/trunk/OpenConcerto/src/org/openconcerto/utils/model/ListComboBoxModel.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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,8 |
*/ |
public class ListComboBoxModel<T> extends DefaultIMutableListModel<T> implements MutableComboBoxModel<T> { |
private boolean truncated; |
public ListComboBoxModel() { |
this(Collections.<T> emptyList()); |
} |
44,4 → 46,12 |
this.removeForJRE(obj); |
} |
public void setTruncated(boolean truncated) { |
this.truncated = truncated; |
} |
public boolean isTruncated() { |
return this.truncated; |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/Platform.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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.utils.cc.ITransformer; |
import java.io.BufferedReader; |
import java.io.ByteArrayOutputStream; |
import java.io.File; |
import java.io.IOException; |
import java.io.InputStreamReader; |
25,6 → 26,7 |
import java.net.InetAddress; |
import java.net.SocketException; |
import java.nio.file.Files; |
import java.nio.file.Path; |
import java.nio.file.Paths; |
import java.util.ArrayList; |
import java.util.List; |
40,6 → 42,8 |
*/ |
public abstract class Platform { |
public static final String PROCESS_ALLOW_AMBIGUOUS_COMMANDS = "jdk.lang.Process.allowAmbiguousCommands"; |
private static final int PING_TIMEOUT = 250; |
public static final Platform getInstance() { |
55,6 → 59,19 |
} |
} |
public static class CannotPassArgumentException extends RuntimeException { |
private final String arg; |
private CannotPassArgumentException(String arg, String message) { |
super("Cannot pass " + arg + message); |
this.arg = arg; |
} |
public final String getArg() { |
return this.arg; |
} |
} |
public abstract boolean supportsPID(); |
public abstract boolean isRunning(final int pid) throws IOException; |
61,6 → 78,14 |
public abstract String getPath(final File f); |
public String getPath(final Path p) { |
return this.getPath(p.toFile()); |
} |
public String getProcessArg(final String arg) { |
return arg; |
} |
public final String getPID() throws IOException { |
// TODO remove reflection and getPreJava9PID() once on java 11 |
try { |
153,7 → 178,19 |
// Keep errors out of cmdSubstitution() (e.g. "ping: sendto: Message too long" when |
// setDontFragment(true)) |
final Process proc = evalPB(command).redirectErrorStream(false).start(); |
final String output = cmdSubstitution(proc); |
// some programs won't write anything until they read everything |
proc.getOutputStream().close(); |
final ByteArrayOutputStream out = new ByteArrayOutputStream(5 * 1024); |
final ByteArrayOutputStream err = new ByteArrayOutputStream(2 * 1024); |
try (final ProcessStreams streams = new ProcessStreams(proc)) { |
streams.start(out, err); |
streams.awaitTermination(); |
} catch (Exception e) { |
throw new IllegalStateException("Couldn't capture output of ping", e); |
} |
final String output = out.toString(); |
try { |
this.waitForSuccess(proc, "ping"); |
final List<String> countAndLastLine = StringUtils.splitIntoLines(output); |
164,7 → 201,7 |
final BigDecimal averageRTT = replied == 0 ? null : parsePingAverageRT(countAndLastLine.get(1).trim()); |
return new PingResult(totalCount, replied, requiredCount, averageRTT); |
} catch (Exception e) { |
throw new IllegalStateException("Couldn't use output :<<<\n" + output + "\n<<<", e); |
throw new IllegalStateException("Couldn't use output :<<<\n" + output + "\n<<<\nerr:<<<\n" + err.toString() + "\n<<<", e); |
} |
} |
230,11 → 267,17 |
return this.exitStatus(p) == 0; |
} |
@Override |
public String getPath(final File f) { |
return f.getPath(); |
} |
@Override |
public String getPath(final Path f) { |
return f.toString(); |
} |
@Override |
protected String getBash() { |
return "bash"; |
} |
381,9 → 424,129 |
} |
}; |
private static final class CygwinPlatform extends Platform { |
// return 171 for "1.8.0_171" |
// return 11 for "11.0.11" |
public static final int getUpdateVersion(final char sep) { |
final String vers = System.getProperty("java.version"); |
final int lastIndexOf = vers.lastIndexOf(sep); |
// e.g. "13" |
if (lastIndexOf < 0) |
return 0; |
return Integer.parseInt(vers.substring(lastIndexOf + 1)); |
} |
public static abstract class WindowsPlatform extends Platform { |
// on Windows program themselves are required to parse the command line, thus a lot of them |
// do it differently, see "How Command Line Parameters Are Parsed" |
// https://daviddeley.com/autohotkey/parameters/parameters.htm |
static private final Pattern quotePatrn = Pattern.compile("([\\\\]*)\""); |
static private final Pattern endSlashPatrn = Pattern.compile("([\\\\]+)\\z"); |
static private boolean needsQuoting(String s) { |
final int len = s.length(); |
if (len == 0) // empty string have to be quoted |
return true; |
for (int i = 0; i < len; i++) { |
switch (s.charAt(i)) { |
case ' ': |
case '\t': |
case '"': |
return true; |
} |
} |
return false; |
} |
// see http://bugs.sun.com/view_bug.do?bug_id=6468220 |
// e.g. find.exe, choice.exe |
public String quoteParamForMsftC(String s) { |
if (!needsQuoting(s)) |
return s; |
if (s.length() > 0) { |
// replace '(\*)"' by '$1$1\"', e.g. '\quote " \"' by '\quote \" \\\"' |
// $1 needed so that the backslash we add isn't escaped itself by a preceding |
// backslash |
s = quotePatrn.matcher(s).replaceAll("$1$1\\\\\""); |
// replace '(\*)\z' by '$1$1', e.g. 'foo\' by 'foo\\' |
// needed to not escape closing quote |
s = endSlashPatrn.matcher(s).replaceAll("$1$1"); |
} |
return '"' + s + '"'; |
} |
@Override |
public String getProcessArg(String arg) { |
return this.getProcessArg(arg, false); |
} |
public final String getScriptProcessArg(String arg) { |
return this.getProcessArg(arg, true); |
} |
private String getProcessArg(String arg, final boolean script) { |
// Perhaps should have one method for .exe and one for .cmd/.bat (ProcessImpl checks |
// with isShellFile() and isExe()). |
if (script && arg.indexOf('"') >= 0) |
throw new CannotPassArgumentException(arg, ", it contains a double quote which is always removed by wscript!SplitCommandLine()"); |
/* |
* If has VERIFICATION_WIN32_SAFE && !allowAmbiguousCommands, then ProcessImpl behaves |
* almost correctly : the argument we pass arrives as-is to the process (so the caller |
* shouldn't quote, ProcessImpl will fail with "Malformed argument has embedded quote" |
* if a character was escaped). Otherwise ProcessImpl does nothing if arg begins and |
* ends with double quotes (it's the caller responsibility to correctly quote). |
*/ |
final boolean doubleQuote; |
// 1.8 or 11 |
final String specVers = System.getProperty("java.specification.version"); |
// 8 or 11 |
final int jreFamilyVersion = Integer.parseInt(specVers.startsWith("1.") ? specVers.substring(2) : specVers); |
final String javaVendor = System.getProperty("java.vendor"); |
// VERIFICATION_WIN32_SAFE thanks to https://nvd.nist.gov/vuln/detail/CVE-2019-2958 |
// For Oracle and OpenJDK : |
// https://www.oracle.com/java/technologies/javase/8u231-relnotes.html#JDK-8221858 |
// https://github.com/openjdk/jdk/commit/5a98b8cfb0cd4c5ce84746e9fa5e42a86d1b2d24#diff-71e1dde85b2937bbf08d0bfd9e8a2e3553c578a3f58907ba6f4f30ec350db184 |
// For AdoptOpenJDK : |
// https://github.com/AdoptOpenJDK/openjdk-jdk11u/commit/0074850a15405a676a492d09e4955d4053966bbc#diff-71e1dde85b2937bbf08d0bfd9e8a2e3553c578a3f58907ba6f4f30ec350db184 |
if (jreFamilyVersion >= 14 || (javaVendor.equals("AdoptOpenJDK") && (jreFamilyVersion == 11 && getUpdateVersion('.') >= 5 || jreFamilyVersion == 13 && getUpdateVersion('.') >= 1)) |
|| (jreFamilyVersion == 8 && getUpdateVersion('_') >= 231)) { |
// copied from ProcessImpl |
final SecurityManager security = System.getSecurityManager(); |
final String value = System.getProperty(PROCESS_ALLOW_AMBIGUOUS_COMMANDS, (security == null ? "true" : "false")); |
final boolean allowAmbiguousCommands = !"false".equalsIgnoreCase(value); |
if ((script || allowAmbiguousCommands) && arg.endsWith("\\")) { |
// with script, we always do our own quoting since ProcessBuilder will double |
// back slashes. |
if (script || needsQuoting(arg)) { |
throw new CannotPassArgumentException(arg, |
", if we'd double quote, then the string would end with 2 back slashes and 1 double quote, but ProcessImpl.unQuote() : 'not properly quoted, treat as unquoted'. So it would get quoted a second time. Try setting '" |
+ PROCESS_ALLOW_AMBIGUOUS_COMMANDS + "' to 'false'."); |
} else { |
doubleQuote = false; |
} |
} else if (!allowAmbiguousCommands && arg.length() >= 2 && arg.charAt(0) == '"' && arg.charAt(arg.length() - 1) == '"') { |
throw new CannotPassArgumentException(arg, |
"\nif we pass it as-is then ProcessImpl will consider the string already quoted and needsEscaping() will return false, thus the quotes will be removed by CommandLineToArgvW()" |
+ "\nif we quote it then ProcessImpl.needsEscaping() will throw 'Malformed argument has embedded quote'"); |
} else { |
doubleQuote = allowAmbiguousCommands; |
} |
} else { |
doubleQuote = true; |
} |
if (script) |
return '"' + arg + '"'; |
else |
return doubleQuote ? quoteParamForMsftC(arg) : arg; |
} |
} |
private static final class CygwinPlatform extends WindowsPlatform { |
@Override |
public boolean supportsPID() { |
return false; |
} |
/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/DesktopEnvironment.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
160,62 → 160,12 |
} |
static public final class Windows extends DEisOS { |
static private boolean needsQuoting(String s) { |
final int len = s.length(); |
if (len == 0) // empty string have to be quoted |
return true; |
for (int i = 0; i < len; i++) { |
switch (s.charAt(i)) { |
case ' ': |
case '\t': |
case '\\': |
case '"': |
return true; |
} |
} |
return false; |
} |
// on Windows program themselves are required to parse the command line, thus a lot of them |
// do it differently, see http://www.autohotkey.net/~deleyd/parameters/parameters.htm |
static private final Pattern quotePatrn = Pattern.compile("([\\\\]*)\""); |
static private final Pattern endSlashPatrn = Pattern.compile("([\\\\]*)\\z"); |
// see http://bugs.sun.com/view_bug.do?bug_id=6468220 |
// e.g. find.exe, choice.exe |
public String quoteParamForMsftC(String s) { |
if (!needsQuoting(s)) |
return s; |
if (s.length() > 0) { |
// replace '(\*)"' by '$1$1\"', e.g. '\quote " \"' by '\quote \" \\\"' |
// $1 needed so that the backslash we add isn't escaped itself by a preceding |
// backslash |
s = quotePatrn.matcher(s).replaceAll("$1$1\\\\\""); |
// replace '(\*)\z' by '$1$1', e.g. 'foo\' by 'foo\\' |
// needed to not escape closing quote |
s = endSlashPatrn.matcher(s).replaceAll("$1$1"); |
} |
return '"' + s + '"'; |
} |
// e.g. bash.exe |
public String quoteParamForGCC(String s) { |
return StringUtils.doubleQuote(s); |
} |
public String quoteParamForScript(final String s) { |
if (s.indexOf('"') >= 0) |
throw new IllegalArgumentException("Can not pass a double quote as part of a parameter"); |
return '"' + s + '"'; |
} |
@Override |
public String quoteParamForExec(final String s) { |
return quoteParamForMsftC(s); |
} |
} |
static public final class Mac extends DEisOS { |
// From CarbonCore/Folders.h |
398,8 → 348,8 |
} |
// on some systems arguments are not passed correctly by ProcessBuilder |
public String quoteParamForExec(String s) { |
return s; |
public final String quoteParamForExec(String s) { |
return Platform.getInstance().getProcessArg(s); |
} |
@Override |
/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/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; |
187,6 → 188,21 |
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/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/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/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/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/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,13 → 48,8 |
@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 |
public boolean addAll(Collection<? extends E> c) { |
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/IntHashSet.java |
---|
New file |
0,0 → 1,675 |
/* |
* 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; |
import java.util.Arrays; |
import java.util.Collection; |
/** |
* A type-specific hash set with with a fast, small-footprint implementation. |
* |
* <p> |
* Instances of this class use a hash table to represent a set. The table is filled up to a |
* specified <em>load factor</em>, and then doubled in size to accommodate new entries. If the table |
* is emptied below <em>one fourth</em> of the load factor, it is halved in size; however, the table |
* is never reduced to a size smaller than that at creation time: this approach makes it possible to |
* create sets with a large capacity in which insertions and deletions do not cause immediately |
* rehashing. Moreover, halving is not performed when deleting entries from an iterator, as it would |
* interfere with the iteration process. |
* |
* <p> |
* Note that {@link #clear()} does not modify the hash table size. Rather, a family of |
* {@linkplain #trim() trimming methods} lets you control the size of the table; this is |
* particularly useful if you reuse instances of this class. |
* |
*/ |
public class IntHashSet { |
static final float DEFAULT_LOAD_FACTOR = 0.75f; |
private static final int DEFAULT_INITIAL_SIZE = 16; |
/** 2<sup>32</sup> · φ, φ = (√5 − 1)/2. */ |
private static final int INT_PHI = 0x9E3779B9; |
/** The reciprocal of {@link #INT_PHI} modulo 2<sup>32</sup>. */ |
private static final int INV_INT_PHI = 0x144cbc89; |
/** The array of keys. */ |
protected transient int[] key; |
/** The mask for wrapping a position counter. */ |
protected transient int mask; |
/** Whether this set contains the null key. */ |
protected transient boolean containsNull; |
/** |
* The current table size. Note that an additional element is allocated for storing the null |
* key. |
*/ |
protected transient int n; |
/** Threshold after which we rehash. It must be the table size times {@link #f}. */ |
protected transient int maxFill; |
/** We never resize below this threshold, which is the construction-time {#n}. */ |
protected final transient int minN; |
/** Number of entries in the set (including the null key, if present). */ |
protected int size; |
/** The acceptable load factor. */ |
protected final float f; |
/** |
* Creates a new hash set. |
* |
* <p> |
* The actual table size will be the least power of two greater than {@code expected}/{@code f}. |
* |
* @param expected the expected number of elements in the hash set. |
* @param f the load factor. |
*/ |
public IntHashSet(final int expected, final float f) { |
if (f <= 0 || f >= 1) |
throw new IllegalArgumentException("Load factor must be greater than 0 and smaller than 1"); |
if (expected < 0) |
throw new IllegalArgumentException("The expected number of elements must be nonnegative"); |
this.f = f; |
minN = n = arraySize(expected, f); |
mask = n - 1; |
maxFill = maxFill(n, f); |
key = new int[n + 1]; |
} |
/** |
* Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor. |
* |
* @param expected the expected number of elements in the hash set. |
*/ |
public IntHashSet(final int expected) { |
this(expected, DEFAULT_LOAD_FACTOR); |
} |
/** |
* Creates a new hash set with initial expected {@link Hash#DEFAULT_INITIAL_SIZE} elements and |
* {@link Hash#DEFAULT_LOAD_FACTOR} as load factor. |
*/ |
public IntHashSet() { |
this(DEFAULT_INITIAL_SIZE, DEFAULT_LOAD_FACTOR); |
} |
/** |
* Creates a new hash set copying a given collection. |
* |
* @param c a {@link Collection} to be copied into the new hash set. |
* @param f the load factor. |
*/ |
public IntHashSet(final Collection<? extends Integer> c, final float f) { |
this(c.size(), f); |
addAll(c); |
} |
/** |
* Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor copying a given |
* collection. |
* |
* @param c a {@link Collection} to be copied into the new hash set. |
*/ |
public IntHashSet(final Collection<? extends Integer> c) { |
this(c, DEFAULT_LOAD_FACTOR); |
} |
/** |
* Creates a new hash set and fills it with the elements of a given array. |
* |
* @param a an array whose elements will be used to fill the set. |
* @param offset the first element to use. |
* @param length the number of elements to use. |
* @param f the load factor. |
*/ |
public IntHashSet(final int[] a, final int offset, final int length, final float f) { |
this(length < 0 ? 0 : length, f); |
ensureOffsetLength(a.length, offset, length); |
for (int i = 0; i < length; i++) |
add(a[offset + i]); |
} |
/** |
* Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor and fills it with |
* the elements of a given array. |
* |
* @param a an array whose elements will be used to fill the set. |
* @param offset the first element to use. |
* @param length the number of elements to use. |
*/ |
public IntHashSet(final int[] a, final int offset, final int length) { |
this(a, offset, length, DEFAULT_LOAD_FACTOR); |
} |
/** |
* Creates a new hash set copying the elements of an array. |
* |
* @param a an array to be copied into the new hash set. |
* @param f the load factor. |
*/ |
public IntHashSet(final int[] a, final float f) { |
this(a, 0, a.length, f); |
} |
/** |
* Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor copying the |
* elements of an array. |
* |
* @param a an array to be copied into the new hash set. |
*/ |
public IntHashSet(final int[] a) { |
this(a, DEFAULT_LOAD_FACTOR); |
} |
/** |
* Creates a new empty hash set. |
* |
* @return a new empty hash set. |
*/ |
public static IntHashSet of() { |
return new IntHashSet(); |
} |
/** |
* Returns the least power of two smaller than or equal to 2<sup>30</sup> and larger than or |
* equal to {@code Math.ceil(expected / f)}. |
* |
* @param expected the expected number of elements in a hash table. |
* @param f the load factor. |
* @return the minimum possible size for a backing array. |
* @throws IllegalArgumentException if the necessary size is larger than 2<sup>30</sup>. |
*/ |
public static int arraySize(final int expected, final float f) { |
final long s = Math.max(2, nextPowerOfTwo((long) Math.ceil(expected / f))); |
if (s > (1 << 30)) |
throw new IllegalArgumentException("Too large (" + expected + " expected elements with load factor " + f + ")"); |
return (int) s; |
} |
/** |
* Returns the maximum number of entries that can be filled before rehashing. |
* |
* @param n the size of the backing array. |
* @param f the load factor. |
* @return the maximum number of entries before rehashing. |
*/ |
public static int maxFill(final int n, final float f) { |
/* |
* We must guarantee that there is always at least one free entry (even with pathological |
* load factors). |
*/ |
return Math.min((int) Math.ceil(n * f), n - 1); |
} |
/** |
* Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor using the given |
* element. |
* |
* @param e the element that the returned set will contain. |
* @return a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor containing |
* {@code e}. |
*/ |
public static IntHashSet of(final int e) { |
IntHashSet result = new IntHashSet(1, DEFAULT_LOAD_FACTOR); |
result.add(e); |
return result; |
} |
/** |
* Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor using the |
* elements given. |
* |
* @param e0 the first element. |
* @param e1 the second element. |
* @return a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor containing |
* {@code e0} and {@code e1}. |
* @throws IllegalArgumentException if there were duplicate entries. |
*/ |
public static IntHashSet of(final int e0, final int e1) { |
IntHashSet result = new IntHashSet(2, DEFAULT_LOAD_FACTOR); |
result.add(e0); |
if (!result.add(e1)) { |
throw new IllegalArgumentException("Duplicate element: " + e1); |
} |
return result; |
} |
/** |
* Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor using the |
* elements given. |
* |
* @param e0 the first element. |
* @param e1 the second element. |
* @param e2 the third element. |
* @return a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor containing |
* {@code e0}, {@code e1}, and {@code e2}. |
* @throws IllegalArgumentException if there were duplicate entries. |
*/ |
public static IntHashSet of(final int e0, final int e1, final int e2) { |
IntHashSet result = new IntHashSet(3, DEFAULT_LOAD_FACTOR); |
result.add(e0); |
if (!result.add(e1)) { |
throw new IllegalArgumentException("Duplicate element: " + e1); |
} |
if (!result.add(e2)) { |
throw new IllegalArgumentException("Duplicate element: " + e2); |
} |
return result; |
} |
/** |
* Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor using a list of |
* elements. |
* |
* @param a a list of elements that will be used to initialize the new hash set. |
* @return a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor containing the |
* elements of {@code a}. |
* @throws IllegalArgumentException if a duplicate entry was encountered. |
*/ |
public static IntHashSet of(final int... a) { |
IntHashSet result = new IntHashSet(a.length, DEFAULT_LOAD_FACTOR); |
for (int element : a) { |
if (!result.add(element)) { |
throw new IllegalArgumentException("Duplicate element " + element); |
} |
} |
return result; |
} |
private int realSize() { |
return containsNull ? size - 1 : size; |
} |
private void ensureCapacity(final int capacity) { |
final int needed = arraySize(capacity, f); |
if (needed > n) |
rehash(needed); |
} |
private void tryCapacity(final long capacity) { |
final int needed = (int) Math.min(1 << 30, Math.max(2, nextPowerOfTwo((long) Math.ceil(capacity / f)))); |
if (needed > n) |
rehash(needed); |
} |
public void addAll(Collection<? extends Integer> c) { |
// The resulting collection will be at least c.size() big |
if (f <= .5) |
ensureCapacity(c.size()); // The resulting collection will be sized for c.size() |
// elements |
else |
tryCapacity(size() + c.size()); // The resulting collection will be tentatively sized |
// for size() + c.size() elements |
for (Integer i : c) { |
add(i); |
} |
} |
/** |
* Quickly mixes the bits of an integer. |
* |
* <p> |
* This method mixes the bits of the argument by multiplying by the golden ratio and xorshifting |
* the result. It is borrowed from <a href="https://github.com/OpenHFT/Koloboke">Koloboke</a>, |
* and it has slightly worse behaviour than {@link #murmurHash3(int)} (in open-addressing hash |
* tables the average number of probes is slightly larger), but it's much faster. |
* |
* @param x an integer. |
* @return a hash value obtained by mixing the bits of {@code x}. |
* @see #invMix(int) |
*/ |
public static int mix(final int x) { |
final int h = x * INT_PHI; |
return h ^ (h >>> 16); |
} |
/** |
* The inverse of {@link #mix(int)}. This method is mainly useful to create unit tests. |
* |
* @param x an integer. |
* @return a value that passed through {@link #mix(int)} would give {@code x}. |
*/ |
public static int invMix(final int x) { |
return (x ^ x >>> 16) * INV_INT_PHI; |
} |
public boolean add(final int k) { |
int pos; |
if (k == 0) { |
if (containsNull) |
return false; |
containsNull = true; |
} else { |
int curr; |
final int[] key = this.key; |
// The starting point. |
if (!((curr = key[pos = (mix((k))) & mask]) == 0)) { |
if (curr == k) |
return false; |
while (!((curr = key[pos = (pos + 1) & mask]) == 0)) |
if (((curr) == (k))) |
return false; |
} |
key[pos] = k; |
} |
if (size++ >= maxFill) |
rehash(arraySize(size + 1, f)); |
return true; |
} |
/** |
* Shifts left entries with the specified hash code, starting at the specified position, and |
* empties the resulting free entry. |
* |
* @param pos a starting position. |
*/ |
protected final void shiftKeys(int pos) { |
// Shift entries with the same hash. |
int last; |
int slot; |
int curr; |
final int[] key = this.key; |
for (;;) { |
pos = ((last = pos) + 1) & mask; |
for (;;) { |
if (((curr = key[pos]) == (0))) { |
key[last] = (0); |
return; |
} |
slot = (mix((curr))) & mask; |
if (last <= pos ? last >= slot || slot > pos : last >= slot && slot > pos) |
break; |
pos = (pos + 1) & mask; |
} |
key[last] = curr; |
} |
} |
private boolean removeEntry(final int pos) { |
size--; |
shiftKeys(pos); |
if (n > minN && size < maxFill / 4 && n > DEFAULT_INITIAL_SIZE) |
rehash(n / 2); |
return true; |
} |
private boolean removeNullEntry() { |
containsNull = false; |
key[n] = (0); |
size--; |
if (n > minN && size < maxFill / 4 && n > DEFAULT_INITIAL_SIZE) |
rehash(n / 2); |
return true; |
} |
public boolean remove(final int k) { |
if (((k) == (0))) { |
if (containsNull) |
return removeNullEntry(); |
return false; |
} |
int curr; |
final int[] key = this.key; |
int pos; |
// The starting point. |
if (((curr = key[pos = (mix((k))) & mask]) == (0))) |
return false; |
if (((k) == (curr))) |
return removeEntry(pos); |
while (true) { |
if (((curr = key[pos = (pos + 1) & mask]) == (0))) |
return false; |
if (((k) == (curr))) |
return removeEntry(pos); |
} |
} |
public boolean contains(final int k) { |
if (((k) == (0))) |
return containsNull; |
int curr; |
final int[] key = this.key; |
int pos; |
// The starting point. |
if (((curr = key[pos = (mix((k))) & mask]) == (0))) |
return false; |
if (((k) == (curr))) |
return true; |
while (true) { |
if (((curr = key[pos = (pos + 1) & mask]) == (0))) |
return false; |
if (((k) == (curr))) |
return true; |
} |
} |
/* |
* Removes all elements from this set. |
* |
* <p>To increase object reuse, this method does not change the table size. If you want to |
* reduce the table size, you must use {@link #trim()}. |
* |
*/ |
public void clear() { |
if (size == 0) |
return; |
size = 0; |
containsNull = false; |
Arrays.fill(key, (0)); |
} |
public int size() { |
return size; |
} |
public boolean isEmpty() { |
return size == 0; |
} |
/** |
* Rehashes this set, making the table as small as possible. |
* |
* <p> |
* This method rehashes the table to the smallest size satisfying the load factor. It can be |
* used when the set will not be changed anymore, so to optimize access speed and size. |
* |
* <p> |
* If the table size is already the minimum possible, this method does nothing. |
* |
* @return true if there was enough memory to trim the set. |
* @see #trim(int) |
*/ |
public boolean trim() { |
return trim(size); |
} |
/** |
* Rehashes this set if the table is too large. |
* |
* <p> |
* Let <var>N</var> be the smallest table size that can hold <code>max(n,{@link #size()})</code> |
* entries, still satisfying the load factor. If the current table size is smaller than or equal |
* to <var>N</var>, this method does nothing. Otherwise, it rehashes this set in a table of size |
* <var>N</var>. |
* |
* <p> |
* This method is useful when reusing sets. {@linkplain #clear() Clearing a set} leaves the |
* table size untouched. If you are reusing a set many times, you can call this method with a |
* typical size to avoid keeping around a very large table just because of a few large transient |
* sets. |
* |
* @param n the threshold for the trimming. |
* @return true if there was enough memory to trim the set. |
* @see #trim() |
*/ |
public boolean trim(final int n) { |
final int l = nextPowerOfTwo((int) Math.ceil(n / f)); |
if (l >= this.n || size > maxFill(l, f)) |
return true; |
try { |
rehash(l); |
} catch (OutOfMemoryError cantDoIt) { |
return false; |
} |
return true; |
} |
/** |
* Rehashes the set. |
* |
* <p> |
* This method implements the basic rehashing strategy, and may be overriden by subclasses |
* implementing different rehashing strategies (e.g., disk-based rehashing). However, you should |
* not override this method unless you understand the internal workings of this class. |
* |
* @param newN the new size |
*/ |
protected void rehash(final int newN) { |
final int key[] = this.key; |
final int mask = newN - 1; // Note that this is used by the hashing macro |
final int newKey[] = new int[newN + 1]; |
int i = n, pos; |
for (int j = realSize(); j-- != 0;) { |
while (((key[--i]) == (0))) |
; |
if (!((newKey[pos = (mix((key[i]))) & mask]) == (0))) |
while (!((newKey[pos = (pos + 1) & mask]) == (0))) |
; |
newKey[pos] = key[i]; |
} |
n = newN; |
this.mask = mask; |
maxFill = maxFill(n, f); |
this.key = newKey; |
} |
/** |
* Returns a deep copy of this set. |
* |
* <p> |
* This method performs a deep copy of this hash set; the data stored in the set, however, is |
* not cloned. Note that this makes a difference only for object keys. |
* |
* @return a deep copy of this set. |
*/ |
@Override |
public IntHashSet clone() { |
IntHashSet c; |
try { |
c = (IntHashSet) super.clone(); |
} catch (CloneNotSupportedException cantHappen) { |
throw new InternalError(); |
} |
c.key = key.clone(); |
c.containsNull = containsNull; |
return c; |
} |
/** |
* Returns a hash code for this set. |
* |
* This method overrides the generic method provided by the superclass. Since {@code equals()} |
* is not overriden, it is important that the value returned by this method is the same value as |
* the one returned by the overriden method. |
* |
* @return a hash code for this set. |
*/ |
@Override |
public int hashCode() { |
int h = 0; |
for (int j = realSize(), i = 0; j-- != 0;) { |
while (((key[i]) == (0))) |
i++; |
h += (key[i]); |
i++; |
} |
// Zero / null have hash zero. |
return h; |
} |
/** |
* Returns the least power of two greater than or equal to the specified value. |
* |
* <p> |
* Note that this function will return 1 when the argument is 0. |
* |
* @param x an integer smaller than or equal to 2<sup>30</sup>. |
* @return the least power of two greater than or equal to the specified value. |
*/ |
public static int nextPowerOfTwo(int x) { |
if (x == 0) |
return 1; |
x--; |
x |= x >> 1; |
x |= x >> 2; |
x |= x >> 4; |
x |= x >> 8; |
return (x | x >> 16) + 1; |
} |
/** |
* Returns the least power of two greater than or equal to the specified value. |
* |
* <p> |
* Note that this function will return 1 when the argument is 0. |
* |
* @param x a long integer smaller than or equal to 2<sup>62</sup>. |
* @return the least power of two greater than or equal to the specified value. |
*/ |
public static long nextPowerOfTwo(long x) { |
if (x == 0) |
return 1; |
x--; |
x |= x >> 1; |
x |= x >> 2; |
x |= x >> 4; |
x |= x >> 8; |
x |= x >> 16; |
return (x | x >> 32) + 1; |
} |
/** |
* Ensures that a range given by an offset and a length fits an array of given length. |
* |
* <p> |
* This method may be used whenever an array range check is needed. |
* |
* <p> |
* In Java 9 and up, this method should be considered deprecated in favor of the |
* {@link java.util.Objects#checkFromIndexSize(int, int, int)} method, which may be intrinsified |
* in recent JVMs. |
* |
* @param arrayLength an array length. |
* @param offset a start index for the fragment |
* @param length a length (the number of elements in the fragment). |
* @throws IllegalArgumentException if {@code length} is negative. |
* @throws ArrayIndexOutOfBoundsException if {@code offset} is negative or |
* {@code offset}+{@code length} is greater than {@code arrayLength}. |
*/ |
public static void ensureOffsetLength(final int arrayLength, final int offset, final int length) { |
// When Java 9 becomes the minimum, use Objects#checkFromIndexSize, as that can be an |
// intrinsic |
if (offset < 0) |
throw new ArrayIndexOutOfBoundsException("Offset (" + offset + ") is negative"); |
if (length < 0) |
throw new IllegalArgumentException("Length (" + length + ") is negative"); |
if (offset + length > arrayLength) |
throw new ArrayIndexOutOfBoundsException("Last index (" + (offset + length) + ") is greater than array length (" + arrayLength + ")"); |
} |
} |
/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/ExceptionHandler.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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,21 → 128,26 |
clipboard.setContents(data, data); |
} |
static public Future<Boolean> handle(Component comp, String msg, Throwable originalExn) { |
return handle(comp, msg, null, originalExn); |
} |
/** |
* Display the passed message. Note: this method doesn't block. |
* |
* @param comp the modal parent of the error window. |
* @param msg the message to display. |
* @param title the title, can be <code>null</code>. |
* @param originalExn the cause, can be <code>null</code>. |
* @return a future completed when the error is handled (e.g. the user clicked on the dialog), |
* <code>false</code> if the error couldn't be displayed to the user. |
*/ |
static public Future<Boolean> handle(Component comp, String msg, Throwable originalExn) { |
static public Future<Boolean> handle(Component comp, String msg, String title, Throwable originalExn) { |
final Future<Boolean> res; |
if (tHandler != null && tHandler.handle(msg, originalExn)) { |
res = TRUE_FUTURE; |
} else { |
res = new ExceptionHandler(comp, msg, originalExn, false).display(); |
res = new ExceptionHandler(comp, msg, title, originalExn, false).display(); |
} |
assert res != null; |
return res; |
165,7 → 170,7 |
* @return an exception. |
*/ |
static public RuntimeException die(String msg, Throwable originalExn) { |
final ExceptionHandler res = new ExceptionHandler(null, msg, originalExn); |
final ExceptionHandler res = new ExceptionHandler(null, msg, null, originalExn); |
res.display(); |
return res; |
} |
180,6 → 185,7 |
// the comp on which to display the popup, may be null |
private final Component comp; |
private final String title; |
private final boolean quit; |
protected static AtomicInteger openedWindows = new AtomicInteger(0); |
private static boolean forceUI; |
241,7 → 247,10 |
return Boolean.FALSE; |
} |
protected final void showMsg(final String msg, final boolean quit) { |
protected final void showMsg(String msg, final boolean quit) { |
if (msg == null) { |
msg = ""; |
} |
final JPanel p = new JPanel(); |
p.setLayout(new GridBagLayout()); |
final GridBagConstraints c = new GridBagConstraints(); |
406,9 → 415,9 |
final Window window = this.comp == null ? null : SwingUtilities.getWindowAncestor(this.comp); |
final JDialog f; |
if (window instanceof Frame) { |
f = new JDialog((Frame) window, "Erreur", true); |
f = new JDialog((Frame) window, this.getTitle(), true); |
} else { |
f = new JDialog((Dialog) window, "Erreur", true); |
f = new JDialog((Dialog) window, this.getTitle(), true); |
} |
f.setContentPane(p); |
f.pack(); |
451,10 → 460,11 |
* |
* @param comp the component upon which to display the popup. |
* @param msg le message d'erreur à afficher. |
* @param title the title, can be <code>null</code>. |
* @param cause la cause de l'exception (peut être <code>null</code>). |
*/ |
private ExceptionHandler(Component comp, String msg, Throwable cause) { |
this(comp, msg, cause, true); |
private ExceptionHandler(Component comp, String msg, String title, Throwable cause) { |
this(comp, msg, title, cause, true); |
} |
/** |
462,15 → 472,23 |
* |
* @param comp the component upon which to display the popup. |
* @param msg the error message to display. |
* @param title the title, can be <code>null</code>. |
* @param cause the cause of the exception (maybe <code>null</code>). |
* @param quit if the VM must exit. |
*/ |
private ExceptionHandler(Component comp, String msg, Throwable cause, boolean quit) { |
private ExceptionHandler(Component comp, String msg, String title, Throwable cause, boolean quit) { |
super(msg, cause); |
this.comp = comp; |
this.title = title; |
this.quit = quit; |
} |
public String getTitle() { |
if (this.title != null) |
return this.title; |
return this.quit ? "Erreur fatale" : "Erreur"; |
} |
private void submitError(String error) { |
final Charset cs = StringUtils.UTF8; |
try { |
/trunk/OpenConcerto/src/org/openconcerto/utils/SystemInfo.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
47,6 → 47,8 |
} |
public static final String CLASS_PROTOCOL = "class"; |
public static final String PROPS_PROTOCOL = "props"; |
public static final String ENV_PROTOCOL = "env"; |
static public NavigableMap<Info, String> get(final boolean html, final Locale locale) { |
final TM tm = TM.getInstance(locale); |
67,12 → 69,22 |
e1.printStackTrace(); |
} |
final Runtime rt = Runtime.getRuntime(); |
final String stats = "<i>" + tm.translate("memory") + " :</i> " + formatBytes(tm, rt.freeMemory()) + " / " + formatBytes(tm, rt.totalMemory()) + " ; " |
final long totalMemory = rt.totalMemory(); |
final String stats = "<i>" + tm.translate("memory.used") + " :</i> " + formatBytes(tm, totalMemory - rt.freeMemory()) + " / " + formatBytes(tm, totalMemory) + " ; " |
+ tm.translate("processors", rt.availableProcessors()); |
final String lafDesc = lookAndFeel == null ? tm.translate("no.laf") : getLink(lookAndFeel.getName(), lafURI, html) + ", " + lookAndFeel.getDescription(); |
URI propsURI = null; |
URI envURI = null; |
try { |
propsURI = new URI(PROPS_PROTOCOL, "/", null); |
envURI = new URI(ENV_PROTOCOL, "/", null); |
} catch (URISyntaxException e1) { |
e1.printStackTrace(); |
} |
final String propsDesc = envURI == null ? "" : lineBreak + getLink(tm.translate("properties.all"), propsURI, html) + ", " + getLink(tm.translate("env.all"), envURI, html); |
final String p = tm.translate("javaVersion", version, getLink(getProperty("java.vendor"), vendorURI, html)) + " ; " |
+ getLink(tm.translate("javaHome"), new File(getProperty("java.home")).toURI(), html) + lineBreak + stats + lineBreak + lafDesc; |
+ getLink(tm.translate("javaHome"), new File(getProperty("java.home")).toURI(), html) + lineBreak + stats + lineBreak + lafDesc + propsDesc; |
res.put(Info.JAVA, p); |
/trunk/OpenConcerto/src/org/openconcerto/utils/NetUtils.java |
---|
32,6 → 32,7 |
import java.util.LinkedHashMap; |
import java.util.Map; |
import java.util.Map.Entry; |
import java.util.regex.Pattern; |
import javax.net.ssl.HostnameVerifier; |
import javax.net.ssl.HttpsURLConnection; |
184,10 → 185,11 |
return content; |
} |
/** |
* Encode for POST message application/x-www-form-urlencoded |
*/ |
static public final String urlEncode(final String... kv) { |
return urlEncode(false, kv); |
} |
static public final String urlEncode(final boolean spaceAsPlus, final String... kv) { |
final int size = kv.length; |
if (size % 2 != 0) |
throw new IllegalArgumentException("Odd number of items : " + size); |
195,13 → 197,22 |
for (int i = 0; i < size; i += 2) { |
map.put(kv[i], kv[i + 1]); |
} |
return urlEncode(map); |
return urlEncode(map, spaceAsPlus); |
} |
/** |
* Encode for POST message application/x-www-form-urlencoded |
* Encode pairs in application/x-www-form-urlencoded format. Although both the |
* <a href="https://url.spec.whatwg.org/#urlencoded-serializing">URL standard</a> and the |
* <a href="https://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1">HTML standard</a> |
* specifies <code>spaceAsPlus</code> some applications expect the opposite (e.g. POST with |
* jetty). |
* |
* @param map the values to encode. |
* @param spaceAsPlus <code>true</code> to use '+' for a space, <code>false</code> to use '%20'. |
* @return the encoded string. |
* @see #percentEncode(String, String, boolean) |
*/ |
static public final String urlEncode(final Map<String, ?> map) { |
static public final String urlEncode(final Map<String, ?> map, final boolean spaceAsPlus) { |
if (map.isEmpty()) |
return ""; |
final String charset = StandardCharsets.UTF_8.name(); |
211,9 → 222,9 |
// Avoid null and "null" confusion. |
if (value != null) { |
try { |
sb.append(URLEncoder.encode(e.getKey(), charset).replace("+", "%20")); |
sb.append(percentEncode(e.getKey(), charset, spaceAsPlus)); |
sb.append('='); |
sb.append(URLEncoder.encode(String.valueOf(value), charset).replace("+", "%20")); |
sb.append(percentEncode(String.valueOf(value), charset, spaceAsPlus)); |
sb.append('&'); |
} catch (UnsupportedEncodingException exn) { |
throw new IllegalStateException("UTF-8 should be standard", exn); |
225,6 → 236,31 |
return sb.toString(); |
} |
static private final Pattern SPACE_PATTERN = Pattern.compile("+", Pattern.LITERAL); |
/** |
* Percent encode a string. |
* |
* @param s the string to encode. |
* @param charset the charset to use. |
* @param spaceAsPlus <code>true</code> to use '+' for a space (as per |
* <a href="https://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1">HTML</a> and |
* <a href= |
* "https://url.spec.whatwg.org/#urlencoded-serializing">application/x-www-form-urlencoded</a> |
* of URL Standard), <code>false</code> to use '%20' (as per |
* <a href="https://url.spec.whatwg.org/#path-state">Path part</a> of URL Standard). |
* @return the encoded string. |
* @throws UnsupportedEncodingException if charset is unknown. |
* @see <a href="https://url.spec.whatwg.org/#string-percent-encode-after-encoding">URL |
* standard</a> |
*/ |
static public final String percentEncode(final String s, final String charset, final boolean spaceAsPlus) throws UnsupportedEncodingException { |
final String withPlus = URLEncoder.encode(s, charset); |
if (spaceAsPlus) |
return withPlus; |
return SPACE_PATTERN.matcher(withPlus).replaceAll("%20"); |
} |
// Create a trust manager that does not validate certificate chains |
static private final TrustManager[] TRUSTALL_MANAGERS = new TrustManager[] { new X509TrustManager() { |
public java.security.cert.X509Certificate[] getAcceptedIssuers() { |
/trunk/OpenConcerto/src/org/openconcerto/utils/checks/MutableValueObject.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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,8 |
* Set the value as if this object was newly created. Ie if this object had a way of computing |
* its initial value, reuse it (eg setValue(currentTime)). |
*/ |
public void resetValue(); |
public default void resetValue() { |
this.setValue(null); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/StringUtils.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
21,6 → 21,7 |
import java.nio.charset.Charset; |
import java.text.Normalizer; |
import java.text.Normalizer.Form; |
import java.text.SimpleDateFormat; |
import java.util.ArrayList; |
import java.util.Collections; |
import java.util.HashMap; |
359,6 → 360,39 |
return res; |
} |
static private final Pattern singleQuote = Pattern.compile("'", Pattern.LITERAL); |
static public final Pattern SINGLE_QUOTED_PATTERN = Pattern.compile("'(('')|[^'])*'"); |
static private final Pattern twoSingleQuote = Pattern.compile("''", Pattern.LITERAL); |
/** |
* Quote a string with single quotes ( e.g. for {@link SimpleDateFormat}). |
* |
* @param s an arbitrary string, eg "salut\ l'ami". |
* @return the quoted form, eg "'salut\ l''ami'". |
*/ |
static public final String singleQuote(String s) { |
if (s.isEmpty()) |
return "''"; |
else |
return "'" + singleQuote.matcher(s).replaceAll("''") + "'"; |
} |
static public final String unSingleQuote(String s) { |
if (!SINGLE_QUOTED_PATTERN.matcher(s).matches()) |
throw new IllegalArgumentException("Invalid quoted string " + s); |
return unSingleQuoteUnsafe(s); |
} |
private static String unSingleQuoteUnsafe(String s) { |
return twoSingleQuote.matcher(s.substring(1, s.length() - 1)).replaceAll("'"); |
} |
static public final String unSingleQuote(Matcher m) { |
if (m.pattern() != SINGLE_QUOTED_PATTERN) |
throw new IllegalArgumentException("Matcher not from SINGLE_QUOTED_PATTERN : " + m); |
return unSingleQuoteUnsafe(m.group()); |
} |
static private final Pattern quotePatrn = Pattern.compile("\"", Pattern.LITERAL); |
static private final Pattern slashPatrn = Pattern.compile("(\\\\+)"); |
740,10 → 774,13 |
} |
/** |
* convert a byte array to its hexa representation |
* Convert a byte array to its hexa representation (uppercase) |
* |
* @param bytes to converts |
* @param size of the byte array to use |
* @return the hexa representation |
*/ |
public static String bytesToHexString(byte[] bytes) { |
final int length = bytes.length; |
public static String bytesToHexString(byte[] bytes, int length) { |
char[] hexChars = new char[length * 2]; |
for (int j = 0; j < length; j++) { |
int v = bytes[j] & 0xFF; |
753,7 → 790,25 |
return new String(hexChars); |
} |
public static String charToHex(char c) { |
int v = c & 0xFF; |
char[] hexChars = new char[2]; |
hexChars[0] = hexArray[v >>> 4]; |
hexChars[1] = hexArray[v & 0x0F]; |
return new String(hexChars); |
} |
/** |
* Convert a byte array to its hexa representation (uppercase) |
* |
* @param bytes to converts |
* @return the hexa representation |
*/ |
public static String bytesToHexString(byte[] bytes) { |
return bytesToHexString(bytes, bytes.length); |
} |
/** |
* Whether the parameter is empty. |
* |
* @param s the string to test. |
768,22 → 823,11 |
} |
/** |
* Return the first parameter that is non-empty. |
* |
* @param s1 the string to test. |
* @param s2 the alternate value. |
* @return <code>s1</code> if not <code>null</code> and not {@link String#isEmpty() empty}, |
* <code>s2</code> otherwise. |
*/ |
public static String coalesce(String s1, String s2) { |
return isEmpty(s1) ? s2 : s1; |
} |
/** |
* Return the first value that is non-empty. |
* |
* @param values values to test for emptiness. |
* @return the first value that is neither <code>null</code> nor {@link String#isEmpty() empty}. |
* @return the first value that is neither <code>null</code> nor {@link String#isEmpty() empty}, |
* <code>null</code> if none. |
*/ |
public static String coalesce(String... values) { |
return coalesce(false, values); |
790,9 → 834,10 |
} |
public static String coalesce(final boolean trim, String... values) { |
for (final String s : values) |
for (final String s : values) { |
if (!isEmpty(s, trim)) |
return s; |
} |
return null; |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/sync/SyncClient.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
18,6 → 18,7 |
import java.io.BufferedInputStream; |
import java.io.BufferedOutputStream; |
import java.io.ByteArrayInputStream; |
import java.io.ByteArrayOutputStream; |
import java.io.DataInputStream; |
import java.io.DataOutputStream; |
529,7 → 530,16 |
wr.close(); |
// Get the response ASAP in order to not block the server while computing locally hash256 |
DataInputStream in = new DataInputStream(new BufferedInputStream(conn.getInputStream())); |
// Copy the entire data first because DataInputStream will fail on partial buffer (mainly |
// readUTF) |
InputStream ins = conn.getInputStream(); |
final ByteArrayOutputStream out = new ByteArrayOutputStream(); |
StreamUtils.copy(ins, out); |
ins.close(); |
DataInputStream in = new DataInputStream(new ByteArrayInputStream(out.toByteArray())); |
int fileCount = in.readInt(); |
this.byteReceived += 4; |
ArrayList<FileProperty> list = new ArrayList<FileProperty>(); |
875,4 → 885,6 |
public void setVerifyHost(boolean verify) { |
this.verifyHost = verify; |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/sync/SimpleSyncClient.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
27,6 → 27,7 |
import java.io.IOException; |
import java.io.InputStream; |
import java.io.OutputStream; |
import java.io.Serializable; |
import java.nio.charset.StandardCharsets; |
import java.nio.file.Files; |
import java.nio.file.Path; |
52,15 → 53,15 |
public final class SimpleSyncClient extends HTTPClient { |
static protected final long getLong(final Object o) { |
protected static final long getLong(final Object o) { |
return ((Number) o).longValue(); |
} |
static protected final Instant getInstant(final Object o) { |
protected static final Instant getInstant(final Object o) { |
return Instant.ofEpochMilli(getLong(o)); |
} |
static protected abstract class BaseAttrs { |
public static abstract class BaseAttrs implements Serializable { |
private final String path; |
private final String name; |
private final Instant lastModified; |
90,8 → 91,8 |
} |
} |
static public final class DirAttrs extends BaseAttrs { |
static protected DirAttrs fromJSON(final String path, final JSONArray array) { |
public static final class DirAttrs extends BaseAttrs { |
protected static DirAttrs fromJSON(final String path, final JSONArray array) { |
return new DirAttrs(path, (String) array.get(0), getInstant(array.get(1))); |
} |
100,9 → 101,9 |
} |
} |
static public final class FileAttrs extends BaseAttrs { |
public static final class FileAttrs extends BaseAttrs { |
static protected FileAttrs fromJSON(final String path, final JSONArray array) { |
protected static FileAttrs fromJSON(final String path, final JSONArray array) { |
return new FileAttrs(path, (String) array.get(0), getInstant(array.get(2)), getLong(array.get(1)), (String) array.get(3)); |
} |
158,7 → 159,7 |
} |
} |
static public final class DirContent { |
public static final class DirContent { |
private final String path; |
private final JSONObject json; |
219,11 → 220,11 |
} |
@FunctionalInterface |
static public interface FileConsumer { |
public static interface FileConsumer { |
public void accept(FileAttrs attrs, InputStream fileStream) throws IOException; |
} |
static private final Set<Integer> GETFILE_OK_CODES = CollectionUtils.createSet(200, 404); |
private static final Set<Integer> GETFILE_OK_CODES = CollectionUtils.createSet(200, 404); |
public Response getFile(final String path, final String fileName, final FileConsumer fileConsumer) throws IOException { |
if (path == null) { |
246,6 → 247,26 |
return res; |
} |
public Integer getCounter(final String key) throws IOException { |
if (key == null) { |
throw new IllegalArgumentException("null key"); |
} |
final HttpsURLConnection con = openConnection("/getCounter"); |
send(con, NetUtils.urlEncode("key", key), false); |
final Response res = checkResponseCode(con, GETFILE_OK_CODES); |
if (res.getCode() == 200) { |
byte[] bytes = new byte[20]; |
try (final InputStream in = getInputStream(con)) { |
int r = in.read(bytes); |
String str = new String(bytes, 0, r); |
return Integer.parseInt(str); |
} |
} |
return null; |
} |
// ATTN contrary to other methods, the result isn't if the request was OK : it ignores |
// throwsException() and always throws. The return value is true if the file existed and was |
// saved. |
281,7 → 302,7 |
} |
public final Response renameFile(final String path, final String fileName, final String newFileName) throws IOException { |
return this.renameFile(path, fileName, null, newFileName); |
return this.renameFile(path, fileName, path, newFileName); |
} |
public final Response renameFile(final String path, final String fileName, final String newPath, final String newFileName) throws IOException { |
331,4 → 352,15 |
return checkResponseCode(send(con, ba.toByteArray(), true)); |
} |
public Response createDir(String path, String fileName) throws IOException { |
if (path == null) { |
throw new IllegalArgumentException("null path"); |
} |
if (fileName == null) { |
throw new IllegalArgumentException("null fileName"); |
} |
final HttpsURLConnection con = openConnection("/mkdir"); |
return checkResponseCode(send(con, NetUtils.urlEncode("rn", fileName, "rp", path), false)); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/jsonrpc/JSONRPCClient.java |
---|
New file |
0,0 → 1,125 |
/* |
* 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.jsonrpc; |
import org.openconcerto.utils.Base64; |
import org.openconcerto.utils.NetUtils; |
import java.io.IOException; |
import java.io.InputStream; |
import java.io.OutputStream; |
import java.net.HttpURLConnection; |
import java.net.URL; |
import java.nio.charset.StandardCharsets; |
import java.security.KeyManagementException; |
import java.security.NoSuchAlgorithmException; |
import java.util.UUID; |
import java.util.zip.GZIPInputStream; |
import javax.net.ssl.HttpsURLConnection; |
import net.minidev.json.JSONObject; |
import net.minidev.json.JSONStyle; |
import net.minidev.json.parser.JSONParser; |
import net.minidev.json.parser.ParseException; |
public class JSONRPCClient { |
private boolean trustAllContext; |
private String token; |
private String login; |
private String encodedPassword; |
public JSONRPCClient(boolean trustAllContext) { |
this.trustAllContext = trustAllContext; |
} |
public void setAuthToken(String token) { |
this.token = token; |
} |
/** |
* @param login : login |
* @param encodedPassword : Base64 encoded hash of the password |
*/ |
public void setCredentials(String login, String encodedPassword) { |
this.login = login; |
this.encodedPassword = encodedPassword; |
} |
public Object rpcCall(String url, String method, JSONObject params) throws IOException { |
final JSONObject request = new JSONObject(); |
request.put("jsonrpc", "2.0"); |
request.put("method", method); |
request.put("id", UUID.randomUUID().toString()); |
request.put("params", params); |
final HttpURLConnection con; |
if (url.startsWith("https")) { |
HttpsURLConnection cons = (HttpsURLConnection) new URL(url).openConnection(); |
if (this.trustAllContext) { |
try { |
cons.setSSLSocketFactory(NetUtils.createTrustAllContext().getSocketFactory()); |
cons.setHostnameVerifier(NetUtils.HostnameNonVerifier); |
} catch (KeyManagementException | NoSuchAlgorithmException e1) { |
throw new IOException(e1); |
} |
} |
con = cons; |
} else { |
con = (HttpURLConnection) new URL(url).openConnection(); |
} |
con.setRequestProperty("Accept-Encoding", "gzip"); |
con.setDoOutput(true); |
final String str = request.toJSONString(JSONStyle.NO_COMPRESS); |
final byte[] bytes = str.getBytes(StandardCharsets.UTF_8); |
con.setRequestProperty("Content-Type", "application/json; charset=utf-8"); |
if (this.token != null) { |
con.setRequestProperty("Authorization", "Bearer " + this.token); |
} else { |
// X_LOGIN and X_PASSWORD_HASH are Base64 encoded |
if (this.login != null) { |
con.setRequestProperty("X_LOGIN", Base64.encodeBytes(this.login.getBytes(StandardCharsets.UTF_8))); |
} |
if (this.encodedPassword != null) { |
con.setRequestProperty("X_PASSWORD_HASH", this.encodedPassword); |
} |
} |
try (final OutputStream out = con.getOutputStream()) { |
out.write(bytes); |
} |
final JSONParser p = new JSONParser(JSONParser.MODE_PERMISSIVE); |
try (InputStream inputStream = "gzip".equals(con.getContentEncoding()) ? new GZIPInputStream(con.getInputStream()) : con.getInputStream()) { |
final JSONObject response = (JSONObject) p.parse(inputStream); |
if (response.containsKey("error")) { |
final JSONObject jsonObject = (JSONObject) response.get("error"); |
if (jsonObject.getAsNumber("code").intValue() == -32601) { |
throw new IOException("method not found on server :" + method + " code : " + jsonObject.getAsNumber("code") + " : " + jsonObject.getAsString("message")); |
} else { |
throw new IOException("rpc call error, code : " + jsonObject.getAsNumber("code") + " : " + jsonObject.getAsString("message")); |
} |
} |
return response.get("result"); |
} catch (final ParseException e) { |
throw new IOException(e); |
} |
} |
} |
/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/SystemUtils.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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 java.util.Arrays; |
import java.util.Collections; |
import java.util.List; |
import java.util.function.Function; |
import java.util.regex.Pattern; |
public class SystemUtils { |
123,4 → 124,18 |
return true; |
} |
} |
static public final <T extends Enum<T>> T getEnumFromProperty(final String propName, final Class<T> clazz, final T def) { |
final String prop = System.getProperty(propName); |
if (prop == null) |
return def; |
return Enum.valueOf(clazz, propName); |
} |
static public final <T> T getProperty(final String propName, final Function<String, T> func, final T def) { |
final String prop = System.getProperty(propName); |
if (prop == null) |
return def; |
return func.apply(prop); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/CompareUtils.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
282,7 → 282,7 |
} |
}; |
static public final <T> boolean equals(List<T> l1, List<T> l2, Equalizer<? super T> comp) { |
static public final <T> boolean equals(List<? extends T> l1, List<? extends T> l2, Equalizer<? super T> comp) { |
return compare(l1, l2, comp, null) == null; |
} |
297,7 → 297,7 |
* @return <code>null</code> if the two lists are equal, otherwise a String explaining the |
* difference. |
*/ |
static public final <T> String compare(List<T> l1, List<T> l2, Equalizer<? super T> comp, final ITransformer<? super T, String> toString) { |
static public final <T> String compare(List<? extends T> l1, List<? extends T> l2, Equalizer<? super T> comp, final ITransformer<? super T, String> toString) { |
final int size = l1.size(); |
if (size != l2.size()) |
return "unequal size"; |
/trunk/OpenConcerto/src/org/openconcerto/utils/TinyMap.java |
---|
13,187 → 13,223 |
package org.openconcerto.utils; |
import java.util.AbstractMap.SimpleImmutableEntry; |
import java.util.AbstractMap; |
import java.util.AbstractSet; |
import java.util.ArrayList; |
import java.util.Collection; |
import java.util.HashSet; |
import java.util.ConcurrentModificationException; |
import java.util.Iterator; |
import java.util.Map; |
import java.util.NoSuchElementException; |
import java.util.Objects; |
import java.util.Set; |
import java.util.function.Function; |
public class TinyMap<K, V> implements Map<K, V> { |
private final ArrayList<K> keys; |
private final ArrayList<V> values; |
public class TinyMap<K, V> extends AbstractMap<K, V> { |
private static final Function<Entry<?, ?>, Object> KEY_GETTER = Entry::getKey; |
private static final Function<Entry<?, ?>, Object> VALUE_GETTER = Entry::getValue; |
private final ArrayList<Entry<K, V>> entries; |
private transient Set<Map.Entry<K, V>> entrySet; |
/** |
* The number of times this HashMap has been structurally modified Structural modifications are |
* those that change the number of mappings in the HashMap or otherwise modify its internal |
* structure (e.g., rehash). This field is used to make iterators on Collection-views of the |
* HashMap fail-fast. (See ConcurrentModificationException). |
*/ |
transient int modCount = 0; |
public TinyMap() { |
this(10); |
} |
public TinyMap(int initialCapacity) { |
keys = new ArrayList<K>(initialCapacity); |
values = new ArrayList<V>(initialCapacity); |
public TinyMap(final int initialCapacity) { |
this.entries = new ArrayList<>(initialCapacity); |
} |
public TinyMap(final Map<? extends K, ? extends V> m) { |
this.entries = new ArrayList<>(m.size()); |
// don't call putAll() to avoid indexOfKey() |
for (final Entry<? extends K, ? extends V> e : m.entrySet()) { |
this.entries.add(new SimpleEntry<>(e)); |
} |
++this.modCount; |
} |
@Override |
public int size() { |
return keys.size(); |
return this.entries.size(); |
} |
@Override |
public boolean isEmpty() { |
return keys.isEmpty(); |
return this.entries.isEmpty(); |
} |
@Override |
public boolean containsKey(Object key) { |
return keys.contains(key); |
public boolean containsKey(final Object key) { |
return indexOfKey(key) >= 0; |
} |
@Override |
public boolean containsValue(Object value) { |
return values.contains(value); |
public boolean containsValue(final Object value) { |
return this.indexOf(value, VALUE_GETTER) >= 0; |
} |
@Override |
public V get(Object key) { |
final int size = this.keys.size(); |
for (int i = 0; i < size; i++) { |
if (this.keys.get(i).equals(key)) { |
return this.values.get(i); |
private int indexOfKey(final Object key) { |
return this.indexOf(key, KEY_GETTER); |
} |
private final int indexOf(final Object o, final Function<Entry<?, ?>, Object> getter) { |
final int stop = this.entries.size(); |
for (int i = 0; i < stop; i++) { |
final Entry<K, V> e = this.entries.get(i); |
if (Objects.equals(o, getter.apply(e))) |
return i; |
} |
return -1; |
} |
@Override |
public V get(final Object key) { |
final int i = indexOfKey(key); |
if (i < 0) |
return null; |
return this.entries.get(i).getValue(); |
} |
@Override |
public V put(K key, V value) { |
final int size = this.keys.size(); |
for (int i = 0; i < size; i++) { |
if (this.keys.get(i).equals(key)) { |
final V old = this.values.get(i); |
this.values.set(i, value); |
return old; |
public V put(final K key, final V value) { |
final int i = this.indexOfKey(key); |
final V res; |
if (i < 0) { |
this.entries.add(new SimpleEntry<>(key, value)); |
++this.modCount; |
res = null; |
} else { |
res = this.entries.get(i).setValue(value); |
} |
return res; |
} |
this.keys.add(key); |
this.values.add(value); |
return null; |
} |
@Override |
public V remove(Object key) { |
final int size = this.keys.size(); |
for (int i = 0; i < size; i++) { |
if (this.keys.get(i).equals(key)) { |
this.keys.remove(i); |
return this.values.remove(i); |
public V remove(final Object key) { |
return this.remove(indexOfKey(key)); |
} |
} |
private V remove(final int i) { |
if (i < 0) |
return null; |
final Entry<K, V> res = this.entries.remove(i); |
++this.modCount; |
return res.getValue(); |
} |
@Override |
public void putAll(Map<? extends K, ? extends V> m) { |
final Set<? extends K> keySet = m.keySet(); |
for (Iterator<? extends K> iterator = keySet.iterator(); iterator.hasNext();) { |
K key = (K) iterator.next(); |
put(key, m.get(key)); |
public boolean remove(final Object key, final Object value) { |
final int i = indexOfKey(key); |
if (i < 0) |
return false; |
final boolean eqVal = Objects.equals(this.entries.get(i).getValue(), value); |
if (eqVal) { |
this.remove(i); |
} |
return eqVal; |
} |
@Override |
public void clear() { |
this.keys.clear(); |
this.values.clear(); |
this.entries.clear(); |
++this.modCount; |
} |
// Views |
@Override |
public Set<K> keySet() { |
return new HashSet<K>(this.keys) { |
public Set<Map.Entry<K, V>> entrySet() { |
Set<Map.Entry<K, V>> es; |
return (es = this.entrySet) == null ? (this.entrySet = new EntrySet()) : es; |
} |
final class EntrySet extends AbstractSet<Map.Entry<K, V>> { |
@Override |
public boolean remove(Object o) { |
TinyMap.this.remove(o); |
return super.remove(o); |
public final int size() { |
return TinyMap.this.size(); |
} |
@Override |
public void clear() { |
clear(); |
super.clear(); |
public final void clear() { |
TinyMap.this.clear(); |
} |
}; |
} |
@Override |
public Collection<V> values() { |
public final Iterator<Map.Entry<K, V>> iterator() { |
return new Iterator<Map.Entry<K, V>>() { |
return new ArrayList<V>(this.values()) { |
private int expectedModCount = TinyMap.this.modCount; |
/** |
* The index last returned, i.e. initial value just before first item. |
* |
* <pre> |
* a b c |
* -1 0 1 2 |
* </pre> |
*/ |
private int currentPos = -1; |
private Entry<K, V> lastReturned = null; |
@Override |
public V remove(int index) { |
keys.remove(index); |
values.remove(index); |
return super.remove(index); |
public boolean hasNext() { |
final int nextIndex = this.currentPos + 1; |
return nextIndex < size(); |
} |
@Override |
public boolean remove(Object o) { |
int index = values.indexOf(o); |
if (index >= 0) { |
keys.remove(index); |
values.remove(index); |
public Entry<K, V> next() { |
checkForComodification(); |
if (!hasNext()) |
throw new NoSuchElementException(); |
this.currentPos++; |
final Entry<K, V> res = TinyMap.this.entries.get(this.currentPos); |
this.lastReturned = res; |
return res; |
} |
return super.remove(o); |
} |
@Override |
public void clear() { |
clear(); |
super.clear(); |
public void remove() { |
checkForComodification(); |
if (this.lastReturned == null) |
throw new IllegalStateException(); |
TinyMap.this.remove(this.currentPos); |
this.currentPos--; |
// per doc : cannot be called twice |
this.lastReturned = null; |
this.expectedModCount = TinyMap.this.modCount; |
} |
}; |
} |
@Override |
public Set<Entry<K, V>> entrySet() { |
final Set<Entry<K, V>> set = new HashSet<Map.Entry<K, V>>() { |
@Override |
public boolean remove(Object o) { |
Map.Entry<K, V> entry = (Map.Entry<K, V>) o; |
int index = values.indexOf(entry.getValue()); |
if (index >= 0) { |
keys.remove(index); |
values.remove(index); |
final void checkForComodification() { |
if (TinyMap.this.modCount != this.expectedModCount) |
throw new ConcurrentModificationException(); |
} |
return super.remove(o); |
}; |
} |
@Override |
public boolean removeAll(Collection<?> c) { |
for (Iterator iterator = c.iterator(); iterator.hasNext();) { |
Entry<K, V> entry = (Entry<K, V>) iterator.next(); |
int index = values.indexOf(entry.getValue()); |
if (index >= 0) { |
keys.remove(index); |
values.remove(index); |
public final boolean contains(final Object o) { |
if (!(o instanceof Map.Entry)) |
return false; |
final Map.Entry<?, ?> e = (Map.Entry<?, ?>) o; |
return TinyMap.this.entries.contains(e); |
} |
} |
return super.removeAll(c); |
} |
@Override |
public void clear() { |
clear(); |
super.clear(); |
public final boolean remove(final Object o) { |
if (o instanceof Map.Entry) { |
final Map.Entry<?, ?> e = (Map.Entry<?, ?>) o; |
final Object key = e.getKey(); |
final Object value = e.getValue(); |
return TinyMap.this.remove(key, value); |
} |
}; |
final int size = this.keys.size(); |
for (int i = 0; i < size; i++) { |
set.add(new SimpleImmutableEntry<K, V>(this.keys.get(i), this.values.get(i))); |
return false; |
} |
return set; |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/reentrantEventsNaive.seq.violet.html |
---|
New file |
0,0 → 1,800 |
<HTML> |
<HEAD> |
<META name="description" |
content="Violet UML Editor cross format document" /> |
<META name="keywords" content="Violet, UML" /> |
<META charset="UTF-8" /> |
<SCRIPT type="text/javascript"> |
function switchVisibility() { |
var obj = document.getElementById("content"); |
obj.style.display = (obj.style.display == "block") ? "none" : "block"; |
} |
</SCRIPT> |
</HEAD> |
<BODY> |
This file was generated with Violet UML Editor 3.0.0. |
( <A href=# onclick="switchVisibility()">View Source</A> / <A href="http://sourceforge.net/projects/violet/files/violetumleditor/" target="_blank">Download Violet</A> ) |
<BR /> |
<BR /> |
<SCRIPT id="content" type="text/xml"><![CDATA[<SequenceDiagramGraph id="1"> |
<nodes id="2"> |
<LifelineNode id="3"> |
<id id="4" value="72e47375-469f-4a88-abb6-e40e46c324ce"/> |
<revision>0</revision> |
<children id="5"> |
<ActivationBarNode id="6"> |
<id id="7" value="a94abf1e-083c-482c-8940-d77d8a5e62a3"/> |
<revision>1</revision> |
<children id="8"/> |
<parent class="LifelineNode" reference="3"/> |
<location class="Point2D.Double" id="9" x="112.5" y="75.0"/> |
<backgroundColor id="10"> |
<red>255</red> |
<green>255</green> |
<blue>255</blue> |
<alpha>255</alpha> |
</backgroundColor> |
<borderColor id="11"> |
<red>191</red> |
<green>191</green> |
<blue>191</blue> |
<alpha>255</alpha> |
</borderColor> |
<textColor id="12"> |
<red>51</red> |
<green>51</green> |
<blue>51</blue> |
<alpha>255</alpha> |
</textColor> |
</ActivationBarNode> |
</children> |
<location class="Point2D.Double" id="13" x="50.0" y="0.0"/> |
<textColor reference="12"/> |
<name id="14"> |
<text>evt 1</text> |
</name> |
<type id="15"> |
<text>SQLElementEvent</text> |
</type> |
<endOfLife>true</endOfLife> |
</LifelineNode> |
<LifelineNode id="16"> |
<id id="17" value="2da76d69-e422-46b0-a548-1d7b83093d9f"/> |
<revision>1</revision> |
<children id="18"> |
<ActivationBarNode id="19"> |
<id id="20" value="030a1385-4bfe-4889-8ec1-ffa386c5db22"/> |
<revision>0</revision> |
<children id="21"/> |
<parent class="LifelineNode" reference="16"/> |
<location class="Point2D.Double" id="22" x="112.5" y="110.0"/> |
<backgroundColor reference="10"/> |
<borderColor reference="11"/> |
<textColor id="23"> |
<red>51</red> |
<green>51</green> |
<blue>51</blue> |
<alpha>255</alpha> |
</textColor> |
</ActivationBarNode> |
</children> |
<location class="Point2D.Double" id="24" x="980.0" y="10.0"/> |
<textColor reference="23"/> |
<name id="25"> |
<text>evt 2</text> |
</name> |
<type id="26"> |
<text>SQLElementEvent</text> |
</type> |
<endOfLife>true</endOfLife> |
</LifelineNode> |
<LifelineNode id="27"> |
<id id="28" value="d3539181-e292-45d4-92ee-bb25e1cd9aaf"/> |
<revision>1</revision> |
<children id="29"> |
<ActivationBarNode id="30"> |
<id id="31" value="4f050be9-7427-4789-b824-b1f78f32cd07"/> |
<revision>0</revision> |
<children id="32"/> |
<parent class="LifelineNode" reference="27"/> |
<location class="Point2D.Double" id="33" x="133.0" y="90.0"/> |
<backgroundColor reference="10"/> |
<borderColor reference="11"/> |
<textColor reference="23"/> |
</ActivationBarNode> |
<ActivationBarNode id="34"> |
<id id="35" value="3af702fc-e885-4000-b97b-bd4921b5fc56"/> |
<revision>0</revision> |
<children id="36"/> |
<parent class="LifelineNode" reference="27"/> |
<location class="Point2D.Double" id="37" x="133.0" y="140.0"/> |
<backgroundColor reference="10"/> |
<borderColor reference="11"/> |
<textColor reference="23"/> |
</ActivationBarNode> |
</children> |
<location class="Point2D.Double" id="38" x="370.0" y="10.0"/> |
<textColor reference="23"/> |
<name id="39"> |
<text>l1</text> |
</name> |
<type id="40"> |
<text>SQLElementEventListener</text> |
</type> |
<endOfLife>false</endOfLife> |
</LifelineNode> |
<LifelineNode id="41"> |
<id id="42" value="66c2c3a4-4956-4a4b-b819-07da280e5582"/> |
<revision>1</revision> |
<children id="43"> |
<ActivationBarNode id="44"> |
<id id="45" value="bf8e10a3-b20f-4568-af06-4c8ffe13924e"/> |
<revision>0</revision> |
<children id="46"/> |
<parent class="LifelineNode" reference="41"/> |
<location class="Point2D.Double" id="47" x="133.0" y="200.0"/> |
<backgroundColor reference="10"/> |
<borderColor reference="11"/> |
<textColor reference="23"/> |
</ActivationBarNode> |
<ActivationBarNode id="48"> |
<id id="49" value="ac5b4a69-b3fa-4a3b-8c4a-570c49d3b4cb"/> |
<revision>0</revision> |
<children id="50"/> |
<parent class="LifelineNode" reference="41"/> |
<location class="Point2D.Double" id="51" x="133.0" y="310.0"/> |
<backgroundColor reference="10"/> |
<borderColor reference="11"/> |
<textColor reference="23"/> |
</ActivationBarNode> |
</children> |
<location class="Point2D.Double" id="52" x="680.0" y="0.0"/> |
<textColor reference="23"/> |
<name id="53"> |
<text>l2</text> |
</name> |
<type id="54"> |
<text>SQLElementEventListener</text> |
</type> |
<endOfLife>false</endOfLife> |
</LifelineNode> |
<NoteNode id="55"> |
<id id="56" value="d4154bcf-2a20-4ffc-aaf5-43d47a5a34f7"/> |
<revision>1</revision> |
<children id="57"/> |
<location class="Point2D.Double" id="58" x="730.0" y="410.0"/> |
<backgroundColor id="59"> |
<red>254</red> |
<green>222</green> |
<blue>188</blue> |
<alpha>255</alpha> |
</backgroundColor> |
<borderColor id="60"> |
<red>253</red> |
<green>186</green> |
<blue>113</blue> |
<alpha>255</alpha> |
</borderColor> |
<textColor id="61"> |
<red>51</red> |
<green>51</green> |
<blue>51</blue> |
<alpha>255</alpha> |
</textColor> |
<text id="62"> |
<text>l2 is notified of evt 2 before evt 1</text> |
</text> |
</NoteNode> |
</nodes> |
<edges id="63"> |
<SynchronousCallEdge id="64"> |
<id id="65" value="69d0c441-b334-4eb1-8b1e-89a9d77ad913"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="6"/> |
<startLocation class="Point2D.Double" id="66" x="7.5" y="15.0"/> |
<endNode class="ActivationBarNode" reference="30"/> |
<endLocation class="Point2D.Double" id="67" x="150.0" y="90.0"/> |
<transitionPoints id="68"/> |
<backgroundColor id="69"> |
<red>255</red> |
<green>255</green> |
<blue>255</blue> |
<alpha>255</alpha> |
</backgroundColor> |
<borderColor id="70"> |
<red>191</red> |
<green>191</green> |
<blue>191</blue> |
<alpha>255</alpha> |
</borderColor> |
<textColor id="71"> |
<red>51</red> |
<green>51</green> |
<blue>51</blue> |
<alpha>255</alpha> |
</textColor> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>0</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>3</selectedEndArrowhead> |
<startLabel id="72"> |
<text></text> |
</startLabel> |
<centerLabel id="73"> |
<text>modification(evt1)</text> |
</centerLabel> |
<endLabel id="74"> |
<text></text> |
</endLabel> |
</SynchronousCallEdge> |
<SynchronousCallEdge id="75"> |
<id id="76" value="2a43a89d-a435-46ef-ba93-fe869e1af5f0"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="30"/> |
<startLocation class="Point2D.Double" id="77" x="7.0" y="20.0"/> |
<endNode class="ActivationBarNode" reference="19"/> |
<endLocation class="Point2D.Double" id="78" x="120.0" y="100.0"/> |
<transitionPoints id="79"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>0</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>3</selectedEndArrowhead> |
<startLabel id="80"> |
<text></text> |
</startLabel> |
<centerLabel id="81"> |
<text>fire</text> |
</centerLabel> |
<endLabel id="82"> |
<text></text> |
</endLabel> |
</SynchronousCallEdge> |
<SynchronousCallEdge id="83"> |
<id id="84" value="0c060aa6-6c2b-4238-a9ae-0b4417cdcbda"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="19"/> |
<startLocation class="Point2D.Double" id="85" x="7.5" y="20.0"/> |
<endNode class="ActivationBarNode" reference="34"/> |
<endLocation class="Point2D.Double" id="86" x="130.0" y="130.0"/> |
<transitionPoints id="87"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>0</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>3</selectedEndArrowhead> |
<startLabel id="88"> |
<text></text> |
</startLabel> |
<centerLabel id="89"> |
<text>modification(evt2)</text> |
</centerLabel> |
<endLabel id="90"> |
<text></text> |
</endLabel> |
</SynchronousCallEdge> |
<SynchronousCallEdge id="91"> |
<id id="92" value="7866f976-b5c5-4dff-b55e-817e0acd0851"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="19"/> |
<startLocation class="Point2D.Double" id="93" x="7.5" y="70.0"/> |
<endNode class="ActivationBarNode" reference="44"/> |
<endLocation class="Point2D.Double" id="94" x="140.0" y="200.0"/> |
<transitionPoints id="95"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>0</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>3</selectedEndArrowhead> |
<startLabel id="96"> |
<text></text> |
</startLabel> |
<centerLabel id="97"> |
<text>modification(evt2)</text> |
</centerLabel> |
<endLabel id="98"> |
<text></text> |
</endLabel> |
</SynchronousCallEdge> |
<ReturnEdge id="99"> |
<id id="100" value="8a1ad364-cc2f-43ce-99d4-77158785be3d"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="19"/> |
<startLocation class="Point2D.Double" id="101" x="7.5" y="130.0"/> |
<endNode class="ActivationBarNode" reference="30"/> |
<endLocation class="Point2D.Double" id="102" x="7.0" y="160.0"/> |
<transitionPoints id="103"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>1</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>1</selectedEndArrowhead> |
<startLabel id="104"> |
<text></text> |
</startLabel> |
<centerLabel id="105"> |
<text></text> |
</centerLabel> |
<endLabel id="106"> |
<text></text> |
</endLabel> |
</ReturnEdge> |
<ReturnEdge id="107"> |
<id id="108" value="5e7f4a16-1fc3-4fc6-9dac-1d228ab268f8"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="34"/> |
<startLocation class="Point2D.Double" id="109" x="7.0" y="20.0"/> |
<endNode class="ActivationBarNode" reference="19"/> |
<endLocation class="Point2D.Double" id="110" x="7.5" y="60.0"/> |
<transitionPoints id="111"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>1</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>1</selectedEndArrowhead> |
<startLabel id="112"> |
<text></text> |
</startLabel> |
<centerLabel id="113"> |
<text></text> |
</centerLabel> |
<endLabel id="114"> |
<text></text> |
</endLabel> |
</ReturnEdge> |
<ReturnEdge id="115"> |
<id id="116" value="5fe641a1-312d-4394-96df-a684b18f6a1e"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="44"/> |
<startLocation class="Point2D.Double" id="117" x="7.0" y="30.0"/> |
<endNode class="ActivationBarNode" reference="19"/> |
<endLocation class="Point2D.Double" id="118" x="7.5" y="110.0"/> |
<transitionPoints id="119"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>1</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>1</selectedEndArrowhead> |
<startLabel id="120"> |
<text></text> |
</startLabel> |
<centerLabel id="121"> |
<text></text> |
</centerLabel> |
<endLabel id="122"> |
<text></text> |
</endLabel> |
</ReturnEdge> |
<ReturnEdge id="123"> |
<id id="124" value="5769cc8e-d632-4921-9aea-5d2e0e15d351"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="30"/> |
<startLocation class="Point2D.Double" id="125" x="7.0" y="170.0"/> |
<endNode class="ActivationBarNode" reference="6"/> |
<endLocation class="Point2D.Double" id="126" x="7.5" y="185.0"/> |
<transitionPoints id="127"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>1</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>1</selectedEndArrowhead> |
<startLabel id="128"> |
<text></text> |
</startLabel> |
<centerLabel id="129"> |
<text></text> |
</centerLabel> |
<endLabel id="130"> |
<text></text> |
</endLabel> |
</ReturnEdge> |
<SynchronousCallEdge id="131"> |
<id id="132" value="1334344f-9a27-4aff-b5d4-5ddfda00b259"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="6"/> |
<startLocation class="Point2D.Double" id="133" x="7.5" y="215.0"/> |
<endNode class="ActivationBarNode" reference="48"/> |
<endLocation class="Point2D.Double" id="134" x="140.0" y="290.0"/> |
<transitionPoints id="135"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>0</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>3</selectedEndArrowhead> |
<startLabel id="136"> |
<text></text> |
</startLabel> |
<centerLabel id="137"> |
<text>modification(evt1)</text> |
</centerLabel> |
<endLabel id="138"> |
<text></text> |
</endLabel> |
</SynchronousCallEdge> |
<ReturnEdge id="139"> |
<id id="140" value="397b25a5-6d9e-43d2-9c80-f1ec604ef31c"/> |
<revision>1</revision> |
<startNode class="ActivationBarNode" reference="48"/> |
<startLocation class="Point2D.Double" id="141" x="7.0" y="30.0"/> |
<endNode class="ActivationBarNode" reference="6"/> |
<endLocation class="Point2D.Double" id="142" x="7.5" y="255.0"/> |
<transitionPoints id="143"/> |
<backgroundColor reference="69"/> |
<borderColor reference="70"/> |
<textColor reference="71"/> |
<selectedBentStyle>1</selectedBentStyle> |
<selectedLineStyle>1</selectedLineStyle> |
<selectedStartArrowhead>0</selectedStartArrowhead> |
<selectedEndArrowhead>1</selectedEndArrowhead> |
<startLabel id="144"> |
<text></text> |
</startLabel> |
<centerLabel id="145"> |
<text></text> |
</centerLabel> |
<endLabel id="146"> |
<text></text> |
</endLabel> |
</ReturnEdge> |
</edges> |
</SequenceDiagramGraph>]]></SCRIPT> |
<BR /> |
<BR /> |
<IMG alt="embedded diagram image" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAABJQAAAHDCAIAAAD9e9f6AABLXklEQVR4Xu3d7ZscV33n/zxP/oA8 |
C8/zUDYxtgO2g43txDiA8cbceSGQJb84sF7uQkiWkIQENkuW+PczBLi4AiGEu2Agu5hkCWhmNLeS |
LM8wI41lyyNL1vgGjSVL49GMpJme35cpVDqqqu4+n65T1ae73q+rL1/Vpz91uqrnfNX9dU/3/MJu |
YOhsN0b2zAGUkC2w0rJ3AKCEbIF5yE4BDL5f2N1TMQDRatSSbtTJApWqopqqmBNopt6qqbe9gGjR |
vGEINWpJN+pkgUpVUU1VzAk0U2/V1NteQLRo3jCEGrWkG3WyQKWqqKYq5gSaqbdq6m0vIFo0bxhC |
jVrSjTpZoFJVVFMVcwLN1Fs19bYXEC2aNwyhRi3pRp0sUKkqqqmKOYFm6q2aetsLiBbNG4ZQo5Z0 |
o04WqFQV1VTFnEAz9VZNve0FRIvmDUOoUUu6UScLVKqKaqpiTqCZequm3vYCokXzhiHUqCXdqJMF |
KlVFNVUxJ9BMvVVTb3sB0aJ5wxBq1JJu1MkClaqimqqYE2im3qqpt72AaNG8YQg1akk36mSBSlVR |
TVXMCTRTb9XU215AtGjeMIQataQbdbJApaqopirmBJqpt2rqbS8gWjRvGEKNWtKNOlmgUlVUUxVz |
As3UWzX1thcQLZo3DKFGLelGnSxQqSqqqYo5gWbqrZp62wuIFs0bhlCjlnSjThaoVBXVVMWcQDP1 |
Vk297QVEi+YNQ6hRS7pRJwtUqopqqmJOoJl6q6be9gKiRfOGIdSoJd2okwUqVUU1VTEn0Ey9VVNv |
ewHRonnDEGrUkm7UyQKVqqKaqpgTaKbeqqm3vYBo0bxhCDVqSTfqZIFKVVFNVcwJNFNv1dTbXkC0 |
aN4whBq1pBt1skClqqimKuYEmqm3auptLyBaNG8YQo1a0o06WaBSVVRTFXMCzdRbNfW2FxAtmjcM |
oUYt6UadLFCpKqqpijmBZuqtmnrbC4gWzVt9du3IjqICjVrSPifbnLXXnDONxJA94D7VpPKZc8ge |
xg6ac6aRGLIH3Kea8nrbC6ohW2wxq6958/yhHj169B/+4R/uvffeq666yiff1cLCwv3333/77bdf |
d911d91111/+5V8eOnQok1ldXf3c5z73O7/zO9dff73F7r777gcffPDMmTOZWOdTSG7NywScPWJU |
eJCXT+ZKmVilpHusZ0lHwudkM48eVdZfhQd5+WSulIlVqvAeCwdTnW9Necb6zqeaVD5zuo/PY489 |
9slPfvKNb3zjtddeazX1vve97+DBg1fGZVSopPAgL5/MlTKxShXeY+FgqvOtKc9Y3/lUU15ve6l8 |
HkOqu+8KD/LyyVwpE6uUdI/RNW+XHzO/fGc//vGPr7766sycmWmXlpZ+8zd/M5vYteu222574okn |
3GQy7o64rtz7skzA2SNGhQd5+WSulIlVSrrHepZ0JHxONvPoXf4RXjneG6pMVXiQl0/mSplYpQrv |
sXBQFWSSGvhUk8pnTvfxSbZdVl8/+tGPrtxDQIWqCg/y8slcKROrVOE9Fg6qgkxSA59qyuttL5XP |
Y5hkXFR3zQoP8vLJXCkTq5R0j9E1b2984xs/97nPPfnkk575zmw2m+S+++47fPjw+fPnl5eXH374 |
4XvvvTcNrK+vv+51r7PMPffcMzExsba2du7cuampKbtqg3fccYeNpOHOh9T51m2PQAwKD7JwsGbS |
MdSzpCPhc7KZR48q66/CgywcrFnhMRQOqoJMUgOfalL5zOk+Pm95y1seeuihp556amNjw2rq7W9/ |
u930hje84co9BFSoqvAgCwdrVngMhYOqIJPUwKea8nrbS+XzGFLdfVd4kIWDNZOOQWjevv/977/r |
Xe961atedc0119x5551/93d/t7q6auOnT5+2EWv9V1ZW3LytmFe+8pU333zzxYsXk2NyuclCnrHO |
XvGKV9gkL7zwQvaGS/75n//ZAnfddZe7fI1dtXKym7785S+ng50PqfOt20WBdg9pIsnbkfzZn/2Z |
PZK33HKLHa2Nnzlz5k/+5E9uuOGGm2666cEHH2y1Wpdn9JvTfjQf+9jHbE6b4YEHHkhnSG51uePp |
JC6fn34yUsWBteO5pIeDz8m2e9DajUuossI51cXsbmf0t8rc7Tz3Vns58tnPfvb1r3/9ddddd+ut |
t77nPe/Zv39/mnGlu5c54ITPDHZgn/zkJ+2BevnLX+7sWsCnmlQ+c2YeFpe9GrObrMqyN3ijQgvn |
bLeukltd7ng6iYsKLTzghM8MVVeo517tDtXn53v5sbvETbZDde+iup1bXemOhbyaN5vdHsfsxLt2 |
Wcf/0ksvWeDDH/6wXf3qV7/q7vXd737XBj/96U9v64e13fGhTHXN2Hq1wBe+8AV79LO37bAH2gL/ |
/u//nr1he9sG7aZ3vOMd6Ujnu+t86/aVga4PaZp///vf7wZGR0ff9ra3uSO2XNQ5P/jBD7qBb3zj |
G+6tLnc82c7r+tOv7sDa6bqkh4nPybZ70NqNu7pmqLJdRXOqi9ndzutjlbnbee6tH/3oRy/vfEma |
yQyWP2D/GT7wgQ8kG1dddVUy3o5PNal85kwOLzu649SpU3bT3Xffnb3hkg77JqjQXUVztltX7mDC |
HU+286jQVIQV2nWvrofa9eeb3bP9T8RFde+iup1bXekdFfJq3r7zne/s2vnt2B/+8Ie2PjY2Nubm |
5t761rfaoDWOFti7d69tv/nNb3b3SgJLS0vJVZ+jcfnku2aSpZmwPth+AA8//HDaJZsbb7xxV5v/ |
aWFNtt1kgXSk892ld5SRCSTbXR/SNH/PPfc8/vjj9uP/xCc+YVetp8+MpIXnP6f9pI4cObK6uvrx |
j3/crlomuTUNpFfdwbzk1q4//eoOrJ2uS3qY+Jxsu0ev3bira4YqK5xTXczJYF5yax+rrHAw5d56 |
ww032PY//dM/nTlzZm1t7cCBA/fdd18+lih/wP4zWGZ8fPzcuXPJYAc+1aTymTM5zuzoDjsXu+mL |
X/xi9oZLOuyboEIL52y3rtJAetUdzEtupULzB+w/Q9UV2nWvrofa9ee7XfQYdkV176K6L3HvsSuv |
5u3ee++1GR999FF38MSJEzb4ute9bnun6Ux+pzZdxIuLi3b17W9/e5qXDmtbz7ezb9+++++///rr |
r08m3LWzUE5f+l8UL3/5y21kc3Pzyp1+Jnkf3H07u/MhpfNnZALJdteHdPtS/ic/+Uly9eTJk4Uj |
N998c3LVf875+fnk6k9/+lO7ag9Omk8C6VV3MC+5tetPv7oDa6frkh4mPifb7tFrN66iyvJzqos5 |
GcxLbu1jlRUOptxbkyN873vf+/nPf/6RRx7Z2toqjCXKH7D/DD/+8Y/dTAc+1aTymTM5zuzo9vYP |
fvCDq666yk7HXg1kb1NQofk5262rNJBedQfzklup0O3cAfvPUHWFdt2r66F2/fluFz2GnVHdSZ7q |
dgPp1c52+zRv1113XTJp3jXXXJNkvvzlL9vVBx98MLma9JTf+9730kmSfHq1KzXfmf0j9eSTT37z |
m9+84447bFo7vGS8w/+TsMFdO5/mTEc6H1LnW7evDPg8pMnV8+fPJ1dt9RSOpL9mUGbO5GoaSK92 |
GHR1/ulXd2DtdF3Sw8TnZNs9eu3Ge0OVdZ4zuZoG0qsdBl39qrLCwZR768zMjD3LJiPmt3/7tw8f |
PpyPJcofsP8M+W/EbsenmlQ+cybHmRm013ZXX321PbtnvhGuZ1Ro5zmTq2kgvdph0EWFRluhXffy |
OdTOP9/tosewA6o7zefXD9XdlVfzdu211yaTFkoyp06dskO0RWAHtLa29us7/L/EJk/Ne1paWrJp |
b7/99uTqO9/5zl0dfxv4/vvvT0c6H1LnW7evDPg8pO62z0iQOfOBdoOuzj/96g6sna5Lepj4nGy7 |
R6/deElUWbpdOJIPtBt09avKCgdTmVs3NjbGx8c/9alP3XrrrTZuP/rC2HaIA+5ths58qknlM2f+ |
OB966KGrd4yMjLjjQVCh6XbhSD7QbtBFhWZGepuhM59qyuu6l8+hdv75bivnQnXn8z4jQebMB9oN |
ujr/9Ks7sHa8mrfkFze7/iXBD33oQ7t23jf8l3/5F9v42Mc+5t6a/Dlg9236zqTT8Je8U5m2wl/9 |
6ld37XymMPPL1nb1jTtfwLpnz550sPMhdb51+8qAz0Oan7DzSJA5C39M+b3yOvz0qzuwdrou6WHi |
c7L5h7fzeElUWYcZttss5vxeeX2psvxerna3Jr/uct111yVX8zOXP+DeZujMp5pUPnNmjjMpGXvQ |
3P+vHxAV2mGG7aLlup3LFKJC3ZHeZujMp5ryuu7lc6jbHX++20WPYSGqO5WfsPNIkDkLf0z5vfI6 |
/PSrO7B2dvs0b7bCbMZbbrnlO9/5zvLy8sbGxurq6uHDh7/+9a+/6U1vSmMzMzO7dt6rTf58xNzc |
nDPH9k033WSDk5OTnkeWP88e2JHYwrVDtQO2FtkOKfniHVuyScCW72tf+9pdOx8inJqasqvr6+t2 |
InZ118534LScryjtfEidb92+MuDzkOYn7DwSZM7CH1N+r7wOP/3qDqydrkt6mPicbP7h7TwuocrU |
OQsXc36vvL5UWX4vl3vr7/3e742Pj58+fdqWwbe+9a1dzqcp8jOXP+DeZujMp5pUPnO6x/nFL35x |
185ru29/+9tXpnpEhapz5pdrPlOICnVHepuhM59qyuu6l8+hbnf8+W4XPYZ5VHfnn37nkSBzFv6Y |
8nvldfjpV3dg7ez2ad7M3/zN3yR3k5dmbAXceeedyR+dyP/Nwcz3Y2ZuTbkZVza3o8NNiSvn+Lmr |
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 |
S3a9ArjS/w9VgMdaZn7aJwAAAABJRU5ErkJg" /> |
</BODY> |
</HTML> |
/trunk/OpenConcerto/src/org/openconcerto/utils/FileUtils.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
277,6 → 277,7 |
* @return the relative path, eg "../x/y.txt". |
* @throws IOException if an error occurs while canonicalizing the files. |
* @throws IllegalArgumentException if fromDir exists and is not directory. |
* @see {@link Path#relativize(Path)} |
*/ |
public static final String relative(File fromDir, File to) throws IOException { |
if (fromDir.exists() && !fromDir.isDirectory()) |
/trunk/OpenConcerto/src/org/openconcerto/utils/prog/ExtractFromBEncoding.java |
---|
New file |
0,0 → 1,234 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific |
* language governing permissions and limitations under the License. |
* |
* When distributing the software, include this License Header Notice in each file. |
*/ |
package org.openconcerto.utils.prog; |
import org.openconcerto.utils.CollectionUtils; |
import org.openconcerto.utils.text.CSVReader; |
import java.io.IOException; |
import java.io.Writer; |
import java.nio.file.CopyOption; |
import java.nio.file.Files; |
import java.nio.file.OpenOption; |
import java.nio.file.Path; |
import java.nio.file.Paths; |
import java.nio.file.StandardCopyOption; |
import java.nio.file.StandardOpenOption; |
import java.util.ArrayList; |
import java.util.Arrays; |
import java.util.Collections; |
import java.util.HashSet; |
import java.util.List; |
import java.util.Map; |
import java.util.Set; |
import java.util.regex.Pattern; |
import javax.mail.internet.InternetAddress; |
import javax.mail.internet.MimeUtility; |
import javax.mail.internet.ParseException; |
import com.zimbra.common.util.BEncoding; |
import com.zimbra.common.util.BEncoding.BEncodingException; |
/** |
* Load a CSV file and replace some {@link BEncoding zimbra benconded map} columns by some of their |
* toplevel values. Sample SQL to generate the CSV : |
* |
* <pre> |
*SELECT 'ID', 'sujet', 'date', 'metadonnées' |
UNION ALL |
SELECT id, subject, from_unixtime(floor(date)), metadata |
FROM mail_item |
WHERE mailbox_id=70 and type=5 |
ORDER BY date DESC |
INTO OUTFILE '/tmp/zimbraMails.csv' FIELDS TERMINATED BY ',' ENCLOSED BY '\"' ESCAPED BY '\\' LINES TERMINATED BY '\n' ; |
* </pre> |
* |
* @author sylvain |
*/ |
public class ExtractFromBEncoding { |
static public final class ExtractInfo { |
private final int fieldIndex; |
private final List<String> keys, names; |
public ExtractInfo(int fieldIndex, String[] keysAndNames) { |
super(); |
this.fieldIndex = fieldIndex; |
final List<String> keys = new ArrayList<>(keysAndNames.length); |
final List<String> names = new ArrayList<>(keysAndNames.length); |
for (final String kAn : keysAndNames) { |
final int colonIndex = kAn.indexOf(':'); |
keys.add(kAn.substring(0, colonIndex)); |
names.add(kAn.substring(colonIndex + 1)); |
} |
this.keys = Collections.unmodifiableList(keys); |
this.names = Collections.unmodifiableList(names); |
assert this.keys.size() > 0 && this.keys.size() == this.names.size(); |
} |
public final int getFieldIndex() { |
return this.fieldIndex; |
} |
public final List<String> getKeys() { |
return this.keys; |
} |
public final List<String> getNames() { |
return this.names; |
} |
} |
private static final boolean OVERWRITE_FILE = Boolean.getBoolean("overwrite"); |
private static final Pattern DOUBLE_QUOTE_PATTERN = Pattern.compile("\"", Pattern.LITERAL); |
private static final Pattern SINGLE_QUOTED_ENCODEDWORD_PATTERN = Pattern.compile("'(=\\?.+?\\?=)'"); |
public static void main(String[] args) throws IOException, BEncodingException, ParseException { |
if (args.length == 0) { |
System.out.println("Load a CSV file and replace some {@link BEncoding zimbra benconded map} columns by some of their toplevel values"); |
System.out.println("The bencoded colums are removed and the new columns are added at the end"); |
System.out.println("inputFile outputFile [bencodedIndex key1:label1,key2:label2,...]..."); |
System.exit(1); |
} |
final Path in = Paths.get(args[0]); |
final Path out = Paths.get(args[1]); |
if (Files.exists(out) && Files.isSameFile(in, out)) |
throw new IllegalArgumentException("Same file"); |
final List<ExtractInfo> extractInfos = new ArrayList<>(); |
// can't have the same index more than one time because we remove it |
final Set<Integer> indexes = new HashSet<>(); |
for (int i = 2; i < args.length; i += 2) { |
final int index = Integer.parseInt(args[i]); |
if (!indexes.add(index)) |
throw new IllegalArgumentException("Duplicate index " + index); |
final String[] keys = args[i + 1].split(","); |
extractInfos.add(new ExtractInfo(index, keys)); |
} |
System.out.println("From " + in + " to " + out); |
final int lines = replace(in, out, extractInfos); |
System.out.println("Processed " + lines + " line(s)"); |
} |
/** |
* Read from <code>in</code> and write to <code>out</code>. |
* |
* @param in the input CSV. |
* @param out the output CSV. |
* @param extractInfos what to extract. |
* @return number of processed lines. |
* @throws IOException if an error occurs while reading or writing. |
* @throws BEncodingException if a value isn't b-encoded. |
* @throws ParseException if a value isn't a valid "encoded-word". |
*/ |
public static int replace(final Path in, final Path out, final List<ExtractInfo> extractInfos) throws IOException, ParseException, BEncodingException { |
if (extractInfos.isEmpty()) { |
Files.copy(in, out, OVERWRITE_FILE ? new CopyOption[] { StandardCopyOption.REPLACE_EXISTING } : new CopyOption[0]); |
return 0; |
} |
int totalLines = 0; |
final OpenOption[] openOptions = OVERWRITE_FILE ? new OpenOption[] { StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING } : new OpenOption[] { StandardOpenOption.CREATE_NEW }; |
final StringBuilder sb = new StringBuilder(512); |
try (final CSVReader r = new CSVReader(Files.newBufferedReader(in), ',', '"', '\\', 0, true); |
// CSVWriter doesn't handle escaping quotes by doubling them (as needed by |
// LibreOffice), so output CSV ourselves |
final Writer w = Files.newBufferedWriter(out, openOptions)) { |
// column names |
String[] line = r.readNext(); |
final int itemsCount = line.length; |
int additionalCols = 0; |
final List<String> listToWrite = new ArrayList<>(itemsCount); |
listToWrite.addAll(Arrays.asList(line)); |
for (final ExtractInfo e : extractInfos) { |
listToWrite.remove(e.getFieldIndex()); |
for (final String n : e.getNames()) { |
listToWrite.add(n); |
} |
// we remove the metadata |
additionalCols += e.getKeys().size() - 1; |
} |
final int outputColCount = itemsCount + additionalCols; |
writeCSV(sb, listToWrite, outputColCount); |
w.append(sb); |
while ((line = r.readNext()) != null) { |
totalLines++; |
if (itemsCount != line.length) { |
throw new IllegalStateException("Expected " + itemsCount + " items but got " + line.length + " : " + Arrays.asList(line)); |
} |
listToWrite.clear(); |
listToWrite.addAll(Arrays.asList(line)); |
for (final ExtractInfo e : extractInfos) { |
String item = line[e.getFieldIndex()]; |
if (item.startsWith("=?")) { |
item = MimeUtility.decodeWord(item); |
} |
final Map<?, ?> bdecoded = (Map<?, ?>) BEncoding.decode(item); |
listToWrite.remove(e.getFieldIndex()); |
for (final String key : e.getKeys()) { |
// BEncoding cannot encode null but a key can be missing |
final Object val = bdecoded.get(key); |
final String itemToWrite; |
if (val == null) { |
itemToWrite = null; |
} else { |
String toParse = val.toString(); |
// illegal single quoting, replace with double quote |
if (toParse.contains("'=?")) |
toParse = SINGLE_QUOTED_ENCODEDWORD_PATTERN.matcher(toParse).replaceAll("\"$1\""); |
// strict=false because we don't care if there's "illegal character" |
// (e.g. annabelle@testaud@ac-paris.fr or |
// <'jeromebarral83@yahoo.fr'>), we want it to be legible |
final InternetAddress[] mimeDecoded = InternetAddress.parseHeader(toParse, false); |
itemToWrite = CollectionUtils.join(Arrays.asList(mimeDecoded), ", ", InternetAddress::toUnicodeString); |
} |
listToWrite.add(itemToWrite); |
} |
} |
writeCSV(sb, listToWrite, outputColCount); |
w.append(sb); |
} |
} |
return totalLines; |
} |
private static void writeCSV(final StringBuilder sb, final List<String> l, final int outputColCount) { |
if (l.size() != outputColCount) |
throw new IllegalStateException("Wrong column count"); |
sb.setLength(0); |
for (final String i : l) { |
if (i == null) { |
sb.append("\\N"); |
} else { |
sb.append('"'); |
sb.append(DOUBLE_QUOTE_PATTERN.matcher(i).replaceAll("\"\"")); |
sb.append('"'); |
} |
sb.append(','); |
} |
// replace last field separator |
sb.setCharAt(sb.length() - 1, '\n'); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/ProductInfo.java |
---|
30,9 → 30,12 |
public static final String PROPERTIES_NAME = "/product.properties"; |
public static final String ORGANIZATION_NAME = "ORGANIZATION_NAME"; |
public static final String ORGANIZATION_ID = "ORGANIZATION_ID"; |
// e.g. LibreOffice |
public static final String NAME = "NAME"; |
public static final String ID = "ID"; |
public static final String VERSION = "VERSION"; |
// Allow multiple end-user applications (e.g. icons) to share a product name, e.g. Writer |
public static final String LAUNCHER = "LAUNCHER"; |
private static ProductInfo INSTANCE; |
156,6 → 159,18 |
return orgID == null ? null : orgID + '.' + this.getID(); |
} |
public final String getLauncher() { |
return this.getProperty(LAUNCHER); |
} |
public final String getFullLauncherID() { |
final String launcher = this.getLauncher(); |
final String fullID = this.getFullID(); |
if (launcher == null || fullID == null) |
return fullID; |
return fullID + '-' + sanitizeDomainPart(launcher); |
} |
public final String getVersion() { |
return this.getProperty(VERSION); |
} |
162,10 → 177,14 |
public final void store(final Path p) throws IOException { |
try (final OutputStream out = Files.newOutputStream(p)) { |
this.getProps().store(out, this.getClass().getName()); |
store(out); |
} |
} |
public final void store(final OutputStream out) throws IOException { |
this.getProps().store(out, this.getClass().getName()); |
} |
@Override |
public String toString() { |
return this.getClass().getSimpleName() + " for " + getName() + " " + getVersion(); |
/trunk/OpenConcerto/src/org/openconcerto/utils/Outlook.powershell |
---|
New file |
0,0 → 1,31 |
Add-Type -assembly "Microsoft.Office.Interop.Outlook" |
Add-Type -assembly "System.Runtime.Interopservices" |
try { |
$outlook = [Runtime.Interopservices.Marshal]::GetActiveObject('Outlook.Application') |
$outlookWasAlreadyRunning = $true |
} catch { |
try { |
$Outlook = New-Object -comobject Outlook.Application |
$outlookWasAlreadyRunning = $false |
} catch { |
write-host "You must exit Outlook first." |
exit |
} |
} |
$mail = $Outlook.CreateItem( [Microsoft.Office.Interop.Outlook.OlItemType]::olMailItem) |
$stdin = [Console]::OpenStandardInput() |
try { |
$sr = New-Object -TypeName 'System.IO.StreamReader' -ArgumentList $stdin,[Text.UTF8Encoding] |
$mail.body = $sr.ReadToEnd() |
} finally { |
$stdin.Close() |
} |
$mail.subject = @subject@ |
$mail.to = @to@ |
foreach ( $att in @attachments@ ) { |
$mail.attachments.add($att) |
} |
$mail.Display() |
/trunk/OpenConcerto/src/org/openconcerto/utils/CollectionUtils.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
13,6 → 13,7 |
package org.openconcerto.utils; |
import org.openconcerto.utils.cc.CustomEquals; |
import org.openconcerto.utils.cc.IClosure; |
import org.openconcerto.utils.cc.IPredicate; |
import org.openconcerto.utils.cc.ITransformer; |
47,7 → 48,10 |
import java.util.TreeMap; |
import java.util.TreeSet; |
import java.util.function.Function; |
import java.util.function.Supplier; |
import java.util.regex.Pattern; |
import java.util.stream.Collectors; |
import java.util.stream.Stream; |
/** |
* Une classe regroupant des méthodes utilitaires pour les collections. |
403,13 → 407,53 |
} |
static public final boolean identityContains(final Collection<?> coll, final Object item) { |
final int size = coll.size(); |
if (size > 0) { |
if (coll instanceof RandomAccess && coll instanceof List) { |
final List<?> list = (List<?>) coll; |
for (int i = 0; i < size; i++) { |
final Object v = list.get(i); |
if (item == v) |
return true; |
} |
} else { |
for (final Object v : coll) { |
if (item == v) |
return true; |
} |
} |
} |
return false; |
} |
static public final boolean identityRemove(final Collection<?> coll, final Object item) { |
final int size = coll.size(); |
if (size > 0) { |
if (coll instanceof RandomAccess && coll instanceof List) { |
final List<?> list = (List<?>) coll; |
for (int i = 0; i < size; i++) { |
final Object v = list.get(i); |
if (item == v) { |
list.remove(i); |
return true; |
} |
} |
} else { |
for (Iterator<?> i = coll.iterator(); i.hasNext();) { |
if (item == i.next()) { |
i.remove(); |
return true; |
} |
} |
} |
} |
return false; |
} |
static public final <T> int identityIndexOf(final List<T> coll, final T item) { |
return CustomEquals.indexOf(coll, item, CustomEquals.getIdentity()); |
} |
static public final boolean identityEquals(final List<?> coll1, final List<?> coll2) { |
if (coll1 == coll2) |
return true; |
463,11 → 507,19 |
return null; |
} |
final List<E> result; |
if (list instanceof RandomAccess) { |
final int size = list.size(); |
final List<E> result = new ArrayList<E>(size); |
for (int i = 0; i < list.size(); i++) { |
result = new ArrayList<>(size); |
for (int i = 0; i < size; i++) { |
result.add(c.cast(list.get(i))); |
} |
} else { |
result = new LinkedList<>(); |
for (final Object o : list) { |
result.add(c.cast(o)); |
} |
} |
return result; |
} |
484,11 → 536,15 |
* a <code>E</code> or a value which the type is not a <code>F</code>. |
*/ |
public static <E, F> Map<E, F> castMap(final Map<?, ?> map, Class<E> cKey, Class<F> cValue) throws ClassCastException { |
return castMap(map, cKey, cValue, HashMap::new); |
} |
public static <E, F> Map<E, F> castMap(final Map<?, ?> map, Class<E> cKey, Class<F> cValue, final Supplier<Map<E, F>> ctor) throws ClassCastException { |
if (map == null) { |
return null; |
} |
final Map<E, F> result = new HashMap<E, F>(); |
final Map<E, F> result = ctor.get(); |
for (final Entry<?, ?> mapEntry : map.entrySet()) { |
final E key; |
try { |
507,6 → 563,14 |
return result; |
} |
public static <T> Stream<T> subclassFilter(final Collection<? super T> coll, final Class<T> subclass) { |
return coll.stream().filter(subclass::isInstance).map(subclass::cast); |
} |
public static <T> List<T> subclassFilterToList(final Collection<? super T> coll, final Class<T> subclass) { |
return subclassFilter(coll, subclass).collect(Collectors.toList()); |
} |
/** |
* The number of equals item between a and b, starting from the end. |
* |
948,13 → 1012,42 |
} |
} |
@SafeVarargs |
public static final <T> List<T> toImmutableList(final T... items) { |
final int length = items.length; |
if (length == 0) |
return Collections.emptyList(); |
else if (length == 1) |
return Collections.singletonList(items[0]); |
return Collections.unmodifiableList(Arrays.asList(Arrays.copyOf(items, length))); |
} |
public static final <T> List<T> toImmutableList(final Collection<? extends T> coll) { |
return toImmutableList(coll, ArrayList::new); |
} |
public static final <T, C extends Collection<? extends T>> List<T> toImmutableList(final C coll, final Function<? super C, ? extends List<T>> createColl) { |
if (coll.isEmpty()) |
return toImmutableList(coll, createColl, true); |
} |
/** |
* Create a new immutable list. |
* |
* @param <T> type of items |
* @param <C> type of collection |
* @param coll the collection holding the items. |
* @param createColl how to copy the collection into a new list. |
* @param std <code>true</code> if the created <code>createColl</code> use |
* {@link Object#equals(Object)}, in that case {@link Collections#singletonList(Object)} |
* can be returned. |
* @return an immutable list with the same items as <code>coll</code>. |
*/ |
public static final <T, C extends Collection<? extends T>> List<T> toImmutableList(final C coll, final Function<? super C, ? extends List<T>> createColl, final boolean std) { |
final int size = coll.size(); |
if (size == 0) |
return Collections.emptyList(); |
else if (std && size == 1) |
return Collections.singletonList(coll.iterator().next()); |
return Collections.unmodifiableList(createColl.apply(coll)); |
} |
966,7 → 1059,7 |
// but not at runtime. |
return toImmutableSet((SortedSet<T>) coll, TreeSet::new); |
} else if (coll instanceof IdentitySet) { |
return toImmutableSet((IdentitySet<? extends T>) coll, LinkedIdentitySet::new); |
return toImmutableSet((IdentitySet<? extends T>) coll, LinkedIdentitySet::new, false); |
} else { |
// In doubt, keep order |
// ATTN LinkedHashSet extends HashSet |
975,8 → 1068,28 |
} |
public static final <T, C extends Collection<? extends T>> Set<T> toImmutableSet(final C coll, final Function<? super C, ? extends Set<T>> createColl) { |
if (coll.isEmpty()) |
return toImmutableSet(coll, createColl, true); |
} |
/** |
* Create a new immutable set. |
* |
* @param <T> type of items |
* @param <C> type of collection |
* @param coll the collection holding the items. |
* @param createColl how to copy the collection into a new list. |
* @param std <code>true</code> if the created <code>createColl</code> use |
* {@link Object#equals(Object)}, in that case {@link Collections#singleton(Object)} can |
* be returned. |
* @return an immutable set with the same items as <code>coll</code>. |
*/ |
public static final <T, C extends Collection<? extends T>> Set<T> toImmutableSet(final C coll, final Function<? super C, ? extends Set<T>> createColl, final boolean std) { |
final int size = coll.size(); |
if (size == 0) |
return Collections.emptySet(); |
else if (std && size == 1) |
return Collections.singleton(coll.iterator().next()); |
final Set<T> res = createColl.apply(coll); |
return Collections.unmodifiableSet(res); |
} |
995,7 → 1108,7 |
// ATTN see eclipse bug below about wrong constructor |
return toImmutableMap((SortedMap<K, ? extends V>) map, TreeMap::new); |
} else if (map instanceof IdentityHashMap) { |
return toImmutableMap((IdentityHashMap<? extends K, ? extends V>) map, IdentityHashMap::new); |
return toImmutableMap((IdentityHashMap<? extends K, ? extends V>) map, IdentityHashMap::new, false); |
} else { |
// In doubt, keep order |
// ATTN LinkedHashMap extends HashMap |
1003,6 → 1116,10 |
} |
} |
public static final <K, V, InMap extends Map<? extends K, ? extends V>> Map<K, V> toImmutableMap(final InMap map, final Function<? super InMap, ? extends Map<K, V>> copyFunction) { |
return toImmutableMap(map, copyFunction, true); |
} |
/** |
* Return an immutable map with the same entries as the passed one. NOTE: <code>copyMap</code> |
* <strong>must</strong> copy the entries so that a modification of <code>map</code> doesn't |
1016,11 → 1133,20 |
* {@link TreeMap#TreeMap(Map)} is (correctly) executed at runtime. |
* @param map the map. |
* @param copyFunction how to copy the passed map. |
* @param std <code>true</code> if the created <code>createColl</code> use |
* {@link Object#equals(Object)}, in that case |
* {@link Collections#singletonMap(Object, Object)} can be returned. |
* @return an immutable map. |
*/ |
public static final <K, V, InMap extends Map<? extends K, ? extends V>> Map<K, V> toImmutableMap(final InMap map, final Function<? super InMap, ? extends Map<K, V>> copyFunction) { |
if (map.isEmpty()) |
public static final <K, V, InMap extends Map<? extends K, ? extends V>> Map<K, V> toImmutableMap(final InMap map, final Function<? super InMap, ? extends Map<K, V>> copyFunction, |
final boolean std) { |
final int size = map.size(); |
if (size == 0) { |
return Collections.emptyMap(); |
} else if (std && size == 1) { |
final Entry<? extends K, ? extends V> e = map.entrySet().iterator().next(); |
return Collections.singletonMap(e.getKey(), e.getValue()); |
} |
return Collections.unmodifiableMap(copyFunction.apply(map)); |
} |
1055,7 → 1181,77 |
return res; |
} |
// same as HashMap and HashSet |
private static final float DEFAULT_LOAD_FACTOR = 0.75f; |
public static interface MapConstructor<K, V, M extends Map<K, V>> { |
M create(int initialCapacity, float loadFactor); |
} |
public static final int getCapacity(final int plannedSize, final float loadFactor) { |
return Math.max((int) Math.ceil(plannedSize / loadFactor), 4); |
} |
public static final <K, V> LinkedHashMap<K, V> newLinkedHashMap(final int plannedSize) { |
return newMap(plannedSize, DEFAULT_LOAD_FACTOR, (MapConstructor<K, V, LinkedHashMap<K, V>>) LinkedHashMap::new); |
} |
public static final <K, V> HashMap<K, V> newHashMap(final int plannedSize) { |
return newHashMap(plannedSize, DEFAULT_LOAD_FACTOR); |
} |
/** |
* Create a new map with no rehash operations until at least a certain size. |
* |
* @param <K> the type of keys maintained by the new map |
* @param <V> the type of mapped values |
* @param plannedSize the number of entries expected to be added to the returned map. |
* @param loadFactor the load factor of the returned map, not advised to be <code>1.0f</code> |
* because it increases the chances of hash collisions. |
* @return a new map that won't rehash until at least <code>plannedSize</code> items. |
*/ |
public static final <K, V> HashMap<K, V> newHashMap(final int plannedSize, final float loadFactor) { |
return newMap(plannedSize, loadFactor, (MapConstructor<K, V, HashMap<K, V>>) HashMap::new); |
} |
/** |
* Create a new map with no rehash operations until at least a certain size. |
* |
* @param <K> the type of keys maintained by the new map |
* @param <V> the type of mapped values |
* @param <M> the type of the new map |
* @param plannedSize the number of entries expected to be added to the returned map |
* @param loadFactor the load factor, passed to <code>ctor</code>. |
* @param ctor how to create the new map, will be passed the computed initial capacity and |
* <code>loadFactor</code>, e.g. {@link LinkedHashMap#LinkedHashMap(int, float) |
* LinkedHashMap::new}. |
* @return a new map that won't rehash until at least <code>plannedSize</code> items. |
*/ |
public static final <K, V, M extends Map<K, V>> M newMap(final int plannedSize, final float loadFactor, final MapConstructor<K, V, M> ctor) { |
return ctor.create(getCapacity(plannedSize, loadFactor), loadFactor); |
} |
public static interface SetConstructor<V, S extends Set<V>> { |
S create(int initialCapacity, float loadFactor); |
} |
public static final <V> LinkedHashSet<V> newLinkedHashSet(final int plannedSize) { |
return newSet(plannedSize, DEFAULT_LOAD_FACTOR, (SetConstructor<V, LinkedHashSet<V>>) LinkedHashSet::new); |
} |
public static final <V> HashSet<V> newHashSet(final int plannedSize) { |
return newHashSet(plannedSize, DEFAULT_LOAD_FACTOR); |
} |
public static final <V> HashSet<V> newHashSet(final int plannedSize, final float loadFactor) { |
return newSet(plannedSize, loadFactor, (SetConstructor<V, HashSet<V>>) HashSet::new); |
} |
public static final <V, S extends Set<V>> S newSet(final int plannedSize, final float loadFactor, final SetConstructor<V, S> ctor) { |
return ctor.create(getCapacity(plannedSize, loadFactor), loadFactor); |
} |
/** |
* Creates a map with null values. |
* |
* @param <K> type of key. |
/trunk/OpenConcerto/src/org/openconcerto/utils/TimeUtils.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
15,6 → 15,9 |
import java.math.BigDecimal; |
import java.math.BigInteger; |
import java.time.LocalDate; |
import java.time.LocalDateTime; |
import java.time.ZoneId; |
import java.util.Arrays; |
import java.util.Calendar; |
import java.util.Collection; |
424,4 → 427,30 |
} |
return finerAmount == finer.convert(coarserAmount, coarser); |
} |
public static LocalDateTime toLocalDateTime(Calendar calendar) { |
if (calendar == null) |
return null; |
return LocalDateTime.ofInstant(calendar.toInstant(), getTZ(calendar)); |
} |
private static ZoneId getTZ(Calendar calendar) { |
final TimeZone tz = calendar.getTimeZone(); |
return tz == null ? ZoneId.systemDefault() : tz.toZoneId(); |
} |
public static final Calendar toCalendar(final LocalDateTime dt) { |
final Calendar cal = Calendar.getInstance(); |
cal.setTimeInMillis(dt.atZone(getTZ(cal)).toInstant().toEpochMilli()); |
return cal; |
} |
public static LocalDate toLocalDate(Calendar calendar) { |
if (calendar == null) |
return null; |
if (calendar instanceof GregorianCalendar) |
return LocalDate.of(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH) + 1, calendar.get(Calendar.DAY_OF_MONTH)); |
else |
return toLocalDateTime(calendar).toLocalDate(); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/utils/EmailClient.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
20,21 → 20,23 |
import org.openconcerto.utils.DesktopEnvironment.XFCE; |
import org.openconcerto.utils.OSFamily.Unix; |
import org.openconcerto.utils.io.PercentEncoder; |
import org.openconcerto.utils.system.Powershell; |
import java.io.BufferedOutputStream; |
import java.io.BufferedWriter; |
import java.io.File; |
import java.io.IOException; |
import java.io.OutputStreamWriter; |
import java.io.OutputStream; |
import java.io.PrintStream; |
import java.io.Writer; |
import java.lang.ProcessBuilder.Redirect; |
import java.net.URI; |
import java.net.URISyntaxException; |
import java.nio.charset.StandardCharsets; |
import java.util.ArrayList; |
import java.util.Arrays; |
import java.util.List; |
import java.util.regex.Matcher; |
import java.util.regex.Pattern; |
import java.util.stream.Collectors; |
public abstract class EmailClient { |
344,26 → 346,34 |
public static final EmailClient Outlook = new EmailClient(EmailClientType.Outlook) { |
@Override |
protected boolean composeNative(String to, String subject, String body, File... attachments) throws IOException, InterruptedException { |
final DesktopEnvironment de = DesktopEnvironment.getDE(); |
final File vbs = FileUtils.getFile(EmailClient.class.getResource("OutlookEmail.vbs")); |
final List<String> l = new ArrayList<String>(6); |
l.add("cscript"); |
l.add(de.quoteParamForExec(vbs.getAbsolutePath())); |
if (to != null) |
l.add(createVBParam("to", to)); |
if (subject != null) |
l.add(createVBParam("subject", subject)); |
// at least set a parameter otherwise the usage get displayed |
l.add(createVBParam("unicodeStdIn", "1")); |
for (File attachment : attachments) { |
l.add(de.quoteParamForExec(attachment.getAbsolutePath())); |
return composePowershell(to, subject, body, attachments); |
} |
final Process process = new ProcessBuilder(l).start(); |
// VBScript only knows ASCII and UTF-16 |
final Writer writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream(), StringUtils.UTF16)); |
writer.write(body); |
writer.close(); |
// only tested with powershell 5.1 |
protected boolean composePowershell(String to, String subject, String body, File... attachments) throws IOException, InterruptedException { |
final Powershell pwsh = Powershell.getInstance(); |
// Don't create temporary file : |
// https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.1 |
String template = new String(StreamUtils.read(EmailClient.class.getResourceAsStream("Outlook.powershell")), StandardCharsets.UTF_8); |
template = template.replace("@to@", pwsh.quote(to == null ? "" : to)); |
template = template.replace("@subject@", pwsh.quote(subject == null ? "" : subject)); |
template = template.replace("@attachments@", pwsh.quoteArray(Arrays.asList(attachments).stream().map(File::getAbsolutePath).collect(Collectors.toList()))); |
final ProcessBuilder pb = new ProcessBuilder(); |
pb.command().add("powershell"); |
// Apparently piping (i.e. "-Command -") only supports ASCII (and would require |
// embedding the body in the script). |
pb.command().add("-EncodedCommand"); |
pb.command().add(pwsh.getEncodedCommand(template)); |
pb.inheritIO(); |
pb.redirectInput(Redirect.PIPE); |
final Process process = pb.start(); |
try (final OutputStream in = process.getOutputStream()) { |
in.write(body.getBytes(StandardCharsets.UTF_8)); |
} |
final int returnCode = process.waitFor(); |
if (returnCode != 0) |
throw new IllegalStateException("Non zero return code: " + returnCode); |
/trunk/OpenConcerto/src/org/openconcerto/laf/IScrollBarUI.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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/laf/ILookAndFeel.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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/laf/IComboBoxUI.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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/laf/IButtonUI.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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/task/config/ComptaOptions.java |
---|
New file |
0,0 → 1,59 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific |
* language governing permissions and limitations under the License. |
* |
* When distributing the software, include this License Header Notice in each file. |
*/ |
package org.openconcerto.task.config; |
import net.jcip.annotations.Immutable; |
@Immutable |
public class ComptaOptions { |
private final Customer customer; |
public ComptaOptions(final Customer customer) { |
this.customer = customer; |
} |
public final Customer getCustomer() { |
return this.customer; |
} |
public final boolean customerUseControle() { |
return customerIsPreventec() || customerIsKD(); |
} |
public final boolean customerIsKD() { |
final boolean kd = getCustomer() == Customer.KD; |
return kd; |
} |
public final boolean customerIsODT() { |
final boolean kd = getCustomer() == Customer.ODT; |
return kd; |
} |
public final boolean customerIsTCP() { |
final boolean kd = getCustomer() == Customer.TCP; |
return kd; |
} |
public final boolean customerIsPreventec() { |
final boolean prev = getCustomer() == Customer.PREVENTEC; |
return prev; |
} |
public final boolean customerIsProfilbat() { |
return getCustomer() == Customer.PROFILBAT; |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/task/config/Customer.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.task.config; |
public enum Customer { |
GESTION_DEFAULT("Gestion_Default"), IDEATION("Ideation"), TCP("TCP"), KD("KD"), PREVENTEC("Preventec"), PROFILBAT("Profilbat"), CTD("CTD"), ODT("ODT"); |
private final String name; |
private Customer(String name) { |
if (name == null) |
throw new NullPointerException(); |
this.name = name; |
} |
public String getName() { |
return this.name; |
} |
public static Customer fromName(String n) { |
return Customer.valueOf(n.toUpperCase()); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationEcritures/GenerationMvtSaisieAchat.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
49,7 → 49,7 |
// iniatilisation des valeurs de la map |
this.date = (Date) saisieRow.getObject("DATE"); |
this.nom = "Achat : " + rowFournisseur.getString("NOM") + " Facture : " + this.saisieRow.getObject("NUMERO_FACTURE").toString() + " " + saisieRow.getObject("NOM").toString(); |
this.nom = " Fourn. : " + rowFournisseur.getString("NOM") + " " + saisieRow.getObject("NOM").toString() + " Facture : " + this.saisieRow.getObject("NUMERO_FACTURE").toString(); |
this.putValue("DATE", this.date); |
AccountingRecordsProvider provider = AccountingRecordsProviderManager.get(ID); |
provider.putLabel(saisieRow, this.mEcritures); |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationEcritures/GenerationMvtSaisieVenteFacture.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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.erp.core.finance.accounting.element.ComptePCESQLElement; |
import org.openconcerto.erp.core.finance.accounting.element.JournalSQLElement; |
import org.openconcerto.erp.generationDoc.SheetXml; |
import org.openconcerto.erp.core.finance.tax.model.TaxeCache; |
import org.openconcerto.erp.generationEcritures.provider.AccountingRecordsProvider; |
import org.openconcerto.erp.generationEcritures.provider.AccountingRecordsProviderManager; |
import org.openconcerto.erp.model.PrixTTC; |
140,13 → 141,20 |
SQLTable tableEchantillon = null; |
BigDecimal portHT = BigDecimal.valueOf(saisieRow.getLong("PORT_HT")).movePointLeft(2); |
BigDecimal fraisDocHT = BigDecimal.valueOf(saisieRow.getLong("FRAIS_DOCUMENT_HT")).movePointLeft(2); |
SQLRow taxeDoc = saisieRow.getNonEmptyForeign("ID_TAXE_FRAIS_DOCUMENT") == null ? TaxeCache.getCache().getFirstTaxe() |
: TaxeCache.getCache().getRowFromId(saisieRow.getForeignID("ID_TAXE_FRAIS_DOCUMENT")).asRow(); |
TotalCalculator calc; |
SQLRow taxePort = saisieRow.getNonEmptyForeign("ID_TAXE_PORT") == null ? TaxeCache.getCache().getFirstTaxe() |
: TaxeCache.getCache().getRowFromId(saisieRow.getForeignID("ID_TAXE_PORT")).asRow(); |
if (clientRow.getTable().contains("ID_COMPTE_PCE_PRODUIT") && !clientRow.isForeignEmpty("ID_COMPTE_PCE_PRODUIT")) { |
calc = getValuesFromElement(false, false, "T_PV_HT", saisieRow, saisieVFTable.getTable("SAISIE_VENTE_FACTURE_ELEMENT"), portHT, saisieRow.getForeign("ID_TAXE_PORT"), fraisDocHT, |
saisieRow.getForeign("ID_TAXE_FRAIS_DOCUMENT"), tableEchantillon, clientRow.getForeign("ID_COMPTE_PCE_PRODUIT")); |
calc = getValuesFromElement(false, false, "T_PV_HT", saisieRow, saisieVFTable.getTable("SAISIE_VENTE_FACTURE_ELEMENT"), portHT, taxePort, fraisDocHT, taxeDoc, tableEchantillon, |
clientRow.getForeign("ID_COMPTE_PCE_PRODUIT")); |
} else { |
calc = getValuesFromElement(saisieRow, saisieVFTable.getTable("SAISIE_VENTE_FACTURE_ELEMENT"), portHT, saisieRow.getForeign("ID_TAXE_PORT"), fraisDocHT, |
saisieRow.getForeign("ID_TAXE_FRAIS_DOCUMENT"), tableEchantillon); |
calc = getValuesFromElement(saisieRow, saisieVFTable.getTable("SAISIE_VENTE_FACTURE_ELEMENT"), portHT, taxePort, fraisDocHT, taxeDoc, tableEchantillon); |
} |
// On génére les ecritures si la facture n'est pas un acompte |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationEcritures/Mouvement.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
101,6 → 101,25 |
} |
public String asString() { |
StringBuilder s = new StringBuilder(); |
BigDecimal d = BigDecimal.ZERO; |
BigDecimal c = BigDecimal.ZERO; |
s.append("Mouvement N°"); |
s.append(this.numero); |
s.append("\n"); |
for (Ecriture e : this.ecritures) { |
s.append("D : " + e.getDebit()); |
s.append("\tC : " + e.getCredit()); |
d = d.add(e.getDebit()); |
c = c.add(e.getCredit()); |
s.append("\tS : " + (d.subtract(c)) + "\n"); |
} |
return s.toString(); |
} |
public boolean isEmpty() { |
return this.ecritures.isEmpty(); |
} |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationEcritures/GenerationMvtReglementAchat.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
80,7 → 80,7 |
// this.nom = saisieRow.getObject("NOM").toString(); |
// si paiement comptant |
if ((modeRegRow.getInt("AJOURS") == 0) && (modeRegRow.getInt("LENJOUR") == 0)) { |
if (typeRegRow.getID() != TypeReglementSQLElement.INDEFINI && (modeRegRow.getInt("AJOURS") == 0) && (modeRegRow.getInt("LENJOUR") == 0)) { |
System.out.println("Règlement Comptant"); |
// test Cheque |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/OOgenerationListeColumnXML.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
152,49 → 152,8 |
} |
Map<String, Map<Integer, String>> mapStyle = searchStyle(sheet, lastColumn, endPageLine); |
// int nbPage = fillTable(tableau, liste, sheet, mapStyle, true, style); |
// int firstLine = Integer.valueOf(tableau.getAttributeValue("firstLine")); |
// int endLine = Integer.valueOf(tableau.getAttributeValue("endLine")); |
// Object printRangeObj = sheet.getPrintRanges(); |
// |
// System.err.println("Nombre de page == " + nbPage); |
// if (nbPage == 1) { |
// fillTable(tableau, liste, sheet, mapStyle, false, style); |
// } else { |
// |
// if (printRangeObj != null) { |
// String s = printRangeObj.toString(); |
// String[] range = s.split(":"); |
// |
// for (int i = 0; i < range.length; i++) { |
// String string = range[i]; |
// range[i] = string.subSequence(string.indexOf('.') + 1, string.length()).toString(); |
// } |
// |
// int rowEnd = -1; |
// if (range.length > 1) { |
// rowEnd = sheet.resolveHint(range[1]).y + 1; |
// int rowEndNew = rowEnd * (nbPage + 1); |
// String sNew = s.replaceAll(String.valueOf(rowEnd), String.valueOf(rowEndNew)); |
// sheet.setPrintRanges(sNew); |
// System.err.println(" ****** Replace print ranges; Old:" + rowEnd + "--" + s + " New:" + |
// rowEndNew + "--" + sNew); |
// } |
// } |
// |
// // le nombre d'éléments ne tient pas dans le tableau du modéle |
// sheet.duplicateFirstRows(endLine, 1); |
// |
// int lineToAdd = endPageLine - endLine; |
// sheet.insertDuplicatedRows(firstLine, lineToAdd); |
// |
// // On duplique la premiere page si on a besoin de plus de deux pages |
// System.err.println("nbPage == " + nbPage); |
// if (nbPage > 2) { |
// sheet.duplicateFirstRows(endPageLine, nbPage - 2); |
// } |
fillTable(tableau, liste, sheet, mapStyle, false, style); |
// } |
} |
/** |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/gestcomm/ReleveChequeEmisSheet.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
83,5 → 83,6 |
// Total |
this.mCell.put("L45", new Double(GestionDevise.currencyToString(montantTotal, false))); |
this.nbPage = 1; |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/gestcomm/AvoirClientXmlSheet.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
38,6 → 38,7 |
super(row); |
this.printer = PrinterNXProps.getInstance().getStringProperty("BonPrinter"); |
this.elt = Configuration.getInstance().getDirectory().getElement("AVOIR_CLIENT"); |
this.setPostProcess(new OptionDocProcessor()); |
} |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/gestcomm/EtatVentesXmlSheet.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
30,6 → 30,7 |
import org.openconcerto.sql.model.SQLSelectJoin; |
import org.openconcerto.sql.model.SQLTable; |
import org.openconcerto.sql.model.Where; |
import org.openconcerto.utils.ListMap; |
import org.openconcerto.utils.Tuple2; |
import java.math.BigDecimal; |
146,13 → 147,19 |
// Stock |
SQLRowValues rowValsArtStock = new SQLRowValues(foreignTableArticle); |
rowValsArtStock.putNulls("ID", "CODE", "NOM"); |
rowValsArtStock.putRowValues("ID_STOCK").putNulls("QTE_REEL", "QTE_TH", "QTE_MIN", "QTE_RECEPT_ATTENTE", "QTE_LIV_ATTENTE"); |
SQLRowValues rowValsStock = new SQLRowValues(tableFactureElement.getTable("STOCK")); |
rowValsStock.putNulls("QTE_REEL", "QTE_TH", "QTE_MIN", "QTE_RECEPT_ATTENTE", "QTE_LIV_ATTENTE").put("ID_ARTICLE", rowValsArtStock); |
SQLRowValuesListFetcher fetcherStock = SQLRowValuesListFetcher.create(rowValsArtStock); |
List<SQLRowValues> resultStock = fetcherStock.fetch(); |
Map<Integer, SQLRowValues> mapStock = new HashMap<>(); |
ListMap<Integer, SQLRowValues> mapStock = new ListMap<>(); |
for (SQLRowValues sqlRowValues : resultStock) { |
mapStock.put(sqlRowValues.getID(), sqlRowValues); |
final Set<SQLRowValues> referentRows = sqlRowValues.getReferentRows(tableFactureElement.getTable("STOCK").getField("ID_ARTICLE")); |
for (SQLRowValues sqlRowValues2 : referentRows) { |
mapStock.add(sqlRowValues.getID(), sqlRowValues2); |
} |
} |
// Requete Pour obtenir les quantités pour chaque type de réglement |
SQLSelect sel = new SQLSelect(); |
324,17 → 331,28 |
mValuesStock.put("NOM", nom); |
mValuesStock.put("QTE", a.qte); |
if (mapStock.containsKey(articleID)) { |
SQLRowValues rowValsArt = mapStock.get(articleID); |
if (rowValsArt.getObject("ID_STOCK") != null && !rowValsArt.isForeignEmpty("ID_STOCK")) { |
SQLRowAccessor rowValsStock = rowValsArt.getForeign("ID_STOCK"); |
mValuesStock.put("QTE_TH", rowValsStock.getObject("QTE_TH")); |
mValuesStock.put("QTE_REEL", rowValsStock.getObject("QTE_REEL")); |
mValuesStock.put("QTE_MIN", rowValsStock.getObject("QTE_MIN")); |
mValuesStock.put("QTE_RECEPT_ATTENTE", rowValsStock.getObject("QTE_RECEPT_ATTENTE")); |
mValuesStock.put("QTE_LIV_ATTENTE", rowValsStock.getObject("QTE_LIV_ATTENTE")); |
List<SQLRowValues> rowValsArt = mapStock.get(articleID); |
BigDecimal totalTh = BigDecimal.ZERO; |
BigDecimal totalReel = BigDecimal.ZERO; |
BigDecimal min = BigDecimal.ZERO; |
BigDecimal attentR = BigDecimal.ZERO; |
BigDecimal attenteL = BigDecimal.ZERO; |
for (SQLRowValues rowStock : rowValsArt) { |
totalTh = totalTh.add(new BigDecimal(rowStock.getFloat("QTE_TH"))); |
totalReel = totalReel.add(new BigDecimal(rowStock.getFloat("QTE_REEL"))); |
min = min.add(new BigDecimal(rowStock.getFloat("QTE_MIN"))); |
attentR = attentR.add(new BigDecimal(rowStock.getFloat("QTE_RECEPT_ATTENTE"))); |
attenteL = attenteL.add(new BigDecimal(rowStock.getFloat("QTE_LIV_ATTENTE"))); |
} |
mValuesStock.put("QTE_TH", totalTh); |
mValuesStock.put("QTE_REEL", totalReel); |
mValuesStock.put("QTE_MIN", min); |
mValuesStock.put("QTE_RECEPT_ATTENTE", attentR); |
mValuesStock.put("QTE_LIV_ATTENTE", attenteL); |
styleStock.put(listValuesStock.size(), "Normal"); |
listValuesStock.add(mValuesStock); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/gestcomm/OptionDocProcessor.java |
---|
New file |
0,0 → 1,78 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific |
* language governing permissions and limitations under the License. |
* |
* When distributing the software, include this License Header Notice in each file. |
*/ |
package org.openconcerto.erp.generationDoc.gestcomm; |
import org.openconcerto.sql.model.SQLRowAccessor; |
import org.openconcerto.sql.model.SQLRowValues; |
import org.openconcerto.utils.cc.ITransformer; |
import org.openconcerto.utils.DecimalUtils; |
import java.math.BigDecimal; |
import java.util.ArrayList; |
import java.util.HashSet; |
import java.util.List; |
import java.util.Set; |
public class OptionDocProcessor implements ITransformer<List<SQLRowAccessor>, List<SQLRowAccessor>> { |
@Override |
public List<SQLRowAccessor> transformChecked(List<SQLRowAccessor> input) { |
List<SQLRowValues> resultRowValues = new ArrayList(input.size()); |
SQLRowValues lastRowNonOptionnal = null; |
Set<SQLRowValues> recalculPrixUnitaire = new HashSet<SQLRowValues>(); |
for (SQLRowAccessor sqlRowAccessor : input) { |
final SQLRowAccessor nonEmptyForeign = sqlRowAccessor.getNonEmptyForeign("ID_ARTICLE"); |
final SQLRowValues asRowValues = sqlRowAccessor.asRowValues(); |
if (lastRowNonOptionnal != null && nonEmptyForeign != null && nonEmptyForeign.getBoolean("OPTION")) { |
lastRowNonOptionnal.put("T_PV_HT", lastRowNonOptionnal.getBigDecimal("T_PV_HT").add(asRowValues.getBigDecimal("T_PV_HT"))); |
lastRowNonOptionnal.put("T_PV_TTC", lastRowNonOptionnal.getBigDecimal("T_PV_TTC").add(asRowValues.getBigDecimal("T_PV_TTC"))); |
recalculPrixUnitaire.add(lastRowNonOptionnal); |
asRowValues.put("PRIX_METRIQUE_VT_1", null); |
asRowValues.put("PV_HT", null); |
asRowValues.put("T_PV_HT", null); |
asRowValues.put("T_PV_TTC", null); |
asRowValues.putEmptyLink("ID_TAXE"); |
} else { |
lastRowNonOptionnal = asRowValues; |
} |
resultRowValues.add(asRowValues); |
} |
List<SQLRowAccessor> result = new ArrayList(input.size()); |
for (SQLRowValues sqlRowValues : resultRowValues) { |
if (recalculPrixUnitaire.contains(sqlRowValues)) { |
String fieldQte = sqlRowValues.getTable().getName().startsWith("BON_DE_LIVRAISON") ? "QTE_LIVREE" : "QTE"; |
BigDecimal qte = sqlRowValues.getBigDecimal("QTE_UNITAIRE").multiply(new BigDecimal(sqlRowValues.getInt(fieldQte), DecimalUtils.HIGH_PRECISION)); |
if (qte.signum() != 0) { |
BigDecimal totalHT = sqlRowValues.getBigDecimal("T_PV_HT"); |
final BigDecimal remisePercent = sqlRowValues.getBigDecimal("POURCENT_REMISE"); |
if (remisePercent != null && remisePercent.signum() > 0) { |
totalHT = totalHT.divide(BigDecimal.ONE.subtract(remisePercent.movePointLeft(2)), org.openconcerto.utils.DecimalUtils.HIGH_PRECISION); |
} |
BigDecimal unitPrice = totalHT.divide(qte, DecimalUtils.HIGH_PRECISION); |
sqlRowValues.put("PV_HT", unitPrice); |
sqlRowValues.put("PRIX_METRIQUE_VT_1", unitPrice); |
} |
} |
result.add(sqlRowValues); |
} |
return result; |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/OOXMLElement.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
129,6 → 129,10 |
if (rmBreakLines) { |
res = (res == null ? res : res.toString().replaceAll("\n", ",")); |
} |
final String brk = this.elt.getAttributeValue("replaceWithBreakLine"); |
if (brk != null && brk.trim().length() > 0) { |
res = (res == null ? res : res.toString().replaceAll(brk, "\n")); |
} |
return res; |
} |
145,8 → 149,24 |
protected String getStringProposition(SQLRowAccessor rowProp) { |
if (rowProp.getTable().getName().equalsIgnoreCase("AFFAIRE")) { |
final SQLRowAccessor nonEmptyForeignProp = rowProp.getNonEmptyForeign("ID_PROPOSITION"); |
String result = ""; |
if (rowProp.getString("NUMERO_PROPOSITION").trim().length() > 0) { |
return "Notre proposition " + rowProp.getString("NUMERO_PROPOSITION"); |
} |
if (nonEmptyForeignProp != null && !nonEmptyForeignProp.isUndefined()) { |
if (result.length() == 0) { |
result = "Notre proposition " + nonEmptyForeignProp.getString("NUMERO_PROPOSITION"); |
} |
result += " du " + format.format(rowProp.getObject("DATE")); |
} |
return result; |
} else { |
return "Notre proposition " + rowProp.getString("NUMERO") + " du " + format.format(rowProp.getObject("DATE")); |
} |
} |
public Double getTotalHTTable(SQLRowAccessor rowFact) { |
215,4 → 235,8 |
public boolean isImage() { |
return this.elt.getAttributeValue("type").equalsIgnoreCase("image"); |
} |
public SQLElement getSQLElement() { |
return this.sqlElt; |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/OOgenerationXML.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
257,16 → 257,21 |
lastColumn = sheet.resolveHint(oLastColTmp.toString() + 1).x + 1; |
} |
Map<String, Map<Integer, String>> mapStyle = searchStyle(sheet, lastColumn, endPageLine); |
int firstLine = Integer.parseInt(tableau.getAttributeValue("firstLine")); |
int endLine = Integer.parseInt(tableau.getAttributeValue("endLine")); |
Object printRangeObj = sheet.getPrintRanges(); |
final Map<String, Map<Integer, String>> mapStyle; |
if (tableau.getAttributeValue("table").equalsIgnoreCase("TVA")) { |
fillTaxeDocumentMap(tableau, sheet, mapStyle, false); |
mapStyle = null; |
fillTaxeDocumentMap(tableau, sheet, false); |
return; |
} else { |
mapStyle = searchStyle(sheet, firstLine, lastColumn, endLine); |
} |
int nbPage = fillTable(tableau, row, sheet, mapStyle, true, rowLanguage); |
int firstLine = Integer.parseInt(tableau.getAttributeValue("firstLine")); |
int endLine = Integer.parseInt(tableau.getAttributeValue("endLine")); |
Object printRangeObj = sheet.getPrintRanges(); |
System.err.println("Nombre de page == " + nbPage); |
if (nbPage == 1) { |
281,10 → 286,9 |
range[i] = string.subSequence(string.indexOf('.') + 1, string.length()).toString(); |
} |
int rowEnd = -1; |
if (range.length > 1) { |
rowEnd = sheet.resolveHint(range[1]).y + 1; |
int rowEndNew = rowEnd * (nbPage); |
int rowEnd = sheet.resolveHint(range[1]).y + 1; |
int rowEndNew = rowEnd * nbPage; |
String sNew = s.replaceAll(String.valueOf(rowEnd), String.valueOf(rowEndNew)); |
sheet.setPrintRanges(sNew); |
System.err.println(" ****** Replace print ranges; Old:" + rowEnd + "--" + s + " New:" + rowEndNew + "--" + sNew); |
752,7 → 756,7 |
return nbCellule; |
} |
private void fillTaxeDocumentMap(Element tableau, Sheet sheet, Map<String, Map<Integer, String>> mapStyle, boolean test) { |
private void fillTaxeDocumentMap(Element tableau, Sheet sheet, boolean test) { |
int line = Integer.parseInt(tableau.getAttributeValue("firstLine")); |
List<Element> listElts = tableau.getChildren("element"); |
892,7 → 896,13 |
// on divise en 2 cellules si il y a des retours à la ligne |
if (controleMultiline && value != null && value.toString().indexOf('\n') >= 0) { |
String[] values = value.toString().split("\n"); |
String[] values; |
if (cell.getRowsSpanned() < 2) { |
// TODO : mettre une option pour splitter ou pas |
values = value.toString().split("\n"); |
} else { |
values = new String[] { value.toString() }; |
} |
Point p = sheet.resolveHint(location); |
int y = 0; |
1030,7 → 1040,7 |
/** |
* parcourt l'ensemble de la feuille pour trouver les style définit |
*/ |
private Map<String, Map<Integer, String>> searchStyle(Sheet sheet, int colEnd, int rowEnd) { |
private Map<String, Map<Integer, String>> searchStyle(Sheet sheet, int rowDeb, int colEnd, int rowEnd) { |
if (cacheStyle.get(sheet) != null) { |
return cacheStyle.get(sheet); |
1043,8 → 1053,10 |
System.err.println("End column search : " + columnCount); |
int rowCount = (rowEnd > 0) ? rowEnd : sheet.getRowCount(); |
int start = (rowDeb - 1 > 0) ? rowDeb - 1 : 0; |
System.err.println("End row search : " + rowCount); |
for (int i = 0; i < rowCount; i++) { |
for (int i = start; i < rowCount; i++) { |
int x = 0; |
Map<Integer, String> mapCellStyle = new HashMap<>(); |
String style = ""; |
1119,12 → 1131,13 |
Object oLastColTmp = tableau.getAttributeValue("lastColumn"); |
int lastColumn = -1; |
int endPageLine = Integer.parseInt(tableau.getAttributeValue("endPageLine")); |
if (oLastColTmp != null) { |
lastColumn = sheet.resolveHint(oLastColTmp.toString() + 1).x + 1; |
} |
int firstLine = Integer.parseInt(tableau.getAttributeValue("firstLine")); |
int endLine = Integer.parseInt(tableau.getAttributeValue("endLine")); |
Map<String, Map<Integer, String>> mapStyle = searchStyle(sheet, lastColumn, endPageLine); |
Map<String, Map<Integer, String>> mapStyle = searchStyle(sheet, firstLine, lastColumn, endLine); |
int nbPage = fillTable(tableau, row, sheet, mapStyle, true, rowLanguage); |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/OOgenerationListeXML.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
182,13 → 182,12 |
range[i] = string.subSequence(string.indexOf('.') + 1, string.length()).toString(); |
} |
int rowEnd = -1; |
if (range.length > 1) { |
rowEnd = sheet.resolveHint(range[1]).y + 1; |
int rowEndNew = rowEnd * (nbPage + 1); |
int rowEnd = sheet.resolveHint(range[1]).y + 1; |
int rowEndNew = rowEnd * nbPage; |
String sNew = s.replaceAll(String.valueOf(rowEnd), String.valueOf(rowEndNew)); |
sheet.setPrintRanges(sNew); |
System.err.println(" ****** Replace print ranges; Old:" + rowEnd + "--" + s + " New:" + rowEndNew + "--" + sNew); |
System.err.println("OOgenerationListXML ****** Replace print ranges; Old:" + rowEnd + "--" + s + " New:" + rowEndNew + "--" + sNew); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/OOXMLTableElement.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
83,47 → 83,20 |
} |
public List<? extends SQLRowAccessor> getRows() { |
SQLTable tableElt = Configuration.getInstance().getRoot().findTable(this.tableau.getAttributeValue("table")); |
String foreignTableName = this.tableau.getAttributeValue("table"); |
SQLTable tableElt = null; |
if (!this.row.isEmpty()) { |
SQLTable tableRow = this.row.get(0).getTable(); |
if (tableRow.getDBRoot().contains(foreignTableName)) { |
tableElt = tableRow.getDBRoot().getTable(foreignTableName); |
} else { |
tableElt = Configuration.getInstance().getRoot().findTable(foreignTableName); |
} |
} |
if (tableElt != null) { |
// // #if gestionnx |
// Set<SQLField> fields = |
// tablePourcentService.getForeignKeys(tableElt); |
// |
// if (((ComptaPropsConfiguration) |
// Configuration.getInstance()).customerIsPreventec() && fields != |
// null && fields.size() > 0) { |
// SQLSelect sel = new |
// SQLSelect(Configuration.getInstance().getBase()); |
// |
// sel.addSelectStar(tableElt); |
// Set<SQLField> fieldsElt = |
// tableElt.getForeignKeys(this.row.getTable()); |
// Where w = new Where(fieldsElt.iterator().next(), "=", |
// this.row.getTable().getKey()); |
// w = w.and(new Where(fields.iterator().next(), "=", |
// tableElt.getKey())); |
// w = w.and(new Where(this.row.getTable().getKey(), "=", |
// this.row.getID())); |
// sel.setWhere(w); |
// sel.addFieldOrder(tablePourcentService.getField("ID_VERIFICATEUR")); |
// sel.addFieldOrder(fields.iterator().next()); |
// List<SQLRow> l = (List<SQLRow>) |
// Configuration.getInstance().getBase().getDataSource().execute(sel.asString(), |
// SQLRowListRSH.createFromSelect(sel, tableElt)); |
// |
// // Suppression des doublons |
// List<SQLRow> list = new ArrayList<SQLRow>(); |
// for (SQLRow sqlRow : l) { |
// if (!list.contains(sqlRow)) { |
// list.add(sqlRow); |
// } |
// } |
// return list; |
// } |
// // #endif |
return cache.getReferentRows(this.row, tableElt, this.tableau.getAttributeValue("groupBy"), this.tableau.getAttributeValue("orderBy"), |
Boolean.valueOf(this.tableau.getAttributeValue("expandNomenclature")), this.tableau.getAttributeValue("foreignField"), |
Boolean.valueOf(this.tableau.getAttributeValue("excludeZeroQty"))); |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/OOXMLCache.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
134,10 → 134,18 |
} else { |
for (SQLRowAccessor rowAccess : row) { |
if (rowAccess != null && !rowAccess.isUndefined()) { |
final Object[] array = tableForeign.getForeignKeys(rowAccess.getTable()).toArray(); |
final SQLField field1; |
if (array == null || array.length == 0) { |
field1 = tableForeign.getField("ID_" + tableForeign.getName().replaceAll("_ELEMENT", "")); |
} else { |
field1 = (SQLField) array[0]; |
} |
if (w == null) { |
w = new Where((SQLField) tableForeign.getForeignKeys(rowAccess.getTable()).toArray()[0], "=", rowAccess.getID()); |
w = new Where(field1, "=", rowAccess.getID()); |
} else { |
w = w.or(new Where((SQLField) tableForeign.getForeignKeys(rowAccess.getTable()).toArray()[0], "=", rowAccess.getID())); |
w = w.or(new Where(field1, "=", rowAccess.getID())); |
} |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/SpreadSheetGeneratorCompta.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
71,7 → 71,10 |
System.err.println("Set Column Count to :: " + (colEnd + 1)); |
sheet.setColumnCount(colEnd + 1); |
} |
sheet.duplicateFirstRows(this.nbRowsPerPage, this.nbPage); |
if (this.nbPage < 1) { |
throw new IllegalArgumentException("invalid page count : " + this.nbPage); |
} |
sheet.duplicateFirstRows(this.nbRowsPerPage, this.nbPage - 1); |
Object printRangeObj = sheet.getPrintRanges(); |
if (printRangeObj != null) { |
82,13 → 85,12 |
range2[i] = string.subSequence(string.indexOf('.') + 1, string.length()).toString(); |
} |
int end = -1; |
if (range2.length > 1) { |
end = sheet.resolveHint(range2[1]).y + 1; |
long rowEndNew = end * (this.nbPage + 1); |
int end = sheet.resolveHint(range2[1]).y + 1; |
int rowEndNew = end * this.nbPage; |
String sNew = s.replaceAll(String.valueOf(end), String.valueOf(rowEndNew)); |
sheet.setPrintRanges(sNew); |
System.err.println(" ****** Replace print ranges; Old:" + end + "--" + s + " New:" + rowEndNew + "--" + sNew); |
System.err.println("SpreadSheetGenerator ****** Replace print ranges; Old:" + end + "--" + s + " New:" + rowEndNew + "--" + sNew); |
} |
} else { |
sheet.removePrintRanges(); |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/PDFAttachment.java |
---|
New file |
0,0 → 1,45 |
/* |
* 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.erp.generationDoc; |
public class PDFAttachment { |
private String name; |
private String fileName; |
private byte[] bytes; |
private String mimetype; |
public PDFAttachment(String name, String fileName, byte[] bytes, String mimetype) { |
this.name = name; |
this.fileName = fileName; |
this.bytes = bytes; |
this.mimetype = mimetype; |
} |
public String getName() { |
return name; |
} |
public String getFileName() { |
return this.fileName; |
} |
public byte[] getBytes() { |
return this.bytes; |
} |
public String getMimetype() { |
return this.mimetype; |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/SheetUtils.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the GNU General Public License Version 3 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a |
19,15 → 19,22 |
import org.openconcerto.utils.ExceptionHandler; |
import org.openconcerto.utils.FileUtils; |
import java.awt.Color; |
import java.awt.Desktop; |
import java.io.ByteArrayInputStream; |
import java.io.File; |
import java.io.FileOutputStream; |
import java.io.FilenameFilter; |
import java.io.IOException; |
import java.io.InputStream; |
import java.util.ArrayList; |
import java.util.Calendar; |
import java.util.Collections; |
import java.util.Comparator; |
import java.util.GregorianCalendar; |
import java.util.HashMap; |
import java.util.List; |
import java.util.Map; |
import javax.swing.JOptionPane; |
import javax.swing.SwingUtilities; |
34,9 → 41,13 |
import org.apache.pdfbox.pdmodel.PDDocument; |
import org.apache.pdfbox.pdmodel.PDDocumentInformation; |
import org.apache.pdfbox.pdmodel.PDDocumentNameDictionary; |
import org.apache.pdfbox.pdmodel.PDEmbeddedFilesNameTreeNode; |
import org.apache.pdfbox.pdmodel.PDPage; |
import org.apache.pdfbox.pdmodel.PDPageContentStream; |
import org.apache.pdfbox.pdmodel.common.PDRectangle; |
import org.apache.pdfbox.pdmodel.common.filespecification.PDComplexFileSpecification; |
import org.apache.pdfbox.pdmodel.common.filespecification.PDEmbeddedFile; |
import org.apache.pdfbox.pdmodel.graphics.form.PDFormXObject; |
import org.apache.pdfbox.util.Matrix; |
import org.jopendocument.model.OpenDocument; |
139,7 → 150,12 |
} |
public static void convert2PDF(final OpenDocument doc, final File pdfFileToCreate) throws Exception { |
convert2PDF(doc, pdfFileToCreate, Collections.emptyList()); |
} |
public static void convert2PDF(final OpenDocument doc, final File pdfFileToCreate, final List<PDFAttachment> attachments) throws Exception { |
assert (!SwingUtilities.isEventDispatchThread()); |
System.out.println("SheetUtils convert2PDF " + doc.getLoadedFile().getAbsolutePath() + " -> " + pdfFileToCreate.getAbsolutePath()); |
try { |
PDDocument document = new PDDocument(); |
PDDocumentInformation info = new PDDocumentInformation(); |
149,8 → 165,29 |
info.setModificationDate(Calendar.getInstance()); |
document.setDocumentInformation(info); |
FileOutputStream fileOutputStream = new FileOutputStream(pdfFileToCreate); |
// Attachments |
final PDEmbeddedFilesNameTreeNode efTree = new PDEmbeddedFilesNameTreeNode(); |
final Map<String, PDComplexFileSpecification> efMap = new HashMap<>(); |
for (final PDFAttachment att : attachments) { |
// first create the file specification, which holds the embedded file |
final PDComplexFileSpecification fs = new PDComplexFileSpecification(); |
fs.setFile(att.getFileName()); |
final InputStream is = new ByteArrayInputStream(att.getBytes()); |
final PDEmbeddedFile ef = new PDEmbeddedFile(document, is); |
// set some of the attributes of the embedded file |
ef.setSubtype(att.getMimetype()); |
ef.setSize(att.getBytes().length); |
ef.setCreationDate(new GregorianCalendar()); |
fs.setEmbeddedFile(ef); |
efMap.put(att.getName(), fs); |
} |
efTree.setNames(efMap); |
// attachments are stored as part of the "names" dictionary in the document catalog |
final PDDocumentNameDictionary names = new PDDocumentNameDictionary(document.getDocumentCatalog()); |
names.setEmbeddedFiles(efTree); |
document.getDocumentCatalog().setNames(names); |
PdfBoxGraphics2DFontTextDrawer fontTextDrawer = new PdfBoxGraphics2DFontTextDrawerDefaultFonts(); |
final File dir = new File("Fonts"); |
if (dir.exists()) { |
169,23 → 206,24 |
// Configure the renderer |
ODTRenderer renderer = new ODTRenderer(doc); |
renderer.setIgnoreMargins(false); |
renderer.setResizeFactor(100); |
renderer.setPaintMaxResolution(true); |
PDRectangle pageSize = PDRectangle.A4; |
// Scale the renderer to fit width or height |
final double widthFactor = renderer.getPrintWidth() / pageSize.getWidth(); |
final double heightFactor = renderer.getPrintHeight() / pageSize.getHeight(); |
renderer.setResizeFactor(Math.max(widthFactor, heightFactor)); |
final int resizeFactor = (int) Math.ceil(Math.max(widthFactor, heightFactor)); |
renderer.setResizeFactor(resizeFactor); |
// Print pages |
for (int i = 0; i < renderer.getPrintedPagesNumber(); i++) { |
PDPage page = new PDPage(pageSize); |
final PDPage page = new PDPage(pageSize); |
document.addPage(page); |
PdfBoxGraphics2D g2 = new PdfBoxGraphics2D(document, pageSize.getWidth(), pageSize.getHeight()); |
g2.setFontTextDrawer(fontTextDrawer); |
// centrage horizontal, alignement vertical en haut |
g2.translate((PageSize.A4.getWidth() - renderer.getPrintWidthInPixel()) / 2.0, 0); |
final double hMargin = (pageSize.getWidth() - renderer.getPageWidthInPixel()) / 2.0; |
g2.translate(hMargin, 0); |
// Render |
renderer.setCurrentPage(i); |
199,8 → 237,9 |
contentStream.transform(matrix); |
contentStream.drawForm(xform); |
contentStream.close(); |
} |
document.save(fileOutputStream); |
document.save(pdfFileToCreate); |
// Close the PDF document |
document.close(); |
226,4 → 265,12 |
final File f = new File(file.getParent(), name); |
return f; |
} |
public static void main(String[] args) throws Exception { |
// final OpenDocument doc = new OpenDocument(new File("Documents/Facture_FACT128.ods")); |
final OpenDocument doc = new OpenDocument(new File("../ODSViewer/documents/bug_marges_pdf/Devis.ods")); |
final File file = new File("out.pdf"); |
convert2PDF(doc, file); |
Desktop.getDesktop().open(file); |
} |
} |
/trunk/OpenConcerto/src/org/openconcerto/erp/generationDoc/SheetXml.java |
---|
1,7 → 1,7 |
/* |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
* |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved. |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved. |
* |
* The contents of this file are subject to the terms of the 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,12 |
import org.openconcerto.erp.config.ComptaPropsConfiguration; |
import org.openconcerto.erp.core.common.ui.PreviewFrame; |
import org.openconcerto.erp.core.customerrelationship.mail.EmailTemplate; |
import org.openconcerto.erp.core.customerrelationship.mail.ValueListener; |
import org.openconcerto.erp.core.sales.invoice.report.VenteFactureXmlSheet; |
import org.openconcerto.erp.core.sales.quote.report.PaypalStamper; |
import org.openconcerto.erp.generationDoc.element.TypeModeleSQLElement; |
import org.openconcerto.erp.model.MouseSheetXmlListeListener; |
import org.openconcerto.erp.preferences.PayPalPreferencePanel; |
import org.openconcerto.erp.storage.CloudStorageEngine; |
import org.openconcerto.erp.storage.StorageEngine; |
25,13 → 28,21 |
import org.openconcerto.openoffice.OOUtils; |
import org.jopendocument.link.Component; |
import org.openconcerto.sql.Configuration; |
import org.openconcerto.sql.element.SQLComponent; |
import org.openconcerto.sql.element.SQLElement; |
import org.openconcerto.sql.model.SQLBase; |
import org.openconcerto.sql.model.SQLRow; |
import org.openconcerto.sql.model.SQLRowAccessor; |
import org.openconcerto.sql.model.SQLTable; |
import org.openconcerto.sql.preferences.SQLPreferences; |
import org.openconcerto.sql.view.EditFrame; |
import org.openconcerto.sql.view.EditPanel.EditMode; |
import org.openconcerto.ui.FrameUtil; |
import org.openconcerto.utils.Action; |
import org.openconcerto.utils.ExceptionHandler; |
import org.openconcerto.utils.cc.ITransformer; |
import org.openconcerto.utils.i18n.Grammar; |
import org.openconcerto.utils.i18n.TranslationManager; |
import java.awt.GraphicsEnvironment; |
import java.awt.print.PrinterJob; |
49,7 → 60,9 |
import java.net.URLConnection; |
import java.net.URLEncoder; |
import java.nio.charset.StandardCharsets; |
import java.util.ArrayList; |
import java.util.Arrays; |
import java.util.Collections; |
import java.util.HashMap; |
import java.util.List; |
import java.util.Map; |
140,15 → 153,19 |
return this.elt; |
} |
public Future<SheetXml> showPrintAndExportAsynchronous(final boolean showDocument, final boolean printDocument, final boolean exportToPDF) { |
return showPrintAndExportAsynchronous(showDocument, printDocument, exportToPDF, Collections.emptyList()); |
} |
/** |
* Show, print and export the document to PDF. This method is asynchronous, but is executed in a |
* single threaded queue shared with createDocument |
*/ |
public Future<SheetXml> showPrintAndExportAsynchronous(final boolean showDocument, final boolean printDocument, final boolean exportToPDF) { |
public Future<SheetXml> showPrintAndExportAsynchronous(final boolean showDocument, final boolean printDocument, final boolean exportToPDF, List<Action> actions) { |
final Callable<SheetXml> c = new Callable<SheetXml>() { |
@Override |
public SheetXml call() throws Exception { |
showPrintAndExport(showDocument, printDocument, exportToPDF); |
showPrintAndExport(showDocument, printDocument, exportToPDF, actions); |
return SheetXml.this; |
} |
}; |
156,6 → 173,55 |
} |
public Future<SheetXml> showPrintAndExportAsynchronous(final boolean showDocument, final boolean printDocument, final boolean exportToPDF, final SQLElement element, SQLRow row) { |
final Callable<SheetXml> c = new Callable<SheetXml>() { |
@Override |