OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 17 | Rev 20 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
17 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 /*
15
 * Créé le 28 oct. 2004
16
 */
17
package org.openconcerto.openoffice;
18
 
19 ilm 19
import org.openconcerto.utils.CollectionUtils;
17 ilm 20
import org.openconcerto.xml.JDOMUtils;
21
import org.openconcerto.xml.Validator;
22
 
23
import java.util.ArrayList;
24
import java.util.HashMap;
25
import java.util.Iterator;
26
import java.util.List;
27
import java.util.Map;
28
import java.util.Map.Entry;
19 ilm 29
import java.util.SortedMap;
30
import java.util.TreeMap;
17 ilm 31
import java.util.regex.Matcher;
32
import java.util.regex.Pattern;
33
 
34
import javax.xml.XMLConstants;
35
import javax.xml.validation.Schema;
36
import javax.xml.validation.SchemaFactory;
37
 
38
import org.jdom.Content;
39
import org.jdom.Document;
40
import org.jdom.Element;
41
import org.jdom.JDOMException;
42
import org.jdom.Namespace;
43
import org.jdom.Parent;
44
import org.jdom.Text;
45
import org.jdom.xpath.XPath;
46
import org.xml.sax.SAXException;
47
 
48
/**
49
 * Various bits of OpenDocument XML.
50
 *
51
 * @author Sylvain CUAZ
19 ilm 52
 * @see #get(XMLFormatVersion)
17 ilm 53
 */
