OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 19 | Go to most recent revision | Details | 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
import org.openconcerto.xml.JDOMUtils;
20
import org.openconcerto.xml.Validator;
21
 
22
import java.util.ArrayList;
23
import java.util.HashMap;
24
import java.util.Iterator;
25
import java.util.List;
26
import java.util.Map;
27
import java.util.Map.Entry;
28
import java.util.regex.Matcher;
29
import java.util.regex.Pattern;
30
 
31
import javax.xml.XMLConstants;
32
import javax.xml.validation.Schema;
33
import javax.xml.validation.SchemaFactory;
34
 
35
import org.apache.commons.collections.Transformer;
36
import org.apache.commons.collections.map.LazyMap;
37
import org.jdom.Content;
38
import org.jdom.Document;
39
import org.jdom.Element;
40
import org.jdom.JDOMException;
41
import org.jdom.Namespace;
42
import org.jdom.Parent;
43
import org.jdom.Text;
44
import org.jdom.xpath.XPath;
45
import org.xml.sax.SAXException;
46
 
47
/**
48
 * Various bits of OpenDocument XML.
49
 *
50
 * @author Sylvain CUAZ
51
 * @see #get(XMLVersion)
52
 */
53
public class OOXML {
54
 
55
    private static final Map instances = LazyMap.decorate(new HashMap(), new Transformer() {
56
        public Object transform(Object input) {
57
            return new OOXML((XMLVersion) input);
58
        }
59
    });
60
 
61
    /**
62
     * Returns the instance that match the requested version.
63
     *
64
     * @param version the version.
65
     * @return the corresponding instance.
66
     */
67
    public static OOXML get(XMLVersion version) {
68
        return (OOXML) instances.get(version);
69
    }
70
 
71
    static public final String getLineBreakS() {
72
        return "<text:line-break/>";
73
    }
74
 
75
    static private final String rt2oo(String content, String tagName, String styleName) {
76
        return content.replaceAll("\\[" + tagName + "\\]", "<text:span text:style-name=\"" + styleName + "\">").replaceAll("\\[/" + tagName + "\\]", "</text:span>");
77
    }
78
 
79
    /**
80
     * Encode spaces for OpenOffice 1, and escape characters for XML.
81
     *
82
     * @param s a string to encode, eg "hi\n 3<4".
83
     * @return the string encoded in XML, eg "hi<text:line-break/><text:s text:c="2"/>3&lt;4".
84
     * @deprecated see {@link #encodeWS(String)}
85
     */
86
    static public final String encodeOOWS(final String s) {
87
        String tmp = JDOMUtils.OUTPUTTER.escapeElementEntities(s).replaceAll("\n", getLineBreakS()).replaceAll("\t", OOXML.get(XMLVersion.OOo).getTabS());
88
 
89
        String res = "";
90
        // les séries de plus d'un espace consécutifs
91
        final Pattern p = Pattern.compile("  +");
92
        final Matcher m = p.matcher(tmp);
93
        int lastEnd = 0;
94
        while (m.find()) {
95
            // c == le nombre d'espaces
96
            res += tmp.substring(lastEnd, m.start()) + "<text:s text:c=\"" + (m.group().length()) + "\"/>";
97
            lastEnd = m.end();
98
        }
99
        res += tmp.substring(lastEnd);
100
 
101
        return res;
102
    }
103
 
104
    // *** instances
105
 
106
    private final XMLVersion version;
107
    private Schema schema = null;
108
 
109
    private OOXML(XMLVersion version) {
110
        this.version = version;
111
    }
112
 
113
    public final XMLVersion getVersion() {
114
        return this.version;
115
    }
116
 
117
    private Schema getSchema() throws SAXException {
118
        if (this.schema == null) {
119
            this.schema = SchemaFactory.newInstance(XMLConstants.RELAXNG_NS_URI).newSchema(getClass().getResource("oofficeDTDs/OpenDocument-strict-schema-v1.1.rng"));
120
        }
121
        assert this.schema != null;
122
        return this.schema;
123
    }
124
 
125
    /**
126
     * Verify that the passed document is a valid OpenOffice.org 1 or ODF document.
127
     *
128
     * @param doc the xml to test.
129
     * @return a validator on <code>doc</code>.
130
     */
131
    public Validator getValidator(Document doc) {
132
        if (this.getVersion() == XMLVersion.OD) {
133
            final Schema schema;
134
            try {
135
                schema = this.getSchema();
136
            } catch (SAXException e) {
137
                throw new IllegalStateException("relaxNG schemas pb", e);
138
            }
139
            return new Validator.JAXPValidator(doc, schema);
140
        } else {
141
            // DTDs are stubborn, xmlns have to be exactly where they want
142
            // in this case the root element
143
            for (final Namespace n : getVersion().getALL())
144
                doc.getRootElement().addNamespaceDeclaration(n);
145
            return new Validator.DTDValidator(doc, OOUtils.getBuilderLoadDTD());
146
        }
147
    }
148
 
149
    /**
150
     * Return the names of font face declarations.
151
     *
152
     * @return at index 0 the name of the container element, at 1 the qualified name of its
153
     *         children.
154
     */
155
    public String[] getFontDecls() {
156
        if (this.getVersion() == XMLVersion.OOo)
157
            return new String[] { "font-decls", "style:font-decl" };
158
        else
159
            return new String[] { "font-face-decls", "style:font-face" };
160
    }
161
 
162
    public final Element getLineBreak() {
163
        return new Element("line-break", getVersion().getTEXT());
164
    }
165
 
166
    public final Element getTab() {
167
        return new Element(this.getVersion().equals(XMLVersion.OD) ? "tab" : "tab-stop", getVersion().getTEXT());
168
    }
169
 
170
    /**
171
     * How to encode a tab.
172
     *
173
     * @return the xml string to encode a tab.
174
     * @deprecated use {@link #getTab()}
175
     */
176
    public final String getTabS() {
177
        return this.getVersion().equals(XMLVersion.OD) ? "<text:tab/>" : "<text:tab-stop/>";
178
    }
179
 
180
    protected final List encodeRT_L(String content, Map styles) {
181
        String res = JDOMUtils.OUTPUTTER.escapeElementEntities(content);
182
        final Iterator iter = styles.entrySet().iterator();
183
        while (iter.hasNext()) {
184
            final Entry e = (Entry) iter.next();
185
            res = rt2oo(res, (String) e.getKey(), (String) e.getValue());
186
        }
187
        try {
188
            return JDOMUtils.parseString(res, getVersion().getALL());
189
        } catch (JDOMException e) {
190
            // should not happpen as we did escapeElementEntities which gives valid xml and then
191
            // rt2oo which introduce only static xml
192
            throw new IllegalStateException("could not parse " + res, e);
193
        }
194
    }
195
 
196
    /**
197
     * Convert rich text (with [] tags) into XML.
198
     *
199
     * @param content the string to convert, eg "texte [b]gras[/b]".
200
     * @param styles the mapping from tagname (eg "b") to the name of the character style (eg
201
     *        "Gras").
202
     * @return the corresponding element.
203
     */
204
    public final Element encodeRT(String content, Map styles) {
205
        return new Element("span", getVersion().getTEXT()).addContent(encodeRT_L(content, styles));
206
    }
207
 
208
    // create the necessary <text:s c="n"/>
209
    private Element createSpaces(String spacesS) {
210
        return new Element("s", getVersion().getTEXT()).setAttribute("c", spacesS.length() + "", getVersion().getTEXT());
211
    }
212
 
213
    /**
214
     * Encode a String to OO XML. Handles substition of whitespaces to their OO equivalent.
215
     *
216
     * @param s a plain ole String, eg "term\tdefinition".
217
     * @return an Element suitable to be inserted in an OO XML document, eg
218
     *
219
     *         <pre>
220
     *     &lt;text:span&gt;term&lt;text:tab-stop/&gt;definition&lt;/text:span&gt;
221
     * </pre>
222
     *
223
     *         .
224
     */
225
    public final Element encodeWS(final String s) {
226
        return new Element("span", getVersion().getTEXT()).setContent(encodeWSasList(s));
227
    }
228
 
229
    private final List<Content> encodeWSasList(final String s) {
230
        final List<Content> res = new ArrayList<Content>();
231
        final Matcher m = Pattern.compile("\n|\t| {2,}").matcher(s);
232
        int last = 0;
233
        while (m.find()) {
234
            res.add(new Text(s.substring(last, m.start())));
235
            switch (m.group().charAt(0)) {
236
            case '\n':
237
                res.add(getLineBreak());
238
                break;
239
            case '\t':
240
                res.add(getTab());
241
                break;
242
            case ' ':
243
                res.add(createSpaces(m.group()));
244
                break;
245
 
246
            default:
247
                throw new IllegalStateException("unknown item: " + m.group());
248
            }
249
            last = m.end();
250
        }
251
        res.add(new Text(s.substring(last)));
252
        return res;
253
    }
254
 
255
    @SuppressWarnings("unchecked")
256
    public final void encodeWS(final Text t) {
257
        final Parent parent = t.getParent();
258
        final int ind = parent.indexOf(t);
259
        t.detach();
260
        parent.getContent().addAll(ind, encodeWSasList(t.getText()));
261
    }
262
 
263
    @SuppressWarnings("unchecked")
264
    public final Element encodeWS(final Element elem) {
265
        final XPath path;
266
        try {
267
            path = OOUtils.getXPath(".//text()", XMLVersion.getVersion(elem));
268
        } catch (JDOMException e) {
269
            // static path, hence always valid
270
            throw new IllegalStateException("cannot create XPath", e);
271
        }
272
        try {
273
            final Iterator iter = new ArrayList(path.selectNodes(elem)).iterator();
274
            while (iter.hasNext()) {
275
                final Text t = (Text) iter.next();
276
                encodeWS(t);
277
            }
278
        } catch (JDOMException e) {
279
            throw new IllegalArgumentException("cannot find text nodes of " + elem, e);
280
        }
281
        return elem;
282
    }
283
 
284
}