001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.visualweb.insync.faces;
043:
044: import org.netbeans.modules.visualweb.api.designer.markup.MarkupService;
045: import org.netbeans.modules.visualweb.insync.InSyncServiceProvider;
046: import org.apache.xml.serialize.HTMLdtd;
047: import org.openide.filesystems.FileObject;
048: import org.openide.util.NbBundle;
049: import org.w3c.dom.Document;
050: import org.w3c.dom.Element;
051: import org.w3c.dom.Node;
052:
053: /**
054: * Class managing XHTML entity lookups
055: *
056: * @author Tor Norbye
057: */
058: public final class Entities {
059:
060: private Entities() {
061: }
062:
063: // <move from designer/markup>
064: public static String expandHtmlEntities(String html) {
065: return expandHtmlEntities(html, true, null);
066: }
067:
068: public static String expandHtmlEntities(String html, boolean warn) {
069: return expandHtmlEntities(html, warn, null);
070: }
071:
072: public static String expandHtmlEntities(String html, boolean warn,
073: Node node) {
074: FileObject fileObject = null;
075: int lineNumber = -1;
076: if (node != null) {
077: if (node.getNodeType() == Node.TEXT_NODE) {
078: node = node.getParentNode();
079: }
080:
081: Element element = MarkupService
082: .getCorrespondingSourceElement((Element) node);
083:
084: if (element != null) {
085: Document doc = element.getOwnerDocument();
086: // <markup_separation>
087: // MarkupUnit unit = doc.getMarkup();
088: // if (unit != null) {
089: // fileObject = unit.getFileObject();
090: // lineNumber = unit.computeLine(element);
091: // }
092: // ====
093: fileObject = InSyncServiceProvider.get().getFileObject(
094: doc);
095: lineNumber = InSyncServiceProvider.get().computeLine(
096: doc, element);
097: // </markup_seaparation>
098: }
099: }
100:
101: return getExpandedString(html, warn, fileObject, lineNumber);
102: }
103:
104: // </move from designer/markup>
105:
106: /** <em>Note:</em> This comment was before for jspxToHtml which impl
107: * was calling this expand method, I am not sure it the comment is still r
108: * <p>
109: * Translate a JSPX string to an XHTML string:
110: * e.g. "&nbsp;" will translate to " ".
111: * In other words, it expands the standard 5 XML entities
112: * and leaves everything else in place.
113: * @param jspx The string to be converted to xhtml. Note that this
114: * string should ONLY represent jspx "text nodes", not stuff like
115: * "<jsp:root...".
116: * @param warn If true, emit warnings to the output window if
117: * problems are encountered, like illegal entities.
118: * @return An xhtml representation of the jspx string
119:
120: * This is an over-simplification. Here's the full sequence
121: * Mark Roth sent me:
122:
123:
124: Well the entire transformation is to just run the JSP engine, so I'll point you to the JSP spec.
125:
126: But I guess what you're looking for is a reasonable approximation of at least the template text portion of the output.
127:
128: This is off the top of my head, so I might be missing something, but to go from the XML-syntax JSP to the ASCII output looks something like this:
129:
130: 1. Strip all XML metainformation - that will be consumed by the
131: parser and will not appear in the output. This includes the
132: XML prolog, doctypes, etc.
133: 2. Strip all comments
134: 3. Remove <jsp:root>
135: 4. Ignore or handle JSP standard actions (anything in the JSP
136: namespace), except for <jsp:text>. The body of <jsp:text> is echoed
137: verbatim, including all whitespace before the first character and
138: after the last character in the body.
139: 5. Strip all text nodes that are only whitespace (with the exception
140: of jsp:text).
141: 6. Any elements that are not in a tag library namespace should be echoed
142: as their ASCII equivalent, including attributes and their values.
143: Just output the literal values of the attributes. Do not perform
144: any escaping. For example, if the input document has "<" as
145: part of an attribute value, output the literal "<" - do not
146: escape it further.
147: 7. Any template text in the body of elements should be echoed as its
148: ASCII equivalent (same note as above on escaping).
149:
150: If you're dealing with JSP 2.0, there are a few other things to keep in mind:
151:
152: 1. EL expressions in attributes and template text should be handled
153: Keep in mind the escaping rules for EL expressions
154: 2. <jsp:output> should be processed and handled
155: 3. There might not be a <jsp:root> element
156:
157: I think this can be emulated fairly simply by parsing the XML with JAXP and constructing a DOM tree. Then, walk the DOM tree and produce an ASCII stream (not another DOM tree) by echoing the tags and their attributes as ASCII, and echoing the text nodes of the tag verbatim as per above.
158:
159: Once you have the ASCII stream, parse it with JAXP to produce your final DOM. You may get errors, and that's what you can use to identify errors in the source document. For example, the source JSPX:
160:
161: ...<b>1 < 2</b>...
162:
163: should be flagged as an error if you know the target language is XHTML, since the output ASCII stream would be the following:
164:
165: ...<b>1 < 2</b>...
166:
167: which is invalid XML. The XML parser will catch this and give you a parse error which you can present to the user in the tool. I would beef up the error message to include a suggestion for how to fix it:
168:
169: ...<b>1 &lt; 2</b>...
170:
171: Does this help? Let me know if you need more details.
172:
173: By the way, if you want the output to be valid XHTML, you'd want to make sure the user adds a DOCTYPE to the output. This can be done as a child of <jsp:root> using a <![CDATA[...]]> element.
174:
175: Also by the way, instead of creating a brand-new JSPX parser inside Creator, I think a better long-term strategy is to use the parser from Tomcat and just walk the tree that it produces. The NetBeans team is already talking to the Tomcat team about exposing a more formal contract for the parser APIs.
176:
177: * @param jspx The jspx string to be converted to xhtml
178: * @param warn Whether to interactively warn the user about errors
179: * @param unit Corresponding markup unit, if any. Can be null, but must not
180: * be null when element is not null.
181: * @param element If not null, points to the nearest element to the error
182: * which can be used to produce a clickable error line
183: */
184: private static String getExpandedString(String unexpanded,
185: boolean warn, FileObject fileObject, int lineNumber) {
186: if (unexpanded.indexOf('&') == -1) { // todo: keep index and copy up to it below
187: return unexpanded;
188: }
189: int n = unexpanded.length();
190: int nm1 = n - 1;
191:
192: // IMPORTANT NOTE: Keeps this code in sync with getJspxOffset below!
193:
194: StringBuffer sb = new StringBuffer(n);
195: for (int i = 0; i < n; i++) {
196: char c = unexpanded.charAt(i);
197: if (c == '&' && i < nm1) {
198: // Locate entity
199: int begin = i + 1;
200: int end = begin;
201: while (end < n && unexpanded.charAt(end) != ';'
202: && (end - begin <= 10)) { // longest entity is 8 chars
203: end++;
204: }
205: if (end == n || unexpanded.charAt(end) != ';') {
206: if (warn) {
207: String message = NbBundle.getMessage(
208: Entities.class, "LBL_IllegalEntity",
209: unexpanded.substring(begin - 1));
210: if (fileObject != null && lineNumber > -1) {
211: InSyncServiceProvider.get()
212: .getRaveErrorHandler()
213: .displayErrorForFileObject(message,
214: fileObject, lineNumber, 0);
215: } else {
216: InSyncServiceProvider.get()
217: .getRaveErrorHandler()
218: .displayError(message);
219: }
220: }
221: sb.append('&'); // browsers show the &
222: continue;
223: }
224: String entity = unexpanded.substring(begin, end);
225: int e = expand(entity);
226: if (e == -1) {
227: if (warn) {
228: String message = NbBundle.getMessage(
229: Entities.class, "LBL_NoSuchEntity",
230: entity);
231: if (fileObject != null && lineNumber > -1) {
232: InSyncServiceProvider.get()
233: .getRaveErrorHandler()
234: .displayErrorForFileObject(message,
235: fileObject, lineNumber, 0);
236: } else {
237: InSyncServiceProvider.get()
238: .getRaveErrorHandler()
239: .displayError(message);
240: }
241: }
242: sb.append('&'); // browsers show the &
243: continue;
244: } else {
245: sb.append((char) e);
246: i = end;
247: }
248: } else {
249: sb.append(c);
250: }
251: }
252: return sb.toString();
253: }
254:
255: /**
256: * Computes the position in the unexpanded string
257: * which corresponds to the given offset (expandedOffset) in the
258: * expanded string.
259: * @param unexpanded unexpanded string
260: * @param expanededOffset offset in the corresponding expanded string
261: * @return offset in the unexpanded string
262: */
263: public static int getUnexpandedOffset(String unexpanded,
264: int expandedOffset) {
265:
266: // IMPORTANT NOTE: Keep this code in sync with the expand method above
267:
268: int n = unexpanded.length();
269: int nm1 = n - 1;
270: int offset = 0;
271: for (int i = 0; i < n; i++) {
272: if (offset == expandedOffset) {
273: return i;
274: }
275: char c = unexpanded.charAt(i);
276: if (c == '&' && i < nm1) {
277: // Locate entity
278: int begin = i + 1;
279: int end = begin;
280: while (end < n && unexpanded.charAt(end) != ';'
281: && (end - begin <= 10)) { // longest entity is 8 chars
282: end++;
283: }
284: if (end == n || unexpanded.charAt(end) != ';') {
285: offset++; // for & displayed by browsers
286: continue;
287: }
288: String entity = unexpanded.substring(begin, end);
289: int e = expand(entity);
290: if (e == -1) {
291: offset++; // for & displayed by browsers
292: continue;
293: } else {
294: offset++;
295: i = end;
296: }
297: } else {
298: offset++;
299: }
300: }
301: return n;
302: }
303:
304: /**
305: * Computes the position in the expanded string
306: * which corresponds to the given offset (unexpandedOffset) in the
307: * unexpanded string.
308: * @param unexpanded unexpanded string
309: * @param unexpanededOffset offset in the unexpanded string
310: * @return offset in the corresponding expanded string
311: */
312: public static int getExpandedOffset(String unexpanded,
313: int unexpandedOffset) {
314:
315: // IMPORTANT NOTE: Keep this code in sync with the method above
316:
317: int n = unexpanded.length();
318: int nm1 = n - 1;
319: int offset = 0;
320: if (unexpandedOffset > n) {
321: unexpandedOffset = n;
322: }
323: for (int i = 0; i < unexpandedOffset; i++) {
324: char c = unexpanded.charAt(i);
325: if (c == '&' && i < nm1) {
326: // Locate entity
327: int begin = i + 1;
328: int end = begin;
329: while (end < n && unexpanded.charAt(end) != ';'
330: && (end - begin <= 10)) { // longest entity is 8 chars
331: end++;
332: }
333: if (end == n || unexpanded.charAt(end) != ';') {
334: offset++; // for & displayed by browsers
335: continue;
336: }
337: String entity = unexpanded.substring(begin, end);
338: int e = expand(entity);
339: if (e == -1) {
340: offset++; // for & displayed by browsers
341: continue;
342: } else {
343: offset++;
344: i = end;
345: }
346: } else {
347: offset++;
348: }
349: }
350: return offset;
351: }
352:
353: /** Look up the given entity and return the corresponding
354: * character. It actually returns an int that can be cast
355: * to a char, but the return value may be -1 to indicate that
356: * the entity is unknown.
357: */
358: private static int expand(String entity) {
359: if (entity.length() > 0) {
360: if (entity.charAt(0) == '#') {
361: // TODO - do I have to map the parsed integer to
362: // the ISO 10646 decimal character set, or is unicode
363: // a subset of that already?
364: return parseNumericReference(entity);
365: } else {
366: //return v.get(entity);
367: return HTMLdtd.charFromName(entity);
368: }
369: }
370: return -1;
371: }
372:
373: /** Very similar to Integer.parseInt (based on it), but doesn't
374: * panic when number strings have a non-digit suffix - for example
375: * calling it with "5;" would return 5, not throw NumberFormatException.
376: * It also decides whether the number is hex or decimal, and skips
377: * the leading numeric reference character.
378: * Example: called with "#500" would return 500, called with
379: * "#x80" would return 128.
380: * The string MUST begin with '#'. Also, negative numbers are not allowed.
381: */
382: private static int parseNumericReference(String s) {
383: // See HTML4 spec section 5.3.1
384: int radix = 10;
385: if (s == null) {
386: return 0;
387: }
388:
389: int i = 0, max = s.length();
390: if (max <= 1) {
391: return -1;
392: }
393: assert s.charAt(0) == '#';
394: if (s.charAt(1) == 'x' || s.charAt(1) == 'X') {
395: radix = 16;
396: i = 2;
397: } else {
398: i = 1;
399: }
400:
401: int result = 0;
402: int limit = -Integer.MAX_VALUE;
403: int digit;
404: int multmin = limit / radix;
405: if (i < max) {
406: digit = Character.digit(s.charAt(i++), radix);
407: if (digit < 0) {
408: return -result;
409: } else {
410: result = -digit;
411: }
412: }
413: while (i < max) {
414: // Accumulating negatively avoids surprises near MAX_VALUE
415: digit = Character.digit(s.charAt(i++), radix);
416: if (digit < 0) {
417: return -result;
418: }
419: if (result < multmin) {
420: return -result;
421: }
422: result *= radix;
423: if (result < limit + digit) {
424: return -result;
425: }
426: result -= digit;
427: }
428: done: return -result;
429: }
430:
431: /* Now using Xerces' HTMLdtd.charFromName instead
432:
433: // I generated the following code by creating a DTD for the various
434: // character entities including in XHTML, and then I ran the following
435: //command line on Solaris to generate the below declarations:
436: // cat entities.dtd | /bin/grep ENTITY | /bin/grep -v "ENTITY % " | tr -d '"&#;>' | /bin/nawk '{ printf " v.put(\"%s\", \\u%04x); // NOI18N\n", $2, $3 }'
437: // "wc" on the output tells us that there are 253 entities.
438: // There's a bug in the above script - it was missing escaped ''s around
439: // the unicode chars - so I used an emacs macro to insert those below.
440:
441: // Note that the "apos" entity had to have special treatment below
442: // since javac will substitute unicode chars before resuming, so it
443: // saw the 0027 declaration as the closing apostrophy in the character
444: // literal.
445:
446: // Note also that the above script did NOT handle two entities: "amp"
447: // and "lt" since these specify multiple mappings, and the above script
448: // just smushed the digits together and computed the resulting wrong
449: // number.
450: // <!ENTITY amp "&#38;"> <!-- ampersand, U+0026 ISOnum -->
451: // <!ENTITY lt "&#60;"> <!-- less-than sign, U+003C ISOnum -->
452: // So these were fixed by hand. There are only two such instances in
453: // the DTD.
454:
455: private final static StringIntMap v =
456: new StringIntMap(255); // 253 actually, see above
457: static {
458: v.put("fnof", '\u0192'); // NOI18N
459: v.put("Alpha", '\u0391'); // NOI18N
460: v.put("Beta", '\u0392'); // NOI18N
461: v.put("Gamma", '\u0393'); // NOI18N
462: v.put("Delta", '\u0394'); // NOI18N
463: v.put("Epsilon", '\u0395'); // NOI18N
464: v.put("Zeta", '\u0396'); // NOI18N
465: v.put("Eta", '\u0397'); // NOI18N
466: v.put("Theta", '\u0398'); // NOI18N
467: v.put("Iota", '\u0399'); // NOI18N
468: v.put("Kappa", '\u039a'); // NOI18N
469: v.put("Lambda", '\u039b'); // NOI18N
470: v.put("Mu", '\u039c'); // NOI18N
471: v.put("Nu", '\u039d'); // NOI18N
472: v.put("Xi", '\u039e'); // NOI18N
473: v.put("Omicron", '\u039f'); // NOI18N
474: v.put("Pi", '\u03a0'); // NOI18N
475: v.put("Rho", '\u03a1'); // NOI18N
476: v.put("Sigma", '\u03a3'); // NOI18N
477: v.put("Tau", '\u03a4'); // NOI18N
478: v.put("Upsilon", '\u03a5'); // NOI18N
479: v.put("Phi", '\u03a6'); // NOI18N
480: v.put("Chi", '\u03a7'); // NOI18N
481: v.put("Psi", '\u03a8'); // NOI18N
482: v.put("Omega", '\u03a9'); // NOI18N
483: v.put("alpha", '\u03b1'); // NOI18N
484: v.put("beta", '\u03b2'); // NOI18N
485: v.put("gamma", '\u03b3'); // NOI18N
486: v.put("delta", '\u03b4'); // NOI18N
487: v.put("epsilon", '\u03b5'); // NOI18N
488: v.put("zeta", '\u03b6'); // NOI18N
489: v.put("eta", '\u03b7'); // NOI18N
490: v.put("theta", '\u03b8'); // NOI18N
491: v.put("iota", '\u03b9'); // NOI18N
492: v.put("kappa", '\u03ba'); // NOI18N
493: v.put("lambda", '\u03bb'); // NOI18N
494: v.put("mu", '\u03bc'); // NOI18N
495: v.put("nu", '\u03bd'); // NOI18N
496: v.put("xi", '\u03be'); // NOI18N
497: v.put("omicron", '\u03bf'); // NOI18N
498: v.put("pi", '\u03c0'); // NOI18N
499: v.put("rho", '\u03c1'); // NOI18N
500: v.put("sigmaf", '\u03c2'); // NOI18N
501: v.put("sigma", '\u03c3'); // NOI18N
502: v.put("tau", '\u03c4'); // NOI18N
503: v.put("upsilon", '\u03c5'); // NOI18N
504: v.put("phi", '\u03c6'); // NOI18N
505: v.put("chi", '\u03c7'); // NOI18N
506: v.put("psi", '\u03c8'); // NOI18N
507: v.put("omega", '\u03c9'); // NOI18N
508: v.put("thetasym", '\u03d1'); // NOI18N
509: v.put("upsih", '\u03d2'); // NOI18N
510: v.put("piv", '\u03d6'); // NOI18N
511: v.put("bull", '\u2022'); // NOI18N
512: v.put("hellip", '\u2026'); // NOI18N
513: v.put("prime", '\u2032'); // NOI18N
514: v.put("Prime", '\u2033'); // NOI18N
515: v.put("oline", '\u203e'); // NOI18N
516: v.put("frasl", '\u2044'); // NOI18N
517: v.put("weierp", '\u2118'); // NOI18N
518: v.put("image", '\u2111'); // NOI18N
519: v.put("real", '\u211c'); // NOI18N
520: v.put("trade", '\u2122'); // NOI18N
521: v.put("alefsym", '\u2135'); // NOI18N
522: v.put("larr", '\u2190'); // NOI18N
523: v.put("uarr", '\u2191'); // NOI18N
524: v.put("rarr", '\u2192'); // NOI18N
525: v.put("darr", '\u2193'); // NOI18N
526: v.put("harr", '\u2194'); // NOI18N
527: v.put("crarr", '\u21b5'); // NOI18N
528: v.put("lArr", '\u21d0'); // NOI18N
529: v.put("uArr", '\u21d1'); // NOI18N
530: v.put("rArr", '\u21d2'); // NOI18N
531: v.put("dArr", '\u21d3'); // NOI18N
532: v.put("hArr", '\u21d4'); // NOI18N
533: v.put("forall", '\u2200'); // NOI18N
534: v.put("part", '\u2202'); // NOI18N
535: v.put("exist", '\u2203'); // NOI18N
536: v.put("empty", '\u2205'); // NOI18N
537: v.put("nabla", '\u2207'); // NOI18N
538: v.put("isin", '\u2208'); // NOI18N
539: v.put("notin", '\u2209'); // NOI18N
540: v.put("ni", '\u220b'); // NOI18N
541: v.put("prod", '\u220f'); // NOI18N
542: v.put("sum", '\u2211'); // NOI18N
543: v.put("minus", '\u2212'); // NOI18N
544: v.put("lowast", '\u2217'); // NOI18N
545: v.put("radic", '\u221a'); // NOI18N
546: v.put("prop", '\u221d'); // NOI18N
547: v.put("infin", '\u221e'); // NOI18N
548: v.put("ang", '\u2220'); // NOI18N
549: v.put("and", '\u2227'); // NOI18N
550: v.put("or", '\u2228'); // NOI18N
551: v.put("cap", '\u2229'); // NOI18N
552: v.put("cup", '\u222a'); // NOI18N
553: v.put("int", '\u222b'); // NOI18N
554: v.put("there4", '\u2234'); // NOI18N
555: v.put("sim", '\u223c'); // NOI18N
556: v.put("cong", '\u2245'); // NOI18N
557: v.put("asymp", '\u2248'); // NOI18N
558: v.put("ne", '\u2260'); // NOI18N
559: v.put("equiv", '\u2261'); // NOI18N
560: v.put("le", '\u2264'); // NOI18N
561: v.put("ge", '\u2265'); // NOI18N
562: v.put("sub", '\u2282'); // NOI18N
563: v.put("sup", '\u2283'); // NOI18N
564: v.put("nsub", '\u2284'); // NOI18N
565: v.put("sube", '\u2286'); // NOI18N
566: v.put("supe", '\u2287'); // NOI18N
567: v.put("oplus", '\u2295'); // NOI18N
568: v.put("otimes", '\u2297'); // NOI18N
569: v.put("perp", '\u22a5'); // NOI18N
570: v.put("sdot", '\u22c5'); // NOI18N
571: v.put("lceil", '\u2308'); // NOI18N
572: v.put("rceil", '\u2309'); // NOI18N
573: v.put("lfloor", '\u230a'); // NOI18N
574: v.put("rfloor", '\u230b'); // NOI18N
575: v.put("lang", '\u2329'); // NOI18N
576: v.put("rang", '\u232a'); // NOI18N
577: v.put("loz", '\u25ca'); // NOI18N
578: v.put("spades", '\u2660'); // NOI18N
579: v.put("clubs", '\u2663'); // NOI18N
580: v.put("hearts", '\u2665'); // NOI18N
581: v.put("diams", '\u2666'); // NOI18N
582: v.put("quot", '\u0022'); // NOI18N
583:
584: // These two items were handled specially because
585: // their DTD entries were unusual. Looks like the unicode
586: // chars are \u0026 and \u003c.
587: v.put("amp", '&'); // NOI18N
588: v.put("lt", '<'); // NOI18N
589:
590:
591: v.put("gt", '\u003e'); // NOI18N
592:
593: // This won't compile
594: // v.put("apos", '\u0027'); // NOI18N
595: // so just handle it specially
596: v.put("apos", '\''); // NOI18N
597:
598:
599: v.put("OElig", '\u0152'); // NOI18N
600: v.put("oelig", '\u0153'); // NOI18N
601: v.put("Scaron", '\u0160'); // NOI18N
602: v.put("scaron", '\u0161'); // NOI18N
603: v.put("Yuml", '\u0178'); // NOI18N
604: v.put("circ", '\u02c6'); // NOI18N
605: v.put("tilde", '\u02dc'); // NOI18N
606: v.put("ensp", '\u2002'); // NOI18N
607: v.put("emsp", '\u2003'); // NOI18N
608: v.put("thinsp", '\u2009'); // NOI18N
609: v.put("zwnj", '\u200c'); // NOI18N
610: v.put("zwj", '\u200d'); // NOI18N
611: v.put("lrm", '\u200e'); // NOI18N
612: v.put("rlm", '\u200f'); // NOI18N
613: v.put("ndash", '\u2013'); // NOI18N
614: v.put("mdash", '\u2014'); // NOI18N
615: v.put("lsquo", '\u2018'); // NOI18N
616: v.put("rsquo", '\u2019'); // NOI18N
617: v.put("sbquo", '\u201a'); // NOI18N
618: v.put("ldquo", '\u201c'); // NOI18N
619: v.put("rdquo", '\u201d'); // NOI18N
620: v.put("bdquo", '\u201e'); // NOI18N
621: v.put("dagger", '\u2020'); // NOI18N
622: v.put("Dagger", '\u2021'); // NOI18N
623: v.put("permil", '\u2030'); // NOI18N
624: v.put("lsaquo", '\u2039'); // NOI18N
625: v.put("rsaquo", '\u203a'); // NOI18N
626: v.put("euro", '\u20ac'); // NOI18N
627: v.put("nbsp", '\u00a0'); // NOI18N
628: v.put("iexcl", '\u00a1'); // NOI18N
629: v.put("cent", '\u00a2'); // NOI18N
630: v.put("pound", '\u00a3'); // NOI18N
631: v.put("curren", '\u00a4'); // NOI18N
632: v.put("yen", '\u00a5'); // NOI18N
633: v.put("brvbar", '\u00a6'); // NOI18N
634: v.put("sect", '\u00a7'); // NOI18N
635: v.put("uml", '\u00a8'); // NOI18N
636: v.put("copy", '\u00a9'); // NOI18N
637: v.put("ordf", '\u00aa'); // NOI18N
638: v.put("laquo", '\u00ab'); // NOI18N
639: v.put("not", '\u00ac'); // NOI18N
640: v.put("shy", '\u00ad'); // NOI18N
641: v.put("reg", '\u00ae'); // NOI18N
642: v.put("macr", '\u00af'); // NOI18N
643: v.put("deg", '\u00b0'); // NOI18N
644: v.put("plusmn", '\u00b1'); // NOI18N
645: v.put("sup2", '\u00b2'); // NOI18N
646: v.put("sup3", '\u00b3'); // NOI18N
647: v.put("acute", '\u00b4'); // NOI18N
648: v.put("micro", '\u00b5'); // NOI18N
649: v.put("para", '\u00b6'); // NOI18N
650: v.put("middot", '\u00b7'); // NOI18N
651: v.put("cedil", '\u00b8'); // NOI18N
652: v.put("sup1", '\u00b9'); // NOI18N
653: v.put("ordm", '\u00ba'); // NOI18N
654: v.put("raquo", '\u00bb'); // NOI18N
655: v.put("frac14", '\u00bc'); // NOI18N
656: v.put("frac12", '\u00bd'); // NOI18N
657: v.put("frac34", '\u00be'); // NOI18N
658: v.put("iquest", '\u00bf'); // NOI18N
659: v.put("Agrave", '\u00c0'); // NOI18N
660: v.put("Aacute", '\u00c1'); // NOI18N
661: v.put("Acirc", '\u00c2'); // NOI18N
662: v.put("Atilde", '\u00c3'); // NOI18N
663: v.put("Auml", '\u00c4'); // NOI18N
664: v.put("Aring", '\u00c5'); // NOI18N
665: v.put("AElig", '\u00c6'); // NOI18N
666: v.put("Ccedil", '\u00c7'); // NOI18N
667: v.put("Egrave", '\u00c8'); // NOI18N
668: v.put("Eacute", '\u00c9'); // NOI18N
669: v.put("Ecirc", '\u00ca'); // NOI18N
670: v.put("Euml", '\u00cb'); // NOI18N
671: v.put("Igrave", '\u00cc'); // NOI18N
672: v.put("Iacute", '\u00cd'); // NOI18N
673: v.put("Icirc", '\u00ce'); // NOI18N
674: v.put("Iuml", '\u00cf'); // NOI18N
675: v.put("ETH", '\u00d0'); // NOI18N
676: v.put("Ntilde", '\u00d1'); // NOI18N
677: v.put("Ograve", '\u00d2'); // NOI18N
678: v.put("Oacute", '\u00d3'); // NOI18N
679: v.put("Ocirc", '\u00d4'); // NOI18N
680: v.put("Otilde", '\u00d5'); // NOI18N
681: v.put("Ouml", '\u00d6'); // NOI18N
682: v.put("times", '\u00d7'); // NOI18N
683: v.put("Oslash", '\u00d8'); // NOI18N
684: v.put("Ugrave", '\u00d9'); // NOI18N
685: v.put("Uacute", '\u00da'); // NOI18N
686: v.put("Ucirc", '\u00db'); // NOI18N
687: v.put("Uuml", '\u00dc'); // NOI18N
688: v.put("Yacute", '\u00dd'); // NOI18N
689: v.put("THORN", '\u00de'); // NOI18N
690: v.put("szlig", '\u00df'); // NOI18N
691: v.put("agrave", '\u00e0'); // NOI18N
692: v.put("aacute", '\u00e1'); // NOI18N
693: v.put("acirc", '\u00e2'); // NOI18N
694: v.put("atilde", '\u00e3'); // NOI18N
695: v.put("auml", '\u00e4'); // NOI18N
696: v.put("aring", '\u00e5'); // NOI18N
697: v.put("aelig", '\u00e6'); // NOI18N
698: v.put("ccedil", '\u00e7'); // NOI18N
699: v.put("egrave", '\u00e8'); // NOI18N
700: v.put("eacute", '\u00e9'); // NOI18N
701: v.put("ecirc", '\u00ea'); // NOI18N
702: v.put("euml", '\u00eb'); // NOI18N
703: v.put("igrave", '\u00ec'); // NOI18N
704: v.put("iacute", '\u00ed'); // NOI18N
705: v.put("icirc", '\u00ee'); // NOI18N
706: v.put("iuml", '\u00ef'); // NOI18N
707: v.put("eth", '\u00f0'); // NOI18N
708: v.put("ntilde", '\u00f1'); // NOI18N
709: v.put("ograve", '\u00f2'); // NOI18N
710: v.put("oacute", '\u00f3'); // NOI18N
711: v.put("ocirc", '\u00f4'); // NOI18N
712: v.put("otilde", '\u00f5'); // NOI18N
713: v.put("ouml", '\u00f6'); // NOI18N
714: v.put("divide", '\u00f7'); // NOI18N
715: v.put("oslash", '\u00f8'); // NOI18N
716: v.put("ugrave", '\u00f9'); // NOI18N
717: v.put("uacute", '\u00fa'); // NOI18N
718: v.put("ucirc", '\u00fb'); // NOI18N
719: v.put("uuml", '\u00fc'); // NOI18N
720: v.put("yacute", '\u00fd'); // NOI18N
721: v.put("thorn", '\u00fe'); // NOI18N
722: v.put("yuml", '\u00ff'); // NOI18N
723: }
724: */
725: }
|