19 ilm 54
public abstract class OOXML implements Comparable<OOXML> {
17 ilm 55
 
19 ilm 56
    /**
57
     * If this system property is set to <code>true</code> then {@link #get(XMLFormatVersion)} will
58
     * never return <code>null</code>, allowing to support unknown versions.
59
     */
60
    public static final String LAST_FOR_UNKNOWN_PROP = OOXML.class.getPackage().getName() + ".lastOOXMLForUnknownVersion";
61
    private static final XML_OO instanceOO = new XML_OO();
62
    private static final SortedMap<String, XML_OD> instancesODByDate = new TreeMap<String, XML_OD>();
63
    private static final Map<String, XML_OD> instancesODByVersion = new HashMap<String, XML_OD>();
64
    private static final List<OOXML> values;
65
    private static OOXML defaultInstance;
17 ilm 66
 
19 ilm 67
    static {
68
        register(new XML_OD_1_0());
69
        register(new XML_OD_1_1());
70
        register(new XML_OD_1_2());
71
        values = new ArrayList<OOXML>(instancesODByDate.size() + 1);
72
        values.add(instanceOO);
73
        values.addAll(instancesODByDate.values());
74
 
75
        setDefault(getLast());
76
    }
77
 
78
    private static void register(XML_OD xml) {
79
        assert xml.getVersion() == XMLVersion.OD;
80
        instancesODByDate.put(xml.getDateString(), xml);
81
        instancesODByVersion.put(xml.getFormatVersion().getOfficeVersion(), xml);
82
    }
83
 
17 ilm 84
    /**
85
     * Returns the instance that match the requested version.
86
     *
87
     * @param version the version.
19 ilm 88
     * @return the corresponding instance, <code>null</code> for unsupported versions.
89
     * @see #LAST_FOR_UNKNOWN_PROP
17 ilm 90
     */
19 ilm 91
    public static OOXML get(XMLFormatVersion version) {
92
        return get(version, Boolean.getBoolean(LAST_FOR_UNKNOWN_PROP));
17 ilm 93
    }
94
 
19 ilm 95
    public static OOXML get(XMLFormatVersion version, final boolean lastForUnknown) {
96
        if (version.getXMLVersion() == XMLVersion.OOo) {
97
            return instanceOO;
98
        } else {
99
            final XML_OD res = instancesODByVersion.get(version.getOfficeVersion());
100
            if (res == null && lastForUnknown)
101
                return getLast(version.getXMLVersion());
102
            else
103
                return res;
104
        }
17 ilm 105
    }
106
 
19 ilm 107
    public static OOXML get(Element root) {
108
        return XMLFormatVersion.get(root).getXML();
17 ilm 109
    }
110
 
111
    /**
19 ilm 112
     * Return all known instances in the order they were published.
17 ilm 113
     *
19 ilm 114
     * @return all known instances ordered.
115
     * @see #compareTo(OOXML)
17 ilm 116
     */
19 ilm 117
    static public final List<OOXML> values() {
118
        return values;
119
    }
17 ilm 120
 
19 ilm 121
    static public final OOXML getLast() {
122
        return CollectionUtils.getLast(values);
123
    }
17 ilm 124
 
19 ilm 125
    static public final OOXML getLast(XMLVersion version) {
126
        if (version == XMLVersion.OOo)
127
            return instanceOO;
128
        else
129
            return instancesODByDate.get(instancesODByDate.lastKey());
17 ilm 130
    }
131
 
19 ilm 132
    public static void setDefault(OOXML ns) {
133
        defaultInstance = ns;
134
    }
135
 
136
    public static OOXML getDefault() {
137
        return defaultInstance;
138
    }
139
 
140
    static private final String rt2oo(String content, String tagName, String styleName) {
141
        return content.replaceAll("\\[" + tagName + "\\]", "<text:span text:style-name=\"" + styleName + "\">").replaceAll("\\[/" + tagName + "\\]", "</text:span>");
142
    }
143
 
17 ilm 144
    // *** instances
145
 
19 ilm 146
    private final XMLFormatVersion version;
147
    private final String dateString;
17 ilm 148
 
19 ilm 149
    private OOXML(XMLFormatVersion version, final String dateString) {
17 ilm 150
        this.version = version;
19 ilm 151
        this.dateString = dateString;
17 ilm 152
    }
153
 
19 ilm 154
    /**
155
     * The date the specification was published.
156
     *
157
     * @return the date in "yyyyMMdd" format.
158
     */
159
    public final String getDateString() {
160
        return this.dateString;
161
    }
162
 
163
    /**
164
     * Compare the date the specification was published.
165
     *
166
     * @param o the object to be compared.
167
     * @see #getDateString()
168
     */
169
    @Override
170
    public int compareTo(OOXML o) {
171
        return this.dateString.compareTo(o.dateString);
172
    }
173
 
17 ilm 174
    public final XMLVersion getVersion() {
19 ilm 175
        return this.getFormatVersion().getXMLVersion();
17 ilm 176
    }
177
 
19 ilm 178
    public final XMLFormatVersion getFormatVersion() {
179
        return this.version;
17 ilm 180
    }
181
 
19 ilm 182
    public abstract boolean canValidate();
183
 
17 ilm 184
    /**
185
     * Verify that the passed document is a valid OpenOffice.org 1 or ODF document.
186
     *
187
     * @param doc the xml to test.
188
     * @return a validator on <code>doc</code>.
189
     */
19 ilm 190
    public abstract Validator getValidator(Document doc);
17 ilm 191
 
192
    /**
193
     * Return the names of font face declarations.
194
     *
195
     * @return at index 0 the name of the container element, at 1 the qualified name of its
196
     *         children.
197
     */
19 ilm 198
    public abstract String[] getFontDecls();
17 ilm 199
 
200
    public final Element getLineBreak() {
201
        return new Element("line-break", getVersion().getTEXT());
202
    }
203
 
19 ilm 204
    public abstract Element getTab();
17 ilm 205
 
19 ilm 206
    public abstract String getFrameQName();
17 ilm 207
 
19 ilm 208
    public abstract Element createFormattingProperties(final String family);
209
 
210
    protected final List encodeRT_L(String content, Map<String, String> styles) {
17 ilm 211
        String res = JDOMUtils.OUTPUTTER.escapeElementEntities(content);
19 ilm 212
        for (final Entry<String, String> e : styles.entrySet()) {
213
            res = rt2oo(res, e.getKey(), e.getValue());
17 ilm 214
        }
215
        try {
216
            return JDOMUtils.parseString(res, getVersion().getALL());
217
        } catch (JDOMException e) {
218
            // should not happpen as we did escapeElementEntities which gives valid xml and then
219
            // rt2oo which introduce only static xml
220
            throw new IllegalStateException("could not parse " + res, e);
221
        }
222
    }
223
 
224
    /**
225
     * Convert rich text (with [] tags) into XML.
226
     *
227
     * @param content the string to convert, eg "texte [b]gras[/b]".
228
     * @param styles the mapping from tagname (eg "b") to the name of the character style (eg
229
     *        "Gras").
230
     * @return the corresponding element.
231
     */
19 ilm 232
    public final Element encodeRT(String content, Map<String, String> styles) {
17 ilm 233
        return new Element("span", getVersion().getTEXT()).addContent(encodeRT_L(content, styles));
234
    }
235
 
236
    // create the necessary <text:s c="n"/>
237
    private Element createSpaces(String spacesS) {
238
        return new Element("s", getVersion().getTEXT()).setAttribute("c", spacesS.length() + "", getVersion().getTEXT());
239
    }
240
 
241
    /**
242
     * Encode a String to OO XML. Handles substition of whitespaces to their OO equivalent.
243
     *
244
     * @param s a plain ole String, eg "term\tdefinition".
245
     * @return an Element suitable to be inserted in an OO XML document, eg
246
     *
247
     *         <pre>
248
     *     &lt;text:span&gt;term&lt;text:tab-stop/&gt;definition&lt;/text:span&gt;
249
     * </pre>
250
     *
251
     *         .
252
     */
253
    public final Element encodeWS(final String s) {
254
        return new Element("span", getVersion().getTEXT()).setContent(encodeWSasList(s));
255
    }
256
 
257
    private final List<Content> encodeWSasList(final String s) {
258
        final List<Content> res = new ArrayList<Content>();
259
        final Matcher m = Pattern.compile("\n|\t| {2,}").matcher(s);
260
        int last = 0;
261
        while (m.find()) {
262
            res.add(new Text(s.substring(last, m.start())));
263
            switch (m.group().charAt(0)) {
264
            case '\n':
265
                res.add(getLineBreak());
266
                break;
267
            case '\t':
268
                res.add(getTab());
269
                break;
270
            case ' ':
271
                res.add(createSpaces(m.group()));
272
                break;
273
 
274
            default:
275
                throw new IllegalStateException("unknown item: " + m.group());
276
            }
277
            last = m.end();
278
        }
279
        res.add(new Text(s.substring(last)));
280
        return res;
281
    }
282
 
283
    @SuppressWarnings("unchecked")
284
    public final void encodeWS(final Text t) {
285
        final Parent parent = t.getParent();
286
        final int ind = parent.indexOf(t);
287
        t.detach();
288
        parent.getContent().addAll(ind, encodeWSasList(t.getText()));
289
    }
290
 
291
    @SuppressWarnings("unchecked")
292
    public final Element encodeWS(final Element elem) {
293
        final XPath path;
294
        try {
19 ilm 295
            path = OOUtils.getXPath(".//text()", getVersion());
17 ilm 296
        } catch (JDOMException e) {
297
            // static path, hence always valid
298
            throw new IllegalStateException("cannot create XPath", e);
299
        }
300
        try {
301
            final Iterator iter = new ArrayList(path.selectNodes(elem)).iterator();
302
            while (iter.hasNext()) {
303
                final Text t = (Text) iter.next();
304
                encodeWS(t);
305
            }
306
        } catch (JDOMException e) {
307
            throw new IllegalArgumentException("cannot find text nodes of " + elem, e);
308
        }
309
        return elem;
310
    }
311
 
19 ilm 312
    private static final class XML_OO extends OOXML {
313
        public XML_OO() {
314
            super(XMLFormatVersion.getOOo(), "20020501");
315
        }
316
 
317
        @Override
318
        public boolean canValidate() {
319
            return true;
320
        }
321
 
322
        @Override
323
        public Validator getValidator(Document doc) {
324
            // DTDs are stubborn, xmlns have to be exactly where they want
325
            // in this case the root element
326
            for (final Namespace n : getVersion().getALL())
327
                doc.getRootElement().addNamespaceDeclaration(n);
328
            return new Validator.DTDValidator(doc, OOUtils.getBuilderLoadDTD());
329
        }
330
 
331
        @Override
332
        public String[] getFontDecls() {
333
            return new String[] { "font-decls", "style:font-decl" };
334
        }
335
 
336
        @Override
337
        public final Element getTab() {
338
            return new Element("tab-stop", getVersion().getTEXT());
339
        }
340
 
341
        @Override
342
        public String getFrameQName() {
343
            return "draw:text-box";
344
        }
345
 
346
        @Override
347
        public Element createFormattingProperties(String family) {
348
            return new Element("properties", this.getVersion().getSTYLE());
349
        }
350
    }
351
 
352
    private static class XML_OD extends OOXML {
353
        private final String schemaFile;
354
        private Schema schema = null;
355
 
356
        public XML_OD(final String dateString, final String versionString, final String schemaFile) {
357
            super(XMLFormatVersion.get(XMLVersion.OD, versionString), dateString);
358
            this.schemaFile = schemaFile;
359
        }
360
 
361
        @Override
362
        public boolean canValidate() {
363
            return this.schemaFile != null;
364
        }
365
 
366
        private Schema getSchema() throws SAXException {
367
            if (this.schema == null && this.schemaFile != null) {
368
                this.schema = SchemaFactory.newInstance(XMLConstants.RELAXNG_NS_URI).newSchema(getClass().getResource("oofficeDTDs/" + this.schemaFile));
369
            }
370
            return this.schema;
371
        }
372
 
373
        @Override
374
        public Validator getValidator(Document doc) {
375
            final Schema schema;
376
            try {
377
                schema = this.getSchema();
378
            } catch (SAXException e) {
379
                throw new IllegalStateException("relaxNG schemas pb", e);
380
            }
381
            return schema == null ? null : new Validator.JAXPValidator(doc, schema);
382
        }
383
 
384
        @Override
385
        public final String[] getFontDecls() {
386
            return new String[] { "font-face-decls", "style:font-face" };
387
        }
388
 
389
        @Override
390
        public final Element getTab() {
391
            return new Element("tab", getVersion().getTEXT());
392
        }
393
 
394
        @Override
395
        public String getFrameQName() {
396
            return "draw:frame";
397
        }
398
 
399
        @Override
400
        public Element createFormattingProperties(String family) {
401
            return new Element(family + "-properties", this.getVersion().getSTYLE());
402
        }
403
    }
404
 
405
    private static final class XML_OD_1_0 extends XML_OD {
406
        public XML_OD_1_0() {
407
            super("20061130", "1.0", null);
408
        }
409
    }
410
 
411
    private static final class XML_OD_1_1 extends XML_OD {
412
        public XML_OD_1_1() {
413
            super("20070201", "1.1", "OpenDocument-strict-schema-v1.1.rng");
414
        }
415
    }
416
 
417
    private static final class XML_OD_1_2 extends XML_OD {
418
        public XML_OD_1_2() {
419
            super("20110317", "1.2", "OpenDocument-v1.2-cs01-schema.rng");
420
        }
421
    }
17 ilm 422
}