Statistics
| Revision:

root / branches / v10 / extensions / extI18n / src / org / gvsig / i18n / impl / I18nManagerImpl.java @ 26431

History | View | Annotate | Download (21.4 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2008 Infrastructures and Transports Department
4
 * of the Valencian Gobernment (CIT)
5
 * 
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 2
9
 * of the License, or (at your option) any later version.
10
 * 
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 * 
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 
19
 * MA  02110-1301, USA.
20
 * 
21
 */
22

    
23
/*
24
 * AUTHORS (In addition to CIT):
25
 * 2008 {DiSiD Technologies}  {New extension for installation and update of text translations}
26
 */
27
package org.gvsig.i18n.impl;
28

    
29
import java.io.*;
30
import java.security.AccessController;
31
import java.util.*;
32
import java.util.Map.Entry;
33
import java.util.zip.*;
34

    
35
import org.gvsig.i18n.*;
36

    
37
import sun.security.action.GetPropertyAction;
38

    
39
import com.iver.andami.Launcher;
40
import com.iver.andami.PluginServices;
41
import com.iver.andami.config.generate.AndamiConfig;
42
import com.iver.utiles.StringUtilities;
43
import com.iver.utiles.XMLEntity;
44

    
45
/**
46
 * Implementation of the I18nManager interface.
47
 * 
48
 * @author <a href="mailto:dcervera@disid.com">David Cervera</a>
49
 * @author <a href="mailto:cordin@disid.com">C?sar Ordi?ana</a>
50
 */
51
public class I18nManagerImpl implements I18nManager {
52

    
53
    private static final String LOCALES_FILE_NAME = "locales.csv";
54

    
55
    private static final String CSV_SEPARATOR = ",";
56

    
57
    private static final String I18N_EXTENSION = "org.gvsig.i18n";
58

    
59
    private static final String VARIANT = "variant";
60

    
61
    private static final String COUNTRY = "country";
62

    
63
    private static final String LANGUAGE = "language";
64

    
65
    private static final String REGISTERED_LOCALES_PERSISTENCE = "RegisteredLocales";
66

    
67
    private static final I18nManager DEFAULT = new I18nManagerImpl();
68

    
69
    private Set registeredLocales;
70

    
71
    /**
72
     * The list of default reference locales. The last one will be used to get
73
     * all the keys when translating to a new locale.
74
     */
75
    private Locale[] referenceLocales = new Locale[] { ENGLISH, SPANISH };
76

    
77
    private Locale[] defaultLocales = new Locale[] {
78
    // Default supported locales
79
            new Locale("ca"), // Catalan
80
            new Locale("cs"), // Czech
81
            new Locale("de"), // German
82
            ENGLISH, // English
83
            SPANISH, // Spanish
84
            new Locale("eu"), // Basque
85
            new Locale("fr"), // French
86
            new Locale("gl"), // Galician
87
            new Locale("it"), // Italian
88
            new Locale("pl"), // Polish
89
            new Locale("pt"), // Portuguese
90
            new Locale("ro"), // Romanian
91
            new Locale("zh"), // Chinese
92
    };
93

    
94
    /**
95
     * Returns the unique instance of the I18nManager.
96
     * 
97
     * @return the unique instance
98
     */
99
    public static I18nManager getInstance() {
100
        return DEFAULT;
101
    }
102

    
103
    public static String capitalize(String text) {
104
        // Convert the first letter to uppercase
105
        String capitalLetter = new String(new char[] { Character
106
                .toUpperCase(text.charAt(0)) });
107
        return capitalLetter.concat(text.substring(1));
108
    }
109

    
110
    /**
111
     * Empty constructor.
112
     */
113
    I18nManagerImpl() {
114
    }
115

    
116
    public Locale[] getInstalledLocales() {
117
        if (registeredLocales == null) {
118

    
119
            XMLEntity child = getRegisteredLocalesPersistence();
120

    
121
            // If the list of registered locales is not already persisted,
122
            // this should be the first time gvSIG is run with the I18nPlugin
123
            // so we will take the list of default locales
124
            if (child == null) {
125
                Locale[] defaultLocales = getDefaultLocales();
126
                registeredLocales = new HashSet(defaultLocales.length);
127
                for (int i = 0; i < defaultLocales.length; i++) {
128
                    registeredLocales.add(defaultLocales[i]);
129
                }
130
                storeInstalledLocales();
131
            }
132
            else {
133
                XMLEntity localesEntity = getRegisteredLocalesPersistence();
134
                registeredLocales = new HashSet(localesEntity
135
                        .getChildrenCount());
136
                for (int i = 0; i < localesEntity.getChildrenCount(); i++) {
137
                    XMLEntity localeEntity = localesEntity.getChild(i);
138
                    String language = localeEntity.getStringProperty(LANGUAGE);
139
                    String country = localeEntity.getStringProperty(COUNTRY);
140
                    String variant = localeEntity.getStringProperty(VARIANT);
141
                    Locale locale = new Locale(language, country, variant);
142
                    registeredLocales.add(locale);
143
                }
144
            }
145
        }
146

    
147
        return (Locale[]) registeredLocales
148
                .toArray(new Locale[registeredLocales.size()]);
149
    }
150

    
151
    public void uninstallLocale(Locale locale) throws I18nException {
152
        if (getCurrentLocale().equals(locale) || isReferenceLocale(locale)) {
153
            throw new UninstallLocaleException(locale);
154
        }
155

    
156
        if (registeredLocales.remove(locale)) {
157
            // Remove from the configured locale list
158
            storeInstalledLocales();
159

    
160
            // Remove the resource bundle file
161
            File bundleFile = new File(getResourcesFolder(),
162
                    getResourceFileName(locale));
163

    
164
            if (bundleFile.exists()) {
165
                bundleFile.delete();
166
            }
167
        }
168
    }
169

    
170
    public String getDisplayName(Locale locale) {
171
        return getDisplayName(locale, locale);
172
    }
173

    
174
    public String getDisplayName(Locale locale, Locale displayLocale) {
175
        StringBuffer name = new StringBuffer(getLanguageDisplayName(locale,
176
                displayLocale));
177

    
178
        boolean close = false;
179

    
180
        if (!isEmpty(locale.getCountry())) {
181
            name.append(" (");
182
            name.append(locale.getDisplayCountry(displayLocale));
183
            close = true;
184
        }
185

    
186
        if (!isEmpty(locale.getVariant())) {
187
            if (close) {
188
                name.append(" - ");
189
            }
190
            else {
191
                name.append(" (");
192
                close = true;
193
            }
194
            name.append(locale.getDisplayVariant(displayLocale));
195
        }
196

    
197
        if (close) {
198
            name.append(')');
199
        }
200

    
201
        return name.toString();
202
    }
203

    
204
    private boolean isEmpty(String text) {
205
        return text == null || text.trim().length() == 0;
206
    }
207

    
208
    public String getLanguageDisplayName(Locale locale) {
209
        return getLanguageDisplayName(locale, locale);
210
    }
211

    
212
    public String getLanguageDisplayName(Locale locale, Locale displayLocale) {
213

    
214
        String displayName;
215

    
216
        // Correction for the Basque language display name,
217
        // show it in Basque, not in Spanish
218
        if ("eu".equals(locale.getLanguage())
219
                && "vascuence".equals(locale.getDisplayLanguage())) {
220
            displayName = "Euskera";
221
        }
222
        // Patch for Valencian/Catalan
223
        else if ("ca".equals(locale.getLanguage())) {
224
            displayName = Messages.getText("__catalan");
225
        }
226
        else {
227
            displayName = locale.getDisplayLanguage(displayLocale);
228
        }
229

    
230
        return capitalize(displayName);
231
    }
232

    
233
    public Locale getCurrentLocale() {
234
        return Locale.getDefault();
235
    }
236

    
237
    public Locale getDefaultSystemLocale() {
238
        String language, region, country, variant;
239
        language = (String) AccessController
240
                .doPrivileged(new GetPropertyAction("user.language", "en"));
241
        // for compatibility, check for old user.region property
242
        region = (String) AccessController.doPrivileged(new GetPropertyAction(
243
                "user.region"));
244
        if (region != null) {
245
            // region can be of form country, country_variant, or _variant
246
            int i = region.indexOf('_');
247
            if (i >= 0) {
248
                country = region.substring(0, i);
249
                variant = region.substring(i + 1);
250
            }
251
            else {
252
                country = region;
253
                variant = "";
254
            }
255
        }
256
        else {
257
            country = (String) AccessController
258
                    .doPrivileged(new GetPropertyAction("user.country", ""));
259
            variant = (String) AccessController
260
                    .doPrivileged(new GetPropertyAction("user.variant", ""));
261
        }
262
        return new Locale(language, country, variant);
263
    }
264

    
265
    public void setCurrentLocale(Locale locale) {
266
        AndamiConfig config = Launcher.getAndamiConfig();
267
        config.setLocaleLanguage(locale.getLanguage());
268
        config.setLocaleCountry(locale.getCountry());
269
        config.setLocaleVariant(locale.getVariant());
270
    }
271

    
272
    public Locale[] installLocales(File importFile) throws I18nException {
273
        List importLocales = new ArrayList();
274

    
275
        try {
276
            ZipFile zipFile = new ZipFile(importFile);
277

    
278
            Map locales = getZipFileNonReferenceLocales(zipFile);
279

    
280
            for (Iterator iterator = locales.entrySet().iterator(); iterator
281
                    .hasNext();) {
282
                Entry entry = (Entry) iterator.next();
283

    
284
                String fileName = (String) entry.getKey();
285
                Locale locale = (Locale) entry.getValue();
286
                importLocales.add(locale);
287

    
288
                // Add the locale to the list of installed ones, if it
289
                // is new, otherwise, update the texts.
290
                if (!registeredLocales.contains(locale)) {
291
                    registeredLocales.add(locale);
292
                    storeInstalledLocales();
293
                }
294

    
295
                // Replace the old bundle with the new one
296
                ZipEntry zipEntry = zipFile.getEntry(fileName);
297
                InputStream is = zipFile.getInputStream(zipEntry);
298
                BufferedReader reader = new BufferedReader(
299
                        new InputStreamReader(is));
300

    
301
                File bundleFile = getResourceFile(locale);
302
                FileWriter fileWriter = new FileWriter(bundleFile);
303
                BufferedWriter writer = new BufferedWriter(fileWriter);
304

    
305
                String line;
306
                while ((line = reader.readLine()) != null) {
307
                    writer.write(line);
308
                    writer.write('\n');
309
                }
310
                writer.flush();
311
                writer.close();
312
                fileWriter.close();
313
                reader.close();
314
                is.close();
315
            }
316

    
317
        } catch (Exception ex) {
318
            throw new InstallLocalesException(importFile, ex);
319
        }
320

    
321
        return (Locale[]) importLocales
322
                .toArray(new Locale[importLocales.size()]);
323
    }
324

    
325
    public void exportLocaleForUpdate(Locale locale, Locale referenceLocale,
326
            File exportFile) throws I18nException {
327

    
328
        exportLocale(locale, referenceLocale, exportFile, true);
329
    }
330

    
331
    public void exportLocaleForTranslation(Locale locale,
332
            Locale referenceLocale, File exportFile) throws I18nException {
333

    
334
        exportLocale(locale, referenceLocale, exportFile, false);
335
    }
336

    
337
    public Locale[] getReferenceLocales() {
338
        return referenceLocales;
339
    }
340

    
341
    public void setReferenceLocales(Locale[] referenceLocales) {
342
        this.referenceLocales = referenceLocales;
343
    }
344

    
345
    public void setDefaultLocales(Locale[] defaultLocales) {
346
        this.defaultLocales = defaultLocales;
347
    }
348

    
349
    private void exportLocale(Locale locale, Locale referenceLocale,
350
            File exportFile, boolean update) throws I18nException {
351

    
352
        Locale[] refArray = getReferenceLocalesToExport(locale, referenceLocale);
353

    
354
        try {
355
            FileOutputStream fos = new FileOutputStream(exportFile);
356
            ZipOutputStream zipos = new ZipOutputStream(fos);
357

    
358
            // Create the index file
359
            writeZipFileLocales(zipos, locale, refArray);
360

    
361
            PrintStream ps = new PrintStream(zipos);
362
            Map texts = null;
363

    
364
            if (update) {
365
                // First, export the locale to update
366
                texts = getAllTexts(locale);
367
                putResourceInZip(zipos, ps, texts, getResourceFileName(locale));
368
            }
369
            else { // translate
370
                // First, export the locale to translate, taking the keys from
371
                // the reference locale, but without values
372
                // We will use the keys of the reference locale
373
                texts = getAllTexts(referenceLocale);
374
                putResourceInZip(zipos, ps, texts, getResourceFileName(locale),
375
                        false);
376

    
377
            }
378

    
379
            // Next, export the reference locales
380
            for (int i = 0; i < refArray.length; i++) {
381
                texts = getAllTexts(refArray[i]);
382
                putResourceInZip(zipos, ps, texts,
383
                        getResourceFileName(refArray[i]));
384
            }
385

    
386
            ps.flush();
387
            ps.close();
388
            zipos.close();
389
            fos.close();
390
        } catch (IOException ex) {
391
            throw new ExportLocaleException(locale, ex);
392
        }
393
    }
394

    
395
    /**
396
     * Returns the list of reference locales to export, as the union of the
397
     * default reference locales list and the one selected as reference. The
398
     * locale to translate or update is extracted from the list.
399
     */
400
    private Locale[] getReferenceLocalesToExport(Locale locale,
401
            Locale referenceLocale) {
402
        // The reference locales to export are the default ones plus the
403
        // selected by the user.
404
        Set exportRefLocales = new HashSet(referenceLocales.length);
405
        for (int i = 0; i < referenceLocales.length; i++) {
406
            exportRefLocales.add(referenceLocales[i]);
407
        }
408
        exportRefLocales.add(referenceLocale);
409
        exportRefLocales.remove(locale);
410
        Locale[] refArray = (Locale[]) exportRefLocales
411
                .toArray(new Locale[exportRefLocales.size()]);
412
        return refArray;
413
    }
414

    
415
    /**
416
     * Returns all the localized texts and its keys for a locale.
417
     */
418
    private Map getAllTexts(Locale locale) {
419
        return Messages.getAllTexts(locale);
420
    }
421

    
422
    private Map getZipFileNonReferenceLocales(ZipFile zipFile)
423
            throws I18nException {
424
        ZipEntry zipEntry = zipFile.getEntry(LOCALES_FILE_NAME);
425

    
426
        if (zipEntry == null) {
427
            return null;
428
        }
429

    
430
        Map locales;
431
        try {
432
            InputStream is = zipFile.getInputStream(zipEntry);
433
            BufferedReader reader = new BufferedReader(
434
                    new InputStreamReader(is));
435

    
436
            locales = new HashMap(2);
437
            String line;
438
            while ((line = reader.readLine()) != null) {
439
                // The excepted format is:
440
                // FILENAME,LANGUAGE,COUNTRY,VARIANT,IS_REFERENCE
441
                StringTokenizer st = new StringTokenizer(line, CSV_SEPARATOR,
442
                        true);
443
                // First: locale file name (required)
444
                String fileName = st.nextToken();
445
                if (CSV_SEPARATOR.equals(fileName)) {
446
                    throw new LocaleFileNameRequiredException(line);
447
                }
448
                else {
449
                    // Read the next separator
450
                    st.nextToken();
451
                }
452
                // Second: the locale language (required)
453
                String language = st.nextToken();
454
                if (CSV_SEPARATOR.equals(language)) {
455
                    throw new LocaleLanguageRequiredException(line);
456
                }
457
                else {
458
                    // Read the next separator
459
                    st.nextToken();
460
                }
461
                // Third: the country
462
                String country = st.nextToken();
463
                if (CSV_SEPARATOR.equals(country)) {
464
                    country = null;
465
                }
466
                else {
467
                    // Read the next separator
468
                    st.nextToken();
469
                }
470
                // Fourth: the variant
471
                String variant = st.nextToken();
472
                if (CSV_SEPARATOR.equals(variant)) {
473
                    variant = null;
474
                }
475
                else {
476
                    // Read the next separator
477
                    st.nextToken();
478
                }
479
                // Fifth: is a reference locale?
480
                String refStr = st.nextToken();
481
                if (CSV_SEPARATOR.equals(refStr)) {
482
                    refStr = null;
483
                }
484

    
485
                // Only add non reference locales
486
                if (refStr != null && !"true".equals(refStr.toLowerCase())) {
487
                    // Variant only accepted if country defined
488
                    if (country == null) {
489
                        variant = null;
490
                    }
491
                    country = country == null ? "" : country;
492
                    variant = variant == null ? "" : variant;
493
                    Locale locale = new Locale(language, country, variant);
494

    
495
                    locales.put(fileName, locale);
496
                }
497
            }
498

    
499
            reader.close();
500
            is.close();
501
        } catch (IOException ex) {
502
            throw new ReadCSVLocalesFileException(ex);
503
        }
504

    
505
        return locales;
506
    }
507

    
508
    private void writeZipFileLocales(ZipOutputStream zos, Locale locale,
509
            Locale[] referenceLocales) throws IOException {
510
        ZipEntry zipEntry = new ZipEntry(LOCALES_FILE_NAME);
511

    
512
        zos.putNextEntry(zipEntry);
513
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(zos));
514

    
515
        writeLocaleEntry(locale, writer, false);
516
        for (int i = 0; i < referenceLocales.length; i++) {
517
            writeLocaleEntry(referenceLocales[i], writer, true);
518
        }
519

    
520
        writer.flush();
521
        zos.closeEntry();
522
    }
523

    
524
    /**
525
     * Writes the locale entry into a writer.
526
     * 
527
     * @param locale
528
     *            the locale to create the entry for
529
     * @param writer
530
     *            to write to
531
     * @param reference
532
     *            is it is a reference locale or not
533
     * @throws IOException
534
     *             if there is an error creating the locale entry
535
     */
536
    private void writeLocaleEntry(Locale locale, BufferedWriter writer,
537
            boolean reference) throws IOException {
538
        String language = locale.getLanguage();
539
        String country = locale.getCountry();
540
        country = country == null ? "" : country;
541
        String variant = locale.getVariant();
542
        variant = variant == null ? "" : variant;
543

    
544
        writer.write(getResourceFileName(locale));
545
        writer.write(',');
546
        writer.write(language);
547
        writer.write(',');
548
        writer.write(country);
549
        writer.write(',');
550
        writer.write(variant);
551
        writer.write(',');
552
        writer.write(Boolean.toString(reference));
553
        writer.write('\n');
554
    }
555

    
556
    /**
557
     * Returns if a locale is one of the default reference ones.
558
     */
559
    private boolean isReferenceLocale(Locale locale) {
560
        for (int i = 0; i < referenceLocales.length; i++) {
561
            if (referenceLocales[i].equals(locale)) {
562
                return true;
563
            }
564
        }
565
        return false;
566
    }
567

    
568
    /**
569
     * Puts a new resource file into a Jar file.
570
     */
571
    private void putResourceInZip(ZipOutputStream zipos, PrintStream ps,
572
            Map texts, String resourceFileName) throws IOException {
573

    
574
        putResourceInZip(zipos, ps, texts, resourceFileName, true);
575
    }
576

    
577
    /**
578
     * Puts a new resource file into a Jar file.
579
     */
580
    private void putResourceInZip(ZipOutputStream zipos, PrintStream ps,
581
            Map texts, String resourceFileName, boolean withValue)
582
            throws IOException {
583
        // Add ZIP entry for the resource bundle file
584
        zipos.putNextEntry(new ZipEntry(resourceFileName));
585

    
586
        for (Iterator iterator = texts.entrySet().iterator(); iterator
587
                .hasNext();) {
588
            Entry entry = (Entry) iterator.next();
589
            String keyEncoded = escape((String) entry.getKey(), true);
590
            ps.print(keyEncoded);
591
            ps.print("=");
592
            if (withValue) {
593
                String valueEncoded = escape((String) entry.getValue(), false);
594
                ps.println(valueEncoded);
595
            }
596
            else {
597
                ps.println();
598
            }
599
        }
600

    
601
        ps.flush();
602

    
603
        // Close the ZIP entry, the file is complete
604
        zipos.closeEntry();
605
    }
606

    
607
    /**
608
     * Returns the file which contains the translations for a locale.
609
     */
610
    private File getResourceFile(Locale locale) {
611
        return new File(getResourcesFolder(), getResourceFileName(locale));
612
    }
613

    
614
    /**
615
     * Returns the name of the file which contains the translations for a
616
     * locale.
617
     */
618
    private String getResourceFileName(Locale locale) {
619
        StringBuffer fileName = new StringBuffer("text");
620

    
621
        // Spanish without country is the default locale
622
        if (!(isEmpty(locale.getCountry()) && "es".equals(locale.getLanguage()))) {
623
            fileName.append('_').append(locale.getLanguage());
624
        }
625

    
626
        // Add the locale country
627
        if (!isEmpty(locale.getCountry())) {
628
            fileName.append('_').append(locale.getCountry());
629
        }
630

    
631
        // Add the locale variant
632
        if (!isEmpty(locale.getVariant())) {
633
            fileName.append('_').append(locale.getVariant());
634
        }
635

    
636
        fileName.append(".properties");
637
        return fileName.toString();
638
    }
639

    
640
    /**
641
     * Returns the folder where to store the resource bundle files.
642
     */
643
    private File getResourcesFolder() {
644
        return PluginServices.getPluginServices("com.iver.cit.gvsig")
645
                .getPluginDirectory();
646
    }
647

    
648
    /**
649
     * Returns the child XMLEntity with the RegisteredLocales.
650
     */
651
    private XMLEntity getRegisteredLocalesPersistence() {
652
        XMLEntity entity = getI18nPersistence();
653
        XMLEntity child = null;
654
        for (int i = entity.getChildrenCount() - 1; i >= 0; i--) {
655
            XMLEntity tmpchild = entity.getChild(i);
656
            if (tmpchild.getName().equals(REGISTERED_LOCALES_PERSISTENCE)) {
657
                child = tmpchild;
658
                break;
659
            }
660
        }
661
        return child;
662
    }
663

    
664
    /**
665
     * Returns the I18n Plugin persistence.
666
     */
667
    private XMLEntity getI18nPersistence() {
668
        XMLEntity entity = PluginServices.getPluginServices(I18N_EXTENSION)
669
                .getPersistentXML();
670
        return entity;
671
    }
672

    
673
    /**
674
     * Returns the list of default locales bundled with gvSIG.
675
     */
676
    private Locale[] getDefaultLocales() {
677
        return defaultLocales;
678
    }
679

    
680
    /**
681
     * Stores the list of installed locales into the plugin persistence.
682
     */
683
    private void storeInstalledLocales() {
684
        XMLEntity localesEntity = getRegisteredLocalesPersistence();
685

    
686
        // Remove the previous list of registered languages
687
        if (localesEntity != null) {
688
            XMLEntity i18nPersistence = getI18nPersistence();
689
            for (int i = i18nPersistence.getChildrenCount() - 1; i >= 0; i--) {
690
                XMLEntity child = i18nPersistence.getChild(i);
691
                if (child.getName().equals(REGISTERED_LOCALES_PERSISTENCE)) {
692
                    i18nPersistence.removeChild(i);
693
                    break;
694
                }
695
            }
696
        }
697

    
698
        // Create the new persistence for the registered languages
699
        localesEntity = new XMLEntity();
700

    
701
        localesEntity.setName(REGISTERED_LOCALES_PERSISTENCE);
702

    
703
        for (Iterator iterator = registeredLocales.iterator(); iterator
704
                .hasNext();) {
705
            Locale locale = (Locale) iterator.next();
706
            XMLEntity localeEntity = new XMLEntity();
707
            localeEntity.setName(locale.getDisplayName());
708
            localeEntity.putProperty(LANGUAGE, locale.getLanguage());
709
            localeEntity.putProperty(COUNTRY, locale.getCountry());
710
            localeEntity.putProperty(VARIANT, locale.getVariant());
711

    
712
            localesEntity.addChild(localeEntity);
713
        }
714

    
715
        getI18nPersistence().addChild(localesEntity);
716
    }
717

    
718
    private String escape(String value, boolean replaceBlanks) {
719
        // First replace non printable characters
720
        if (replaceBlanks) {
721
            value = StringUtilities.replace(value, " ", "\\ ");
722
        }
723
        value = StringUtilities.replace(value, ":", "\\:");
724
        value = StringUtilities.replace(value, "\n", "\\n");
725
        value = StringUtilities.replace(value, "\t", "\\t");
726
        value = StringUtilities.replace(value, "\b", "\\b");
727
        value = StringUtilities.replace(value, "\f", "\\f");
728
        value = StringUtilities.replace(value, "\r", "\\r");
729
        // value = StringUtilities.replace(value, "\\", "\\\\");
730
        // value = StringUtilities.replace(value, "\'", "\\\'");
731
        // value = StringUtilities.replace(value, "\"", "\\\"");
732

    
733
        // Next, encode in raw-unicode-escape
734
        return toRawUnicodeEncoded(value);
735
    }
736

    
737
    private String toRawUnicodeEncoded(String value) {
738
        StringBuffer sb = new StringBuffer();
739
        for (int i = 0; i < value.length(); i++) {
740
            char c = value.charAt(i);
741
            if (c <= 0x80) {
742
                sb.append(c);
743
            }
744
            else {
745
                sb.append("\\u");
746
                String hexValue = Integer.toHexString((int) c);
747
                // Append 0 if the hex value has less than 4 digits
748
                for (int j = hexValue.length(); j < 4; j++) {
749
                    sb.append('0');
750
                }
751
                sb.append(hexValue);
752
            }
753
        }
754
        return sb.toString();
755
    }
756
}