Source Code Cross Referenced for TranslatorReader.java in  » Portal » Open-Portal » com » ecyrd » jspwiki » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Portal » Open Portal » com.ecyrd.jspwiki 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /* 
0002:            JSPWiki - a JSP-based WikiWiki clone.
0003:
0004:            Copyright (C) 2001-2005 Janne Jalkanen (Janne.Jalkanen@iki.fi)
0005:
0006:            This program is free software; you can redistribute it and/or modify
0007:            it under the terms of the GNU Lesser General Public License as published by
0008:            the Free Software Foundation; either version 2.1 of the License, or
0009:            (at your option) any later version.
0010:
0011:            This program is distributed in the hope that it will be useful,
0012:            but WITHOUT ANY WARRANTY; without even the implied warranty of
0013:            MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014:            GNU Lesser General Public License for more details.
0015:
0016:            You should have received a copy of the GNU Lesser General Public License
0017:            along with this program; if not, write to the Free Software
0018:            Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
0019:         */
0020:        package com.ecyrd.jspwiki;
0021:
0022:        import java.io.*;
0023:        import java.util.*;
0024:
0025:        import org.apache.log4j.Logger;
0026:        import org.apache.oro.text.*;
0027:        import org.apache.oro.text.regex.*;
0028:
0029:        import com.ecyrd.jspwiki.plugin.PluginManager;
0030:        import com.ecyrd.jspwiki.plugin.PluginException;
0031:        import com.ecyrd.jspwiki.attachment.AttachmentManager;
0032:        import com.ecyrd.jspwiki.attachment.Attachment;
0033:        import com.ecyrd.jspwiki.providers.ProviderException;
0034:        import com.ecyrd.jspwiki.acl.AccessControlList;
0035:        import com.ecyrd.jspwiki.auth.modules.PageAuthorizer;
0036:        import com.ecyrd.jspwiki.auth.WikiSecurityException;
0037:        import com.ecyrd.jspwiki.auth.UserManager;
0038:
0039:        /**
0040:         *  Handles conversion from Wiki format into fully featured HTML.
0041:         *  This is where all the magic happens.  It is CRITICAL that this
0042:         *  class is tested, or all Wikis might die horribly.
0043:         *  <P>
0044:         *  The output of the HTML has not yet been validated against
0045:         *  the HTML DTD.  However, it is very simple.
0046:         *
0047:         *  @author Janne Jalkanen
0048:         */
0049:
0050:        public class TranslatorReader extends Reader {
0051:            public static final int READ = 0;
0052:            public static final int EDIT = 1;
0053:            private static final int EMPTY = 2; // Empty message
0054:            private static final int LOCAL = 3;
0055:            private static final int LOCALREF = 4;
0056:            private static final int IMAGE = 5;
0057:            private static final int EXTERNAL = 6;
0058:            private static final int INTERWIKI = 7;
0059:            private static final int IMAGELINK = 8;
0060:            private static final int IMAGEWIKILINK = 9;
0061:            public static final int ATTACHMENT = 10;
0062:            private static final int ATTACHMENTIMAGE = 11;
0063:
0064:            /** Lists all punctuation characters allowed in WikiMarkup. These
0065:                will not be cleaned away. */
0066:
0067:            private static final String PUNCTUATION_CHARS_ALLOWED = "._:";
0068:
0069:            /** Allow this many characters to be pushed back in the stream.  In effect,
0070:                this limits the size of a single heading line.  */
0071:            private static final int PUSHBACK_BUFFER_SIZE = 10 * 1024;
0072:            private PushbackReader m_in;
0073:
0074:            private StringReader m_data = new StringReader("");
0075:
0076:            private static Logger log = Logger
0077:                    .getLogger(TranslatorReader.class);
0078:
0079:            //private boolean        m_iscode       = false;
0080:            private boolean m_isbold = false;
0081:            private boolean m_isitalic = false;
0082:            private boolean m_isTypedText = false;
0083:            private boolean m_istable = false;
0084:            private boolean m_isPre = false;
0085:            private boolean m_isEscaping = false;
0086:            private boolean m_isdefinition = false;
0087:
0088:            /** Contains style information, in multiple forms. */
0089:            private Stack m_styleStack = new Stack();
0090:
0091:            // general list handling
0092:            private int m_genlistlevel = 0;
0093:            private StringBuffer m_genlistBulletBuffer = new StringBuffer(); // stores the # and * pattern
0094:            private boolean m_allowPHPWikiStyleLists = true;
0095:
0096:            private boolean m_isOpenParagraph = false;
0097:
0098:            /** Tag that gets closed at EOL. */
0099:            private String m_closeTag = null;
0100:
0101:            private WikiEngine m_engine;
0102:            private WikiContext m_context;
0103:
0104:            /** Optionally stores internal wikilinks */
0105:            private ArrayList m_localLinkMutatorChain = new ArrayList();
0106:            private ArrayList m_externalLinkMutatorChain = new ArrayList();
0107:            private ArrayList m_attachmentLinkMutatorChain = new ArrayList();
0108:            private ArrayList m_headingListenerChain = new ArrayList();
0109:
0110:            /** Keeps image regexp Patterns */
0111:            private ArrayList m_inlineImagePatterns;
0112:
0113:            private PatternMatcher m_inlineMatcher = new Perl5Matcher();
0114:
0115:            private ArrayList m_linkMutators = new ArrayList();
0116:
0117:            /**
0118:             *  This property defines the inline image pattern.  It's current value
0119:             *  is jspwiki.translatorReader.inlinePattern
0120:             */
0121:            public static final String PROP_INLINEIMAGEPTRN = "jspwiki.translatorReader.inlinePattern";
0122:
0123:            /** If true, consider CamelCase hyperlinks as well. */
0124:            public static final String PROP_CAMELCASELINKS = "jspwiki.translatorReader.camelCaseLinks";
0125:
0126:            /** If true, all hyperlinks are translated as well, regardless whether they
0127:                are surrounded by brackets. */
0128:            public static final String PROP_PLAINURIS = "jspwiki.translatorReader.plainUris";
0129:
0130:            /** If true, all outward links (external links) have a small link image appended. */
0131:            public static final String PROP_USEOUTLINKIMAGE = "jspwiki.translatorReader.useOutlinkImage";
0132:
0133:            /** If set to "true", allows using raw HTML within Wiki text.  Be warned,
0134:                this is a VERY dangerous option to set - never turn this on in a publicly
0135:                allowable Wiki, unless you are absolutely certain of what you're doing. */
0136:            public static final String PROP_ALLOWHTML = "jspwiki.translatorReader.allowHTML";
0137:
0138:            /** If set to "true", all external links are tagged with 'rel="nofollow"' */
0139:            public static final String PROP_USERELNOFOLLOW = "jspwiki.translatorReader.useRelNofollow";
0140:
0141:            /** If set to "true", enables plugins during parsing */
0142:            public static final String PROP_RUNPLUGINS = "jspwiki.translatorReader.runPlugins";
0143:
0144:            /** If true, then considers CamelCase links as well. */
0145:            private boolean m_camelCaseLinks = false;
0146:
0147:            /** If true, consider URIs that have no brackets as well. */
0148:            // FIXME: Currently reserved, but not used.
0149:            private boolean m_plainUris = false;
0150:
0151:            /** If true, all outward links use a small link image. */
0152:            private boolean m_useOutlinkImage = true;
0153:
0154:            /** If true, allows raw HTML. */
0155:            private boolean m_allowHTML = false;
0156:
0157:            /** If true, executes plugins; otherwise ignores them. */
0158:            private boolean m_enablePlugins = true;
0159:
0160:            private boolean m_useRelNofollow = false;
0161:
0162:            private boolean m_inlineImages = true;
0163:
0164:            private PatternMatcher m_matcher = new Perl5Matcher();
0165:            private PatternCompiler m_compiler = new Perl5Compiler();
0166:            private Pattern m_camelCasePtrn;
0167:
0168:            private TextRenderer m_renderer;
0169:
0170:            /**
0171:             *  The default inlining pattern.  Currently "*.png"
0172:             */
0173:            public static final String DEFAULT_INLINEPATTERN = "*.png";
0174:
0175:            /**
0176:             *  These characters constitute word separators when trying
0177:             *  to find CamelCase links.
0178:             */
0179:            private static final String WORD_SEPARATORS = ",.|;+=&()";
0180:
0181:            protected static final int BOLD = 0;
0182:            protected static final int ITALIC = 1;
0183:            protected static final int TYPED = 2;
0184:
0185:            /**
0186:             *  This list contains all IANA registered URI protocol
0187:             *  types as of September 2004 + a few well-known extra types.
0188:             *
0189:             *  JSPWiki recognises all of them as external links.
0190:             */
0191:            static final String[] c_externalLinks = { "http:", "ftp:",
0192:                    "https:", "mailto:", "news:", "file:", "rtsp:", "mms:",
0193:                    "ldap:", "gopher:", "nntp:", "telnet:", "wais:",
0194:                    "prospero:", "z39.50s", "z39.50r", "vemmi:", "imap:",
0195:                    "nfs:", "acap:", "tip:", "pop:", "dav:",
0196:                    "opaquelocktoken:", "sip:", "sips:", "tel:", "fax:",
0197:                    "modem:", "soap.beep:", "soap.beeps", "xmlrpc.beep",
0198:                    "xmlrpc.beeps", "urn:", "go:", "h323:", "ipp:", "tftp:",
0199:                    "mupdate:", "pres:", "im:", "mtqp", "smb:" };
0200:
0201:            /**
0202:             *  Creates a TranslatorReader using the default HTML renderer.
0203:             */
0204:            public TranslatorReader(WikiContext context, Reader in) {
0205:                initialize(context, in, new HTMLRenderer());
0206:            }
0207:
0208:            public TranslatorReader(WikiContext context, Reader in,
0209:                    TextRenderer renderer) {
0210:                initialize(context, in, renderer);
0211:            }
0212:
0213:            /**
0214:             *  Replaces the current input character stream with a new one.
0215:             *  @param in New source for input.  If null, this method does nothing.
0216:             *  @return the old stream
0217:             */
0218:            public Reader setInputReader(Reader in) {
0219:                Reader old = m_in;
0220:
0221:                if (in != null) {
0222:                    m_in = new PushbackReader(new BufferedReader(in),
0223:                            PUSHBACK_BUFFER_SIZE);
0224:                }
0225:
0226:                return old;
0227:            }
0228:
0229:            /**
0230:             *  @param engine The WikiEngine this reader is attached to.  Is
0231:             * used to figure out of a page exits.
0232:             */
0233:
0234:            // FIXME: TranslatorReaders should be pooled for better performance.
0235:            private void initialize(WikiContext context, Reader in,
0236:                    TextRenderer renderer) {
0237:                PatternCompiler compiler = new GlobCompiler();
0238:                ArrayList compiledpatterns = new ArrayList();
0239:
0240:                m_engine = context.getEngine();
0241:                m_context = context;
0242:
0243:                m_renderer = renderer;
0244:
0245:                setInputReader(in);
0246:
0247:                Collection ptrns = getImagePatterns(m_engine);
0248:
0249:                //
0250:                //  Make them into Regexp Patterns.  Unknown patterns
0251:                //  are ignored.
0252:                //
0253:                for (Iterator i = ptrns.iterator(); i.hasNext();) {
0254:                    try {
0255:                        compiledpatterns.add(compiler
0256:                                .compile((String) i.next()));
0257:                    } catch (MalformedPatternException e) {
0258:                        log.error("Malformed pattern in properties: ", e);
0259:                    }
0260:                }
0261:
0262:                m_inlineImagePatterns = compiledpatterns;
0263:
0264:                try {
0265:                    m_camelCasePtrn = m_compiler
0266:                            .compile("^([[:^alnum:]]*|\\~)([[:upper:]]+[[:lower:]]+[[:upper:]]+[[:alnum:]]*)[[:^alnum:]]*$");
0267:                } catch (MalformedPatternException e) {
0268:                    log.fatal(
0269:                            "Internal error: Someone put in a faulty pattern.",
0270:                            e);
0271:                    throw new InternalWikiException(
0272:                            "Faulty camelcasepattern in TranslatorReader");
0273:                }
0274:
0275:                //
0276:                //  Set the properties.
0277:                //
0278:                Properties props = m_engine.getWikiProperties();
0279:
0280:                String cclinks = (String) m_context.getPage().getAttribute(
0281:                        PROP_CAMELCASELINKS);
0282:
0283:                if (cclinks != null) {
0284:                    m_camelCaseLinks = TextUtil.isPositive(cclinks);
0285:                } else {
0286:                    m_camelCaseLinks = TextUtil.getBooleanProperty(props,
0287:                            PROP_CAMELCASELINKS, m_camelCaseLinks);
0288:                }
0289:
0290:                m_plainUris = TextUtil.getBooleanProperty(props,
0291:                        PROP_PLAINURIS, m_plainUris);
0292:                m_useOutlinkImage = TextUtil.getBooleanProperty(props,
0293:                        PROP_USEOUTLINKIMAGE, m_useOutlinkImage);
0294:                m_allowHTML = TextUtil.getBooleanProperty(props,
0295:                        PROP_ALLOWHTML, m_allowHTML);
0296:
0297:                m_useRelNofollow = TextUtil.getBooleanProperty(props,
0298:                        PROP_USERELNOFOLLOW, m_useRelNofollow);
0299:
0300:                String runplugins = m_engine.getVariable(m_context,
0301:                        PROP_RUNPLUGINS);
0302:                if (runplugins != null)
0303:                    enablePlugins(TextUtil.isPositive(runplugins));
0304:
0305:                if (m_engine.getUserManager() == null
0306:                        || m_engine.getUserManager().getAuthenticator() == null) {
0307:                    disableAccessRules();
0308:                }
0309:
0310:                m_context.getPage().setHasMetadata();
0311:            }
0312:
0313:            /**
0314:             *  Sets the currently used renderer.  This method is protected because
0315:             *  we only want to use it internally for now.  The renderer interface
0316:             *  is not yet set to stone, so it's not expected that third parties
0317:             *  would use this.
0318:             */
0319:            protected void setRenderer(TextRenderer renderer) {
0320:                m_renderer = renderer;
0321:            }
0322:
0323:            /**
0324:             *  Adds a hook for processing link texts.  This hook is called
0325:             *  when the link text is written into the output stream, and
0326:             *  you may use it to modify the text.  It does not affect the
0327:             *  actual link, only the user-visible text.
0328:             *
0329:             *  @param mutator The hook to call.  Null is safe.
0330:             */
0331:            public void addLinkTransmutator(StringTransmutator mutator) {
0332:                if (mutator != null) {
0333:                    m_linkMutators.add(mutator);
0334:                }
0335:            }
0336:
0337:            /**
0338:             *  Adds a hook for processing local links.  The engine
0339:             *  transforms both non-existing and existing page links.
0340:             *
0341:             *  @param mutator The hook to call.  Null is safe.
0342:             */
0343:            public void addLocalLinkHook(StringTransmutator mutator) {
0344:                if (mutator != null) {
0345:                    m_localLinkMutatorChain.add(mutator);
0346:                }
0347:            }
0348:
0349:            /**
0350:             *  Adds a hook for processing external links.  This includes
0351:             *  all http:// ftp://, etc. links, including inlined images.
0352:             *
0353:             *  @param mutator The hook to call.  Null is safe.
0354:             */
0355:            public void addExternalLinkHook(StringTransmutator mutator) {
0356:                if (mutator != null) {
0357:                    m_externalLinkMutatorChain.add(mutator);
0358:                }
0359:            }
0360:
0361:            /**
0362:             *  Adds a hook for processing attachment links.
0363:             *
0364:             *  @param mutator The hook to call.  Null is safe.
0365:             */
0366:            public void addAttachmentLinkHook(StringTransmutator mutator) {
0367:                if (mutator != null) {
0368:                    m_attachmentLinkMutatorChain.add(mutator);
0369:                }
0370:            }
0371:
0372:            public void addHeadingListener(HeadingListener listener) {
0373:                if (listener != null) {
0374:                    m_headingListenerChain.add(listener);
0375:                }
0376:            }
0377:
0378:            private boolean m_parseAccessRules = true;
0379:
0380:            public void disableAccessRules() {
0381:                m_parseAccessRules = false;
0382:            }
0383:
0384:            /**
0385:             *  Can be used to turn on plugin execution on a translator-reader basis
0386:             */
0387:            public void enablePlugins(boolean toggle) {
0388:                m_enablePlugins = toggle;
0389:            }
0390:
0391:            /**
0392:             *  Use this to turn on or off image inlining.
0393:             *  @param toggle If true, images are inlined (as per set in jspwiki.properties)
0394:             *                If false, then images won't be inlined; instead, they will be
0395:             *                treated as standard hyperlinks.
0396:             *  @since 2.2.9
0397:             */
0398:            public void enableImageInlining(boolean toggle) {
0399:                m_inlineImages = toggle;
0400:            }
0401:
0402:            /**
0403:             *  Figure out which image suffixes should be inlined.
0404:             *  @return Collection of Strings with patterns.
0405:             */
0406:
0407:            protected static Collection getImagePatterns(WikiEngine engine) {
0408:                Properties props = engine.getWikiProperties();
0409:                ArrayList ptrnlist = new ArrayList();
0410:
0411:                for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
0412:                    String name = (String) e.nextElement();
0413:
0414:                    if (name.startsWith(PROP_INLINEIMAGEPTRN)) {
0415:                        String ptrn = props.getProperty(name);
0416:
0417:                        ptrnlist.add(ptrn);
0418:                    }
0419:                }
0420:
0421:                if (ptrnlist.size() == 0) {
0422:                    ptrnlist.add(DEFAULT_INLINEPATTERN);
0423:                }
0424:
0425:                return ptrnlist;
0426:            }
0427:
0428:            /**
0429:             *  Returns link name, if it exists; otherwise it returns null.
0430:             */
0431:            private String linkExists(String page) {
0432:                try {
0433:                    if (page == null || page.length() == 0)
0434:                        return null;
0435:
0436:                    return m_engine.getFinalPageName(page);
0437:                } catch (ProviderException e) {
0438:                    log.warn("TranslatorReader got a faulty page name!", e);
0439:
0440:                    return page; // FIXME: What would be the correct way to go back?
0441:                }
0442:            }
0443:
0444:            /**
0445:             *  Calls a transmutator chain.
0446:             *
0447:             *  @param list Chain to call
0448:             *  @param text Text that should be passed to the mutate() method
0449:             *              of each of the mutators in the chain.
0450:             *  @return The result of the mutation.
0451:             */
0452:
0453:            private String callMutatorChain(Collection list, String text) {
0454:                if (list != null && list.size() > 0) {
0455:                    for (Iterator i = list.iterator(); i.hasNext();) {
0456:                        StringTransmutator m = (StringTransmutator) i.next();
0457:
0458:                        text = m.mutate(m_context, text);
0459:                    }
0460:                }
0461:
0462:                // clean up display of relative vs absolute vs global page names
0463:                if (text.indexOf(":") != -1) {
0464:                    String wikiName = WikiEngine.getWikiName(text);
0465:                    if (wikiName.length() == 0) {
0466:                        // global page - display 'Main*'
0467:                        text = text.substring(1) + "*";
0468:                    } else
0469:                        text = WikiEngine.getRelativeToWikiPageName(text);
0470:                }
0471:                return text;
0472:            }
0473:
0474:            private void callHeadingListenerChain(Heading param) {
0475:                List list = m_headingListenerChain;
0476:
0477:                for (Iterator i = list.iterator(); i.hasNext();) {
0478:                    HeadingListener h = (HeadingListener) i.next();
0479:
0480:                    h.headingAdded(m_context, param);
0481:                }
0482:            }
0483:
0484:            /**
0485:             *  Write a HTMLized link depending on its type.
0486:             *  The link mutator chain is processed.
0487:             *
0488:             *  @param type Type of the link.
0489:             *  @param link The actual link.
0490:             *  @param text The user-visible text for the link.
0491:             */
0492:            public String makeLink(int type, String link, String text) {
0493:                if (text == null)
0494:                    text = link;
0495:
0496:                text = callMutatorChain(m_linkMutators, text);
0497:
0498:                return m_renderer.makeLink(type, link, text);
0499:            }
0500:
0501:            /**
0502:             *  Just like makeLink, but also adds the section reference (#sect...)
0503:             */
0504:            private String makeLink(int type, String link, String text,
0505:                    String sectref) {
0506:                if (text == null)
0507:                    text = link;
0508:
0509:                text = callMutatorChain(m_linkMutators, text);
0510:
0511:                return m_renderer.makeLink(type, link, text, sectref);
0512:            }
0513:
0514:            /**
0515:             *  Cleans a Wiki name.
0516:             *  <P>
0517:             *  [ This is a link ] -&gt; ThisIsALink
0518:             *
0519:             *  @param link Link to be cleared. Null is safe, and causes this to return null.
0520:             *  @return A cleaned link.
0521:             *
0522:             *  @since 2.0
0523:             */
0524:            public static String cleanLink(String link) {
0525:                if (link == null)
0526:                    return null;
0527:                String wiki = cleanLinkPart(WikiEngine.getWikiName(link), false);
0528:                link = WikiEngine.getRelativePageName(link);
0529:                if (link != null) {
0530:                    String linkbits[] = link.split("/", 2);
0531:                    link = cleanLinkPart(linkbits[0], true);
0532:                    if (linkbits.length == 2)
0533:                        link = link + "/" + linkbits[1];
0534:                }
0535:                if (wiki != null)
0536:                    link = wiki + ":" + link;
0537:                return link;
0538:            }
0539:
0540:            // if morph is false, then we only contract spaces and dashes, no character manipulation
0541:            public static String cleanLinkPart(String link, boolean morph) {
0542:                if (link == null)
0543:                    return null;
0544:
0545:                StringBuffer clean = new StringBuffer();
0546:
0547:                //
0548:                //  Compress away all whitespace and capitalize
0549:                //  all words in between.
0550:                //
0551:
0552:                StringTokenizer st = new StringTokenizer(link, " -");
0553:
0554:                while (st.hasMoreTokens()) {
0555:                    StringBuffer component = new StringBuffer(st.nextToken());
0556:
0557:                    if (morph)
0558:                        component.setCharAt(0, Character.toUpperCase(component
0559:                                .charAt(0)));
0560:
0561:                    //
0562:                    //  We must do this, because otherwise compiling on JDK 1.4 causes
0563:                    //  a downwards incompatibility to JDK 1.3.
0564:                    //
0565:                    clean.append(component.toString());
0566:                }
0567:
0568:                //
0569:                //  Remove non-alphanumeric characters that should not
0570:                //  be put inside WikiNames.  Note that all valid
0571:                //  Unicode letters are considered okay for WikiNames.
0572:                //  It is the problem of the WikiPageProvider to take
0573:                //  care of actually storing that information.
0574:                //
0575:
0576:                for (int i = 0; i < clean.length(); i++) {
0577:                    char ch = clean.charAt(i);
0578:
0579:                    if (!(Character.isLetterOrDigit(ch) || PUNCTUATION_CHARS_ALLOWED
0580:                            .indexOf(ch) != -1)) {
0581:                        clean.deleteCharAt(i);
0582:                        --i; // We just shortened this buffer.
0583:                    }
0584:                }
0585:
0586:                return clean.toString();
0587:            }
0588:
0589:            /**
0590:             *  Figures out if a link is an off-site link.  This recognizes
0591:             *  the most common protocols by checking how it starts.
0592:             */
0593:
0594:            // FIXME: Should really put the external link types to a sorted set,
0595:            //        then searching for them would be faster.
0596:            private boolean isExternalLink(String link) {
0597:                for (int i = 0; i < c_externalLinks.length; i++) {
0598:                    if (link.startsWith(c_externalLinks[i]))
0599:                        return true;
0600:                }
0601:
0602:                return false;
0603:            }
0604:
0605:            /**
0606:             *  Returns true, if the link in question is an access
0607:             *  rule.
0608:             */
0609:            private static boolean isAccessRule(String link) {
0610:                return link.startsWith("{ALLOW") || link.startsWith("{DENY");
0611:            }
0612:
0613:            /**
0614:             *  Matches the given link to the list of image name patterns
0615:             *  to determine whether it should be treated as an inline image
0616:             *  or not.
0617:             */
0618:            private boolean isImageLink(String link) {
0619:                if (m_inlineImages) {
0620:                    for (Iterator i = m_inlineImagePatterns.iterator(); i
0621:                            .hasNext();) {
0622:                        if (m_inlineMatcher.matches(link, (Pattern) i.next()))
0623:                            return true;
0624:                    }
0625:                }
0626:
0627:                return false;
0628:            }
0629:
0630:            private static boolean isMetadata(String link) {
0631:                return link.startsWith("{SET");
0632:            }
0633:
0634:            /**
0635:             *  Returns true, if the argument contains a number, otherwise false.
0636:             *  In a quick test this is roughly the same speed as Integer.parseInt()
0637:             *  if the argument is a number, and roughly ten times the speed, if
0638:             *  the argument is NOT a number.
0639:             */
0640:
0641:            private boolean isNumber(String s) {
0642:                if (s == null)
0643:                    return false;
0644:
0645:                if (s.length() > 1 && s.charAt(0) == '-')
0646:                    s = s.substring(1);
0647:
0648:                for (int i = 0; i < s.length(); i++) {
0649:                    if (!Character.isDigit(s.charAt(i)))
0650:                        return false;
0651:                }
0652:
0653:                return true;
0654:            }
0655:
0656:            /**
0657:             *  Checks for the existence of a traditional style CamelCase link.
0658:             *  <P>
0659:             *  We separate all white-space -separated words, and feed it to this
0660:             *  routine to find if there are any possible camelcase links.
0661:             *  For example, if "word" is "__HyperLink__" we return "HyperLink".
0662:             *
0663:             *  @param word A phrase to search in.
0664:             *  @return The match within the phrase.  Returns null, if no CamelCase
0665:             *          hyperlink exists within this phrase.
0666:             */
0667:            private String checkForCamelCaseLink(String word) {
0668:                PatternMatcherInput input;
0669:
0670:                input = new PatternMatcherInput(word);
0671:
0672:                if (m_matcher.contains(input, m_camelCasePtrn)) {
0673:                    MatchResult res = m_matcher.getMatch();
0674:
0675:                    int start = res.beginOffset(2);
0676:                    int end = res.endOffset(2);
0677:
0678:                    String link = res.group(2);
0679:
0680:                    if (res.group(1) != null) {
0681:                        if (res.group(1).equals("~")
0682:                                || res.group(1).indexOf('[') != -1) {
0683:                            // Delete the (~) from beginning.
0684:                            // We'll make '~' the generic kill-processing-character from
0685:                            // now on.
0686:                            return null;
0687:                        }
0688:                    }
0689:
0690:                    return link;
0691:                } // if match
0692:
0693:                return null;
0694:            }
0695:
0696:            /**
0697:             *  When given a link to a WikiName, we just return
0698:             *  a proper HTML link for it.  The local link mutator
0699:             *  chain is also called.
0700:             */
0701:            private String makeCamelCaseLink(String wikiname) {
0702:                String matchedLink;
0703:                String link;
0704:
0705:                callMutatorChain(m_localLinkMutatorChain, wikiname);
0706:
0707:                if ((matchedLink = linkExists(wikiname)) != null) {
0708:                    link = makeLink(READ, matchedLink, wikiname);
0709:                } else {
0710:                    link = makeLink(EDIT, wikiname, wikiname);
0711:                }
0712:
0713:                return link;
0714:            }
0715:
0716:            private String makeDirectURILink(String url) {
0717:                String last = "";
0718:                String result;
0719:
0720:                if (url.endsWith(",") || url.endsWith(".")) {
0721:                    last = url.substring(url.length() - 1);
0722:                    url = url.substring(0, url.length() - 1);
0723:                }
0724:
0725:                callMutatorChain(m_externalLinkMutatorChain, url);
0726:
0727:                if (isImageLink(url)) {
0728:                    result = handleImageLink(url, url, false);
0729:                } else {
0730:                    result = makeLink(EXTERNAL, url, url)
0731:                            + m_renderer.outlinkImage();
0732:                }
0733:
0734:                result += last;
0735:
0736:                return result;
0737:            }
0738:
0739:            /**
0740:             *  Image links are handled differently:
0741:             *  1. If the text is a WikiName of an existing page,
0742:             *     it gets linked.
0743:             *  2. If the text is an external link, then it is inlined.  
0744:             *  3. Otherwise it becomes an ALT text.
0745:             *
0746:             *  @param reallink The link to the image.
0747:             *  @param link     Link text portion, may be a link to somewhere else.
0748:             *  @param hasLinkText If true, then the defined link had a link text available.
0749:             *                  This means that the link text may be a link to a wiki page,
0750:             *                  or an external resource.
0751:             */
0752:
0753:            private String handleImageLink(String reallink, String link,
0754:                    boolean hasLinkText) {
0755:                String possiblePage = cleanLink(link);
0756:                String matchedLink;
0757:                String res = "";
0758:
0759:                if (isExternalLink(link) && hasLinkText) {
0760:                    res = makeLink(IMAGELINK, reallink, link);
0761:                } else if ((matchedLink = linkExists(possiblePage)) != null
0762:                        && hasLinkText) {
0763:                    // System.out.println("Orig="+link+", Matched: "+matchedLink);
0764:                    callMutatorChain(m_localLinkMutatorChain, possiblePage);
0765:
0766:                    res = makeLink(IMAGEWIKILINK, reallink, link);
0767:                } else {
0768:                    res = makeLink(IMAGE, reallink, link);
0769:                }
0770:
0771:                return res;
0772:            }
0773:
0774:            private String handleAccessRule(String ruleLine) {
0775:                if (!m_parseAccessRules)
0776:                    return "";
0777:                AccessControlList acl;
0778:                WikiPage page = m_context.getPage();
0779:                UserManager mgr = m_context.getEngine().getUserManager();
0780:
0781:                if (ruleLine.startsWith("{"))
0782:                    ruleLine = ruleLine.substring(1);
0783:                if (ruleLine.endsWith("}"))
0784:                    ruleLine = ruleLine.substring(0, ruleLine.length() - 1);
0785:
0786:                log.debug("page=" + page.getName() + ", ACL = " + ruleLine);
0787:
0788:                try {
0789:                    acl = PageAuthorizer.parseAcl(page, mgr, ruleLine);
0790:
0791:                    page.setAcl(acl);
0792:
0793:                    log.debug(acl.toString());
0794:                } catch (WikiSecurityException wse) {
0795:                    return m_renderer.makeError(wse.getMessage());
0796:                }
0797:
0798:                return "";
0799:            }
0800:
0801:            /**
0802:             *  Handles metadata setting [{SET foo=bar}]
0803:             */
0804:            private String handleMetadata(String link) {
0805:                try {
0806:                    String args = link.substring(link.indexOf(' '), link
0807:                            .length() - 1);
0808:
0809:                    String name = args.substring(0, args.indexOf('='));
0810:                    String val = args.substring(args.indexOf('=') + 1, args
0811:                            .length());
0812:
0813:                    name = name.trim();
0814:                    val = val.trim();
0815:
0816:                    if (val.startsWith("'"))
0817:                        val = val.substring(1);
0818:                    if (val.endsWith("'"))
0819:                        val = val.substring(0, val.length() - 1);
0820:
0821:                    // log.debug("SET name='"+name+"', value='"+val+"'.");
0822:
0823:                    if (name.length() > 0 && val.length() > 0) {
0824:                        val = m_engine.getVariableManager().expandVariables(
0825:                                m_context, val);
0826:
0827:                        m_context.getPage().setAttribute(name, val);
0828:                    }
0829:                } catch (Exception e) {
0830:                    m_renderer.makeError(" Invalid SET found: " + link);
0831:                }
0832:
0833:                return "";
0834:            }
0835:
0836:            /**
0837:             *  Gobbles up all hyperlinks that are encased in square brackets.
0838:             */
0839:            private String handleHyperlinks(String link) {
0840:                StringBuffer sb = new StringBuffer();
0841:                String reallink;
0842:                int cutpoint;
0843:
0844:                if (isAccessRule(link)) {
0845:                    return handleAccessRule(link);
0846:                }
0847:
0848:                if (isMetadata(link)) {
0849:                    return handleMetadata(link);
0850:                }
0851:
0852:                if (PluginManager.isPluginLink(link)) {
0853:                    String included = "";
0854:                    try {
0855:                        if (m_enablePlugins) {
0856:                            included = m_engine.getPluginManager().execute(
0857:                                    m_context, link);
0858:                        }
0859:                    } catch (PluginException e) {
0860:                        log.info("Failed to insert plugin", e);
0861:                        log.info("Root cause:", e.getRootThrowable());
0862:                        included = m_renderer
0863:                                .makeError("Plugin insertion failed: "
0864:                                        + e.getMessage());
0865:                    }
0866:
0867:                    sb.append(included);
0868:
0869:                    return sb.toString();
0870:                }
0871:
0872:                link = TextUtil.replaceEntities(link);
0873:
0874:                if ((cutpoint = link.indexOf('|')) != -1) {
0875:                    reallink = link.substring(cutpoint + 1).trim();
0876:                    link = link.substring(0, cutpoint);
0877:                } else {
0878:                    reallink = link.trim();
0879:                }
0880:
0881:                int interwikipoint = -1;
0882:
0883:                //
0884:                //  Yes, we now have the components separated.
0885:                //  link     = the text the link should have
0886:                //  reallink = the url or page name.
0887:                //
0888:                //  In many cases these are the same.  [link|reallink].
0889:                //  
0890:                if (VariableManager.isVariableLink(link)) {
0891:                    String value;
0892:
0893:                    try {
0894:                        value = m_engine.getVariableManager().parseAndGetValue(
0895:                                m_context, link);
0896:                    } catch (NoSuchVariableException e) {
0897:                        value = m_renderer.makeError(e.getMessage());
0898:                    } catch (IllegalArgumentException e) {
0899:                        value = m_renderer.makeError(e.getMessage());
0900:                    }
0901:
0902:                    sb.append(value);
0903:                } else if (isExternalLink(reallink)) {
0904:                    // It's an external link, out of this Wiki
0905:
0906:                    callMutatorChain(m_externalLinkMutatorChain, reallink);
0907:
0908:                    if (isImageLink(reallink)) {
0909:                        sb.append(handleImageLink(reallink, link,
0910:                                (cutpoint != -1)));
0911:                    } else {
0912:                        sb.append(makeLink(EXTERNAL, reallink, link));
0913:                        sb.append(m_renderer.outlinkImage());
0914:                    }
0915:                } else if ((interwikipoint = reallink.indexOf("::")) != -1) {
0916:                    // It's an interwiki link
0917:                    // InterWiki links also get added to external link chain
0918:                    // after the links have been resolved.
0919:
0920:                    // FIXME: There is an interesting issue here:  We probably should
0921:                    //        URLEncode the wikiPage, but we can't since some of the
0922:                    //        Wikis use slashes (/), which won't survive URLEncoding.
0923:                    //        Besides, we don't know which character set the other Wiki
0924:                    //        is using, so you'll have to write the entire name as it appears
0925:                    //        in the URL.  Bugger.
0926:
0927:                    String extWiki = reallink.substring(0, interwikipoint);
0928:                    String wikiPage = reallink.substring(interwikipoint + 2);
0929:
0930:                    String urlReference;
0931:                    if (extWiki.equals("Edit")) {
0932:                        urlReference = m_context.getURL(WikiContext.EDIT,
0933:                                wikiPage);
0934:                    } else
0935:                        urlReference = m_engine.getInterWikiURL(extWiki);
0936:
0937:                    if (urlReference != null) {
0938:                        urlReference = TextUtil.replaceString(urlReference,
0939:                                "%s", wikiPage);
0940:                        callMutatorChain(m_externalLinkMutatorChain,
0941:                                urlReference);
0942:
0943:                        sb.append(makeLink(INTERWIKI, urlReference, link));
0944:
0945:                        if (isExternalLink(urlReference)) {
0946:                            sb.append(m_renderer.outlinkImage());
0947:                        }
0948:                    } else {
0949:                        sb
0950:                                .append(link
0951:                                        + " "
0952:                                        + m_renderer
0953:                                                .makeError("No InterWiki reference defined in properties for Wiki called '"
0954:                                                        + extWiki + "'!)"));
0955:                    }
0956:                } else if (reallink.startsWith("#")) {
0957:                    // It defines a local footnote
0958:                    sb.append(makeLink(LOCAL, reallink, link));
0959:                } else if (isNumber(reallink)) {
0960:                    // It defines a reference to a local footnote
0961:                    sb.append(makeLink(LOCALREF, reallink, link));
0962:                } else {
0963:                    int hashMark = -1;
0964:
0965:                    //
0966:                    //  Internal wiki link, but is it an attachment link?
0967:                    //
0968:                    String rellink = reallink;
0969:                    String attachment = findAttachment(rellink); // attachments can have relative names
0970:                    reallink = WikiEngine.makeAbsolutePageName(reallink);
0971:                    if (attachment != null) {
0972:                        callMutatorChain(m_attachmentLinkMutatorChain,
0973:                                attachment);
0974:
0975:                        if (isImageLink(reallink)) {
0976:                            attachment = m_context.getURL(WikiContext.ATTACH,
0977:                                    attachment);
0978:                            sb.append(handleImageLink(attachment, link,
0979:                                    (cutpoint != -1)));
0980:                        } else {
0981:                            sb.append(makeLink(ATTACHMENT, attachment, link));
0982:                        }
0983:                    } else if ((hashMark = reallink.indexOf('#')) != -1) {
0984:                        // It's an internal Wiki link, but to a named section
0985:
0986:                        String namedSection = reallink.substring(hashMark + 1);
0987:                        reallink = reallink.substring(0, hashMark);
0988:
0989:                        reallink = cleanLink(reallink);
0990:
0991:                        callMutatorChain(m_localLinkMutatorChain, reallink);
0992:
0993:                        String matchedLink;
0994:                        if ((matchedLink = linkExists(reallink)) != null) {
0995:                            String sectref = "section-"
0996:                                    + m_engine.encodeName(matchedLink) + "-"
0997:                                    + namedSection;
0998:                            sectref = sectref.replace('%', '_');
0999:                            sb
1000:                                    .append(makeLink(READ, matchedLink, link,
1001:                                            sectref));
1002:                        } else {
1003:                            sb.append(makeLink(EDIT, reallink, link));
1004:                        }
1005:                    } else {
1006:                        // It's an internal Wiki link
1007:                        int debug;
1008:                        if (reallink.indexOf(":") != -1)
1009:                            debug = 77;
1010:                        reallink = cleanLink(reallink);
1011:
1012:                        callMutatorChain(m_localLinkMutatorChain, reallink);
1013:
1014:                        String matchedLink;
1015:                        if (reallink != null && reallink.endsWith(":"))
1016:                            matchedLink = reallink; // link to another wiki's default page - use READ mode
1017:                        else
1018:                            matchedLink = linkExists(reallink);
1019:
1020:                        if (matchedLink != null) {
1021:                            sb.append(makeLink(READ, matchedLink, link));
1022:                        } else {
1023:                            sb.append(makeLink(EDIT, reallink, link));
1024:                        }
1025:                    }
1026:                }
1027:
1028:                return sb.toString();
1029:            }
1030:
1031:            private String findAttachment(String link) {
1032:                AttachmentManager mgr = m_engine.getAttachmentManager();
1033:                WikiPage currentPage = m_context.getPage();
1034:                Attachment att = null;
1035:
1036:                /*
1037:                System.out.println("Finding attachment of page "+currentPage.getName());
1038:                System.out.println("With name "+link);
1039:                 */
1040:
1041:                try {
1042:                    att = mgr.getAttachmentInfo(m_context, link);
1043:                } catch (ProviderException e) {
1044:                    log.warn("Finding attachments failed: ", e);
1045:                    return null;
1046:                }
1047:
1048:                if (att != null) {
1049:                    return att.getName();
1050:                } else if (link.indexOf('/') != -1) {
1051:                    return link;
1052:                }
1053:
1054:                return null;
1055:            }
1056:
1057:            /**
1058:             *  Closes all annoying lists and things that the user might've
1059:             *  left open.
1060:             */
1061:            private String closeAll() {
1062:                StringBuffer buf = new StringBuffer();
1063:
1064:                if (m_isbold) {
1065:                    buf.append(m_renderer.closeTextEffect(BOLD));
1066:                    m_isbold = false;
1067:                }
1068:
1069:                if (m_isitalic) {
1070:                    buf.append(m_renderer.closeTextEffect(ITALIC));
1071:                    m_isitalic = false;
1072:                }
1073:
1074:                if (m_isTypedText) {
1075:                    buf.append(m_renderer.closeTextEffect(TYPED));
1076:                    m_isTypedText = false;
1077:                }
1078:
1079:                /*
1080:                for( ; m_listlevel > 0; m_listlevel-- )
1081:                {
1082:                    buf.append( "</ul>\n" );
1083:                }
1084:
1085:                for( ; m_numlistlevel > 0; m_numlistlevel-- )
1086:                {
1087:                    buf.append( "</ol>\n" );
1088:                }
1089:                 */
1090:                // cleanup OL and UL lists
1091:                buf.append(unwindGeneralList());
1092:
1093:                if (m_isPre) {
1094:                    buf.append(m_renderer.closePreformatted());
1095:                    m_isEscaping = false;
1096:                    m_isPre = false;
1097:                }
1098:
1099:                if (m_istable) {
1100:                    buf.append(m_renderer.closeTable());
1101:                    m_istable = false;
1102:                }
1103:
1104:                if (m_isOpenParagraph) {
1105:                    buf.append(m_renderer.closeParagraph());
1106:                    m_isOpenParagraph = false;
1107:                }
1108:
1109:                return buf.toString();
1110:            }
1111:
1112:            // FIXME: should remove countChar() as it is unused
1113:            /**
1114:             *  Counts how many consecutive characters of a certain type exists on the line.
1115:             *  @param line String of chars to check.
1116:             *  @param startPos Position to start reading from.
1117:             *  @param char Character to check for.
1118:             */
1119:            private int countChar(String line, int startPos, char c) {
1120:                int count;
1121:
1122:                for (count = 0; (startPos + count < line.length())
1123:                        && (line.charAt(count + startPos) == c); count++)
1124:                    ;
1125:
1126:                return count;
1127:            }
1128:
1129:            /**
1130:             *  Returns a new String that has char c n times.
1131:             */
1132:            private String repeatChar(char c, int n) {
1133:                StringBuffer sb = new StringBuffer();
1134:                for (int i = 0; i < n; i++)
1135:                    sb.append(c);
1136:
1137:                return sb.toString();
1138:            }
1139:
1140:            private int nextToken() throws IOException {
1141:                if (m_in == null)
1142:                    return -1;
1143:                return m_in.read();
1144:            }
1145:
1146:            /**
1147:             *  Push back any character to the current input.  Does not
1148:             *  push back a read EOF, though.
1149:             */
1150:            private void pushBack(int c) throws IOException {
1151:                if (c != -1 && m_in != null) {
1152:                    m_in.unread(c);
1153:                }
1154:            }
1155:
1156:            /**
1157:             *  Pushes back any string that has been read.  It will obviously
1158:             *  be pushed back in a reverse order.
1159:             *
1160:             *  @since 2.1.77
1161:             */
1162:            private void pushBack(String s) throws IOException {
1163:                for (int i = s.length() - 1; i >= 0; i--) {
1164:                    pushBack(s.charAt(i));
1165:                }
1166:            }
1167:
1168:            private String handleBackslash() throws IOException {
1169:                int ch = nextToken();
1170:
1171:                if (ch == '\\') {
1172:                    int ch2 = nextToken();
1173:
1174:                    if (ch2 == '\\') {
1175:                        return m_renderer.lineBreak(true);
1176:                    }
1177:
1178:                    pushBack(ch2);
1179:
1180:                    return m_renderer.lineBreak(false);
1181:                }
1182:
1183:                pushBack(ch);
1184:
1185:                return "\\";
1186:            }
1187:
1188:            private String handleUnderscore() throws IOException {
1189:                int ch = nextToken();
1190:                String res = "_";
1191:
1192:                if (ch == '_') {
1193:                    res = m_isbold ? m_renderer.closeTextEffect(BOLD)
1194:                            : m_renderer.openTextEffect(BOLD);
1195:                    m_isbold = !m_isbold;
1196:                } else {
1197:                    pushBack(ch);
1198:                }
1199:
1200:                return res;
1201:            }
1202:
1203:            /**
1204:             *  For example: italics.
1205:             */
1206:            private String handleApostrophe() throws IOException {
1207:                int ch = nextToken();
1208:                String res = "'";
1209:
1210:                if (ch == '\'') {
1211:                    res = m_isitalic ? m_renderer.closeTextEffect(ITALIC)
1212:                            : m_renderer.openTextEffect(ITALIC);
1213:                    m_isitalic = !m_isitalic;
1214:                } else {
1215:                    pushBack(ch);
1216:                }
1217:
1218:                return res;
1219:            }
1220:
1221:            private String handleOpenbrace(boolean isBlock) throws IOException {
1222:                int ch = nextToken();
1223:                String res = "{";
1224:
1225:                if (ch == '{') {
1226:                    int ch2 = nextToken();
1227:
1228:                    if (ch2 == '{') {
1229:                        res = startBlockLevel()
1230:                                + m_renderer.openPreformatted(isBlock);
1231:                        m_isPre = true;
1232:                        m_isEscaping = true;
1233:                    } else {
1234:                        pushBack(ch2);
1235:
1236:                        res = m_renderer.openTextEffect(TYPED);
1237:                        m_isTypedText = true;
1238:                    }
1239:                } else {
1240:                    pushBack(ch);
1241:                }
1242:
1243:                return res;
1244:            }
1245:
1246:            /**
1247:             *  Handles both }} and }}}
1248:             */
1249:            private String handleClosebrace() throws IOException {
1250:                String res = "}";
1251:
1252:                int ch2 = nextToken();
1253:
1254:                if (ch2 == '}') {
1255:                    int ch3 = nextToken();
1256:
1257:                    if (ch3 == '}') {
1258:                        if (m_isPre) {
1259:                            m_isPre = false;
1260:                            m_isEscaping = false;
1261:                            res = m_renderer.closePreformatted();
1262:                        } else {
1263:                            res = "}}}";
1264:                        }
1265:                    } else {
1266:                        pushBack(ch3);
1267:
1268:                        if (!m_isEscaping) {
1269:                            res = m_renderer.closeTextEffect(TYPED);
1270:                            m_isTypedText = false;
1271:                        } else {
1272:                            pushBack(ch2);
1273:                        }
1274:                    }
1275:                } else {
1276:                    pushBack(ch2);
1277:                }
1278:
1279:                return res;
1280:            }
1281:
1282:            private String handleDash() throws IOException {
1283:                int ch = nextToken();
1284:
1285:                if (ch == '-') {
1286:                    int ch2 = nextToken();
1287:
1288:                    if (ch2 == '-') {
1289:                        int ch3 = nextToken();
1290:
1291:                        if (ch3 == '-') {
1292:                            // Empty away all the rest of the dashes.
1293:                            // Do not forget to return the first non-match back.
1294:                            while ((ch = nextToken()) == '-')
1295:                                ;
1296:
1297:                            pushBack(ch);
1298:                            return startBlockLevel() + m_renderer.makeRuler();
1299:                        }
1300:
1301:                        pushBack(ch3);
1302:                    }
1303:                    pushBack(ch2);
1304:                }
1305:
1306:                pushBack(ch);
1307:
1308:                return "-";
1309:            }
1310:
1311:            /**
1312:             *  This method peeks ahead in the stream until EOL and returns the result.
1313:             *  It will keep the buffers untouched.
1314:             *
1315:             *  @return The string from the current position to the end of line.
1316:             */
1317:
1318:            // FIXME: Always returns an empty line, even if the stream is full.
1319:            private String peekAheadLine() throws IOException {
1320:                String s = readUntilEOL().toString();
1321:                pushBack(s);
1322:
1323:                return s;
1324:            }
1325:
1326:            private String handleHeading() throws IOException {
1327:                StringBuffer buf = new StringBuffer();
1328:
1329:                int ch = nextToken();
1330:
1331:                Heading hd = new Heading();
1332:
1333:                if (ch == '!') {
1334:                    int ch2 = nextToken();
1335:
1336:                    if (ch2 == '!') {
1337:                        String title = peekAheadLine();
1338:
1339:                        buf.append(m_renderer.makeHeading(
1340:                                Heading.HEADING_LARGE, title, hd));
1341:                    } else {
1342:                        pushBack(ch2);
1343:                        String title = peekAheadLine();
1344:                        buf.append(m_renderer.makeHeading(
1345:                                Heading.HEADING_MEDIUM, title, hd));
1346:                    }
1347:                } else {
1348:                    pushBack(ch);
1349:                    String title = peekAheadLine();
1350:                    buf.append(m_renderer.makeHeading(Heading.HEADING_SMALL,
1351:                            title, hd));
1352:                }
1353:
1354:                callHeadingListenerChain(hd);
1355:
1356:                return buf.toString();
1357:            }
1358:
1359:            /**
1360:             *  Reads the stream until the next EOL or EOF.  Note that it will also read the
1361:             *  EOL from the stream.
1362:             */
1363:            private StringBuffer readUntilEOL() throws IOException {
1364:                int ch;
1365:                StringBuffer buf = new StringBuffer();
1366:
1367:                while (true) {
1368:                    ch = nextToken();
1369:
1370:                    if (ch == -1)
1371:                        break;
1372:
1373:                    buf.append((char) ch);
1374:
1375:                    if (ch == '\n')
1376:                        break;
1377:                }
1378:
1379:                return buf;
1380:            }
1381:
1382:            /**
1383:             *  Starts a block level element, therefore closing the
1384:             *  a potential open paragraph tag.
1385:             */
1386:            private String startBlockLevel() {
1387:                if (m_isOpenParagraph) {
1388:                    m_isOpenParagraph = false;
1389:                    return m_renderer.closeParagraph();
1390:                }
1391:
1392:                return "";
1393:            }
1394:
1395:            /**
1396:             *  Like original handleOrderedList() and handleUnorderedList()
1397:             *  however handles both ordered ('#') and unordered ('*') mixed together.
1398:             */
1399:
1400:            // FIXME: Refactor this; it's a bit messy.
1401:            private String handleGeneralList() throws IOException {
1402:                String cStrShortName = "handleGeneralList()"; //put log messages in some context
1403:
1404:                StringBuffer buf = new StringBuffer();
1405:
1406:                buf.append(startBlockLevel());
1407:
1408:                String strBullets = readWhile("*#");
1409:                String strBulletsRaw = strBullets; // to know what was original before phpwiki style substitution
1410:                int numBullets = strBullets.length();
1411:
1412:                // override the beginning portion of bullet pattern to be like the previous
1413:                // to simulate PHPWiki style lists
1414:
1415:                if (m_allowPHPWikiStyleLists) {
1416:                    // only substitute if different
1417:                    if (!(strBullets.substring(0, Math.min(numBullets,
1418:                            m_genlistlevel))
1419:                            .equals(m_genlistBulletBuffer.substring(0, Math
1420:                                    .min(numBullets, m_genlistlevel))))) {
1421:                        if (numBullets <= m_genlistlevel) {
1422:                            // Substitute all but the last character (keep the expressed bullet preference)
1423:                            strBullets = (numBullets > 1 ? m_genlistBulletBuffer
1424:                                    .substring(0, numBullets - 1)
1425:                                    : "")
1426:                                    + strBullets.substring(numBullets - 1,
1427:                                            numBullets);
1428:                        } else {
1429:                            strBullets = m_genlistBulletBuffer
1430:                                    + strBullets.substring(m_genlistlevel,
1431:                                            numBullets);
1432:                        }
1433:                    }
1434:                }
1435:
1436:                //
1437:                //  Check if this is still of the same type
1438:                //
1439:                if (strBullets.substring(0,
1440:                        Math.min(numBullets, m_genlistlevel)).equals(
1441:                        m_genlistBulletBuffer.substring(0, Math.min(numBullets,
1442:                                m_genlistlevel)))) {
1443:                    int chBullet;
1444:
1445:                    if (numBullets > m_genlistlevel) {
1446:                        buf.append(m_renderer.openList(strBullets
1447:                                .charAt(m_genlistlevel++)));
1448:
1449:                        for (; m_genlistlevel < numBullets; m_genlistlevel++) {
1450:                            // bullets are growing, get from new bullet list
1451:                            buf.append(m_renderer.openListItem());
1452:                            buf.append(m_renderer.openList(strBullets
1453:                                    .charAt(m_genlistlevel)));
1454:                        }
1455:                    } else if (numBullets < m_genlistlevel) {
1456:                        //  Close the previous list item.
1457:                        buf.append(m_renderer.closeListItem());
1458:
1459:                        for (; m_genlistlevel > numBullets; m_genlistlevel--) {
1460:                            // bullets are shrinking, get from old bullet list
1461:                            buf.append(m_renderer
1462:                                    .closeList(m_genlistBulletBuffer
1463:                                            .charAt(m_genlistlevel - 1)));
1464:                            if (m_genlistlevel > 0)
1465:                                buf.append(m_renderer.closeListItem());
1466:
1467:                        }
1468:                    } else {
1469:                        if (m_genlistlevel > 0)
1470:                            buf.append(m_renderer.closeListItem());
1471:                    }
1472:                } else {
1473:                    //
1474:                    //  The pattern has changed, unwind and restart
1475:                    //
1476:                    char chBullet;
1477:                    int numEqualBullets;
1478:                    int numCheckBullets;
1479:
1480:                    // find out how much is the same
1481:                    numEqualBullets = 0;
1482:                    numCheckBullets = Math.min(numBullets, m_genlistlevel);
1483:
1484:                    while (numEqualBullets < numCheckBullets) {
1485:                        // if the bullets are equal so far, keep going
1486:                        if (strBullets.charAt(numEqualBullets) == m_genlistBulletBuffer
1487:                                .charAt(numEqualBullets))
1488:                            numEqualBullets++;
1489:                        // otherwise giveup, we have found how many are equal
1490:                        else
1491:                            break;
1492:                    }
1493:
1494:                    //unwind
1495:                    for (; m_genlistlevel > numEqualBullets; m_genlistlevel--) {
1496:                        buf.append(m_renderer.closeList(m_genlistBulletBuffer
1497:                                .charAt(m_genlistlevel - 1)));
1498:                        if (m_genlistlevel > 0)
1499:                            buf.append(m_renderer.closeListItem());
1500:                    }
1501:
1502:                    //rewind
1503:                    buf.append(m_renderer.openList(strBullets
1504:                            .charAt(numEqualBullets++)));
1505:                    for (int i = numEqualBullets; i < numBullets; i++) {
1506:                        buf.append(m_renderer.openListItem());
1507:                        buf.append(m_renderer.openList(strBullets.charAt(i)));
1508:                    }
1509:                    m_genlistlevel = numBullets;
1510:                }
1511:                buf.append(m_renderer.openListItem());
1512:
1513:                // work done, remember the new bullet list (in place of old one)
1514:                m_genlistBulletBuffer.setLength(0);
1515:                m_genlistBulletBuffer.append(strBullets);
1516:
1517:                return buf.toString();
1518:            }
1519:
1520:            private String unwindGeneralList() {
1521:                // String cStrShortName = "unwindGeneralList()";
1522:
1523:                StringBuffer buf = new StringBuffer();
1524:                int bulletCh;
1525:
1526:                //unwind
1527:                for (; m_genlistlevel > 0; m_genlistlevel--) {
1528:                    buf.append(m_renderer.closeListItem());
1529:                    buf.append(m_renderer.closeList(m_genlistBulletBuffer
1530:                            .charAt(m_genlistlevel - 1)));
1531:                }
1532:
1533:                m_genlistBulletBuffer.setLength(0);
1534:
1535:                return buf.toString();
1536:            }
1537:
1538:            private String handleDefinitionList() throws IOException {
1539:                if (!m_isdefinition) {
1540:                    m_isdefinition = true;
1541:
1542:                    m_closeTag = m_renderer.closeDefinitionItem()
1543:                            + m_renderer.closeDefinitionList();
1544:
1545:                    return startBlockLevel() + m_renderer.openDefinitionList()
1546:                            + m_renderer.openDefinitionTitle();
1547:                }
1548:
1549:                return ";";
1550:            }
1551:
1552:            private String handleOpenbracket() throws IOException {
1553:                StringBuffer sb = new StringBuffer();
1554:                int ch;
1555:                boolean isPlugin = false;
1556:
1557:                while ((ch = nextToken()) == '[') {
1558:                    sb.append((char) ch);
1559:                }
1560:
1561:                if (ch == '{') {
1562:                    isPlugin = true;
1563:                }
1564:
1565:                pushBack(ch);
1566:
1567:                if (sb.length() > 0) {
1568:                    return sb.toString();
1569:                }
1570:
1571:                //
1572:                //  Find end of hyperlink
1573:                //
1574:
1575:                ch = nextToken();
1576:
1577:                while (ch != -1) {
1578:                    if (ch == ']'
1579:                            && (!isPlugin || sb.charAt(sb.length() - 1) == '}')) {
1580:                        break;
1581:                    }
1582:
1583:                    sb.append((char) ch);
1584:
1585:                    ch = nextToken();
1586:                }
1587:
1588:                if (ch == -1) {
1589:                    log.debug("Warning: unterminated link detected!");
1590:                    return sb.toString();
1591:                }
1592:
1593:                return handleHyperlinks(sb.toString());
1594:            }
1595:
1596:            /**
1597:             *  Reads the stream until the current brace is closed or stream end. 
1598:             */
1599:            private String readBraceContent(char opening, char closing)
1600:                    throws IOException {
1601:                StringBuffer sb = new StringBuffer();
1602:                int braceLevel = 1;
1603:                int ch;
1604:                while ((ch = nextToken()) != -1) {
1605:                    if (ch == '\\') {
1606:                        continue;
1607:                    } else if (ch == opening) {
1608:                        braceLevel++;
1609:                    } else if (ch == closing) {
1610:                        braceLevel--;
1611:                        if (braceLevel == 0) {
1612:                            break;
1613:                        }
1614:                    }
1615:                    sb.append((char) ch);
1616:                }
1617:                return sb.toString();
1618:            }
1619:
1620:            /**
1621:             *  Reads the stream until it meets one of the specified
1622:             *  ending characters, or stream end.  The ending character will be left
1623:             *  in the stream.
1624:             */
1625:            private String readUntil(String endChars) throws IOException {
1626:                StringBuffer sb = new StringBuffer();
1627:                int ch = nextToken();
1628:
1629:                while (ch != -1) {
1630:                    if (ch == '\\') {
1631:                        ch = nextToken();
1632:                        if (ch == -1) {
1633:                            break;
1634:                        }
1635:                    } else {
1636:                        if (endChars.indexOf((char) ch) != -1) {
1637:                            pushBack(ch);
1638:                            break;
1639:                        }
1640:                    }
1641:                    sb.append((char) ch);
1642:                    ch = nextToken();
1643:                }
1644:
1645:                return sb.toString();
1646:            }
1647:
1648:            /**
1649:             *  Reads the stream while the characters that have been specified are
1650:             *  in the stream, returning then the result as a String.
1651:             */
1652:            private String readWhile(String endChars) throws IOException {
1653:                StringBuffer sb = new StringBuffer();
1654:                int ch = nextToken();
1655:
1656:                while (ch != -1) {
1657:                    if (endChars.indexOf((char) ch) == -1) {
1658:                        pushBack(ch);
1659:                        break;
1660:                    }
1661:
1662:                    sb.append((char) ch);
1663:                    ch = nextToken();
1664:                }
1665:
1666:                return sb.toString();
1667:            }
1668:
1669:            /**
1670:             *  Handles constructs of type %%(style) and %%class
1671:             * @param newLine
1672:             * @return
1673:             * @throws IOException
1674:             */
1675:            private String handleDiv(boolean newLine) throws IOException {
1676:                int ch = nextToken();
1677:
1678:                if (ch == '%') {
1679:                    StringBuffer sb = new StringBuffer();
1680:
1681:                    String style = null;
1682:                    String clazz = null;
1683:
1684:                    ch = nextToken();
1685:
1686:                    boolean isspan = false;
1687:
1688:                    //
1689:                    //  Style or class?
1690:                    //
1691:                    if (ch == '(') {
1692:                        style = readBraceContent('(', ')');
1693:                    } else if (Character.isLetter((char) ch)) {
1694:                        pushBack(ch);
1695:                        clazz = readUntil(" \t\n\r");
1696:                        ch = nextToken();
1697:
1698:                        //
1699:                        //  Pop out only spaces, so that the upcoming EOL check does not check the
1700:                        //  next line.
1701:                        //
1702:                        if (ch == '\n' || ch == '\r') {
1703:                            pushBack(ch);
1704:                        }
1705:                    } else {
1706:                        //
1707:                        // Anything else stops.
1708:                        //
1709:
1710:                        pushBack(ch);
1711:
1712:                        try {
1713:                            Boolean isSpan = (Boolean) m_styleStack.pop();
1714:
1715:                            if (isSpan == null) {
1716:                                // Fail quietly
1717:                            } else if (isSpan.booleanValue()) {
1718:                                sb.append(m_renderer.closeSpan());
1719:                            } else {
1720:                                sb.append(m_renderer.closeDiv());
1721:                            }
1722:                        } catch (EmptyStackException e) {
1723:                            log
1724:                                    .debug("Page '"
1725:                                            + m_context.getPage().getName()
1726:                                            + "' closes a %%-block that has not been opened.");
1727:                        }
1728:
1729:                        return sb.toString();
1730:                    }
1731:
1732:                    //
1733:                    //  Decide if we should open a div or a span?
1734:                    //
1735:                    String eol = peekAheadLine();
1736:
1737:                    if (eol.trim().length() > 0) {
1738:                        // There is stuff after the class
1739:
1740:                        sb.append(m_renderer.openSpan(style, clazz));
1741:
1742:                        m_styleStack.push(Boolean.TRUE);
1743:                    } else {
1744:                        sb.append(startBlockLevel());
1745:                        sb.append(m_renderer.openDiv(style, clazz));
1746:                        m_styleStack.push(Boolean.FALSE);
1747:                    }
1748:
1749:                    return sb.toString();
1750:                }
1751:
1752:                pushBack(ch);
1753:
1754:                return "%";
1755:            }
1756:
1757:            private String handleBar(boolean newLine) throws IOException {
1758:                StringBuffer sb = new StringBuffer();
1759:
1760:                if (!m_istable && !newLine) {
1761:                    return "|";
1762:                }
1763:
1764:                if (newLine) {
1765:                    if (!m_istable) {
1766:                        sb.append(startBlockLevel());
1767:                        sb.append(m_renderer.openTable());
1768:                        m_istable = true;
1769:                    }
1770:
1771:                    sb.append(m_renderer.openTableRow());
1772:                    m_closeTag = m_renderer.closeTableItem()
1773:                            + m_renderer.closeTableRow();
1774:                }
1775:
1776:                int ch = nextToken();
1777:
1778:                if (ch == '|') {
1779:                    if (!newLine) {
1780:                        sb.append(m_renderer.closeTableHeading());
1781:                    }
1782:                    sb.append(m_renderer.openTableHeading());
1783:                    m_closeTag = m_renderer.closeTableHeading()
1784:                            + m_renderer.closeTableRow();
1785:                } else {
1786:                    if (!newLine) {
1787:                        sb.append(m_renderer.closeTableItem());
1788:                    }
1789:                    sb.append(m_renderer.openTableItem());
1790:                    pushBack(ch);
1791:                }
1792:
1793:                return sb.toString();
1794:            }
1795:
1796:            /**
1797:             *  Generic escape of next character or entity.
1798:             */
1799:            private String handleTilde() throws IOException {
1800:                int ch = nextToken();
1801:
1802:                if (ch == '|' || ch == '~' || ch == '\\' || ch == '*'
1803:                        || ch == '#' || ch == '-' || ch == '!' || ch == '\''
1804:                        || ch == '_' || ch == '[' || ch == '{' || ch == ']'
1805:                        || ch == '}') {
1806:                    StringBuffer sb = new StringBuffer();
1807:                    sb.append((char) ch);
1808:                    sb.append(readWhile("" + (char) ch));
1809:                    return sb.toString();
1810:                }
1811:
1812:                if (Character.isUpperCase((char) ch)) {
1813:                    return String.valueOf((char) ch);
1814:                }
1815:
1816:                // No escape.
1817:                pushBack(ch);
1818:
1819:                return "~";
1820:            }
1821:
1822:            private void fillBuffer() throws IOException {
1823:                StringBuffer buf = new StringBuffer();
1824:                StringBuffer word = null;
1825:                int previousCh = -2;
1826:                int start = 0;
1827:
1828:                boolean quitReading = false;
1829:                boolean newLine = true; // FIXME: not true if reading starts in middle of buffer
1830:
1831:                while (!quitReading) {
1832:                    int ch = nextToken();
1833:                    String s = null;
1834:
1835:                    //
1836:                    //  Check if we're actually ending the preformatted mode.
1837:                    //  We still must do an entity transformation here.
1838:                    //
1839:                    if (m_isEscaping) {
1840:                        if (ch == '}') {
1841:                            buf.append(handleClosebrace());
1842:                        } else if (ch == -1) {
1843:                            quitReading = true;
1844:                        } else {
1845:                            m_renderer.doChar(buf, (char) ch);
1846:                        }
1847:
1848:                        continue;
1849:                    }
1850:
1851:                    //
1852:                    //  CamelCase detection, a non-trivial endeavour.
1853:                    //  We keep track of all white-space separated entities, which we
1854:                    //  hereby refer to as "words".  We then check for an existence
1855:                    //  of a CamelCase format text string inside the "word", and
1856:                    //  if one exists, we replace it with a proper link.
1857:                    //
1858:
1859:                    if (m_camelCaseLinks) {
1860:                        // Quick parse of start of a word boundary.
1861:
1862:                        if (word == null
1863:                                && (Character.isWhitespace((char) previousCh)
1864:                                        || WORD_SEPARATORS
1865:                                                .indexOf((char) previousCh) != -1 || newLine)
1866:                                && !Character.isWhitespace((char) ch)) {
1867:                            word = new StringBuffer();
1868:                        }
1869:
1870:                        // Are we currently tracking a word?
1871:                        if (word != null) {
1872:                            //
1873:                            //  Check for the end of the word.
1874:                            //
1875:
1876:                            if (Character.isWhitespace((char) ch) || ch == -1
1877:                                    || WORD_SEPARATORS.indexOf((char) ch) != -1) {
1878:                                String potentialLink = word.toString();
1879:
1880:                                String camelCase = checkForCamelCaseLink(potentialLink);
1881:
1882:                                if (camelCase != null) {
1883:                                    // System.out.println("Buffer is "+buf);
1884:
1885:                                    // System.out.println("  Replacing "+camelCase+" with proper link.");
1886:                                    start = buf.toString().lastIndexOf(
1887:                                            camelCase);
1888:                                    buf.replace(start, start
1889:                                            + camelCase.length(),
1890:                                            makeCamelCaseLink(camelCase));
1891:
1892:                                    // System.out.println("  Resulting with "+buf);
1893:                                } else {
1894:                                    // System.out.println("Checking for potential URI: "+potentialLink);
1895:                                    if (isExternalLink(potentialLink)) {
1896:                                        // System.out.println("buf="+buf);
1897:                                        start = buf.toString().lastIndexOf(
1898:                                                potentialLink);
1899:
1900:                                        if (start >= 0) {
1901:                                            String link = readUntil(" \t()[]{}!\"'\n|");
1902:
1903:                                            link = potentialLink + (char) ch
1904:                                                    + link; // Do not forget the start.
1905:
1906:                                            // System.out.println("start="+start+", pl="+potentialLink);
1907:
1908:                                            buf.replace(start, start
1909:                                                    + potentialLink.length(),
1910:                                                    makeDirectURILink(link));
1911:
1912:                                            // System.out.println("Resulting with "+buf);
1913:
1914:                                            ch = nextToken();
1915:                                        }
1916:                                    }
1917:                                }
1918:
1919:                                // We've ended a word boundary, so time to reset.
1920:                                word = null;
1921:                            } else {
1922:                                // This should only be appending letters and digits.
1923:                                word.append((char) ch);
1924:                            } // if end of word
1925:                        } // if word's not null
1926:
1927:                        // Always set the previous character to test for word starts.
1928:                        previousCh = ch;
1929:
1930:                    } // if m_camelCaseLinks
1931:
1932:                    //
1933:                    //  An empty line stops a list
1934:                    //
1935:                    if (newLine && ch != '*' && ch != '#' && ch != ' '
1936:                            && m_genlistlevel > 0) {
1937:                        buf.append(unwindGeneralList());
1938:                    }
1939:
1940:                    if (newLine && ch != '|' && m_istable) {
1941:                        buf.append(m_renderer.closeTable());
1942:                        m_istable = false;
1943:                        m_closeTag = null;
1944:                    }
1945:
1946:                    //
1947:                    //  Now, check the incoming token.
1948:                    //
1949:                    switch (ch) {
1950:                    case '\r':
1951:                        // DOS linefeeds we forget
1952:                        s = null;
1953:                        break;
1954:
1955:                    case '\n':
1956:                        //
1957:                        //  Close things like headings, etc.
1958:                        //
1959:                        if (m_closeTag != null) {
1960:                            buf.append(m_closeTag);
1961:                            m_closeTag = null;
1962:                        }
1963:
1964:                        m_isdefinition = false;
1965:
1966:                        if (newLine) {
1967:                            // Paragraph change.
1968:                            buf.append(startBlockLevel());
1969:
1970:                            //
1971:                            //  Figure out which elements cannot be enclosed inside
1972:                            //  a <p></p> pair according to XHTML rules.
1973:                            //
1974:                            String nextLine = peekAheadLine();
1975:                            if (nextLine.length() == 0
1976:                                    || (nextLine.length() > 0
1977:                                            && !nextLine.startsWith("{{{")
1978:                                            && !nextLine.startsWith("----")
1979:                                            && !nextLine.startsWith("%%") && "*#!;"
1980:                                            .indexOf(nextLine.charAt(0)) == -1)) {
1981:                                buf.append(m_renderer.openParagraph());
1982:                                m_isOpenParagraph = true;
1983:                            }
1984:                        } else {
1985:                            buf.append("\n");
1986:                            newLine = true;
1987:                        }
1988:
1989:                        break;
1990:
1991:                    case '\\':
1992:                        s = handleBackslash();
1993:                        break;
1994:
1995:                    case '_':
1996:                        s = handleUnderscore();
1997:                        break;
1998:
1999:                    case '\'':
2000:                        s = handleApostrophe();
2001:                        break;
2002:
2003:                    case '{':
2004:                        s = handleOpenbrace(newLine);
2005:                        break;
2006:
2007:                    case '}':
2008:                        s = handleClosebrace();
2009:                        break;
2010:
2011:                    case '-':
2012:                        s = handleDash();
2013:                        break;
2014:
2015:                    case '!':
2016:                        if (newLine) {
2017:                            s = handleHeading();
2018:                        } else {
2019:                            s = "!";
2020:                        }
2021:                        break;
2022:
2023:                    case ';':
2024:                        if (newLine) {
2025:                            s = handleDefinitionList();
2026:                        } else {
2027:                            s = ";";
2028:                        }
2029:                        break;
2030:
2031:                    case ':':
2032:                        if (m_isdefinition) {
2033:                            s = m_renderer.closeDefinitionTitle()
2034:                                    + m_renderer.openDefinitionItem();
2035:                            m_isdefinition = false;
2036:                        } else {
2037:                            s = ":";
2038:                        }
2039:                        break;
2040:
2041:                    case '[':
2042:                        s = handleOpenbracket();
2043:                        break;
2044:
2045:                    case '*':
2046:                        if (newLine) {
2047:                            pushBack('*');
2048:                            s = handleGeneralList();
2049:                        } else {
2050:                            s = "*";
2051:                        }
2052:                        break;
2053:
2054:                    case '#':
2055:                        if (newLine) {
2056:                            pushBack('#');
2057:                            s = handleGeneralList();
2058:                        } else {
2059:                            s = "#";
2060:                        }
2061:                        break;
2062:
2063:                    case '|':
2064:                        s = handleBar(newLine);
2065:                        break;
2066:
2067:                    case '<':
2068:                        s = m_allowHTML ? "<" : "&lt;";
2069:                        break;
2070:
2071:                    case '>':
2072:                        s = m_allowHTML ? ">" : "&gt;";
2073:                        break;
2074:
2075:                    case '\"':
2076:                        s = m_allowHTML ? "\"" : "&quot;";
2077:                        break;
2078:
2079:                    /*
2080:                    case '&':
2081:                    s = "&amp;";
2082:                    break;
2083:                     */
2084:                    case '~':
2085:                        s = handleTilde();
2086:                        break;
2087:
2088:                    case '%':
2089:                        s = handleDiv(newLine);
2090:                        break;
2091:
2092:                    case -1:
2093:                        if (m_closeTag != null) {
2094:                            buf.append(m_closeTag);
2095:                            m_closeTag = null;
2096:                        }
2097:                        quitReading = true;
2098:                        break;
2099:
2100:                    default:
2101:                        buf.append((char) ch);
2102:                        newLine = false;
2103:                        break;
2104:                    }
2105:
2106:                    if (s != null) {
2107:                        buf.append(s);
2108:                        newLine = false;
2109:                    }
2110:
2111:                }
2112:
2113:                m_data = new StringReader(buf.toString());
2114:            }
2115:
2116:            public int read() throws IOException {
2117:                int val = m_data.read();
2118:
2119:                if (val == -1) {
2120:                    fillBuffer();
2121:                    val = m_data.read();
2122:
2123:                    if (val == -1) {
2124:                        m_data = new StringReader(closeAll());
2125:
2126:                        val = m_data.read();
2127:                    }
2128:                }
2129:
2130:                return val;
2131:            }
2132:
2133:            public int read(char[] buf, int off, int len) throws IOException {
2134:                return m_data.read(buf, off, len);
2135:            }
2136:
2137:            public boolean ready() throws IOException {
2138:                log.debug("ready ? " + m_data.ready());
2139:                if (!m_data.ready()) {
2140:                    fillBuffer();
2141:                }
2142:
2143:                return m_data.ready();
2144:            }
2145:
2146:            public void close() {
2147:            }
2148:
2149:            /**
2150:             *  All HTML output stuff is here.  This class is a helper class, and will
2151:             *  be spawned later on with a proper API of its own so that we can have
2152:             *  different kinds of renderers.
2153:             */
2154:
2155:            // FIXME: Not everything is yet, and in the future this class will be spawned
2156:            //        out to be its own class.
2157:            private class HTMLRenderer extends TextRenderer {
2158:                private boolean m_isPreBlock = false;
2159:                private TranslatorReader m_cleanTranslator;
2160:
2161:                /*
2162:                   FIXME: It's relatively slow to create two TranslatorReaders each time.
2163:                 */
2164:                public HTMLRenderer() {
2165:                }
2166:
2167:                /**
2168:                 *  Does a lazy init.  Otherwise, we would get into a situation
2169:                 *  where HTMLRenderer would try and boot a TranslatorReader before
2170:                 *  the TranslatorReader it is contained by is up.
2171:                 */
2172:                private TranslatorReader getCleanTranslator() {
2173:                    if (m_cleanTranslator == null) {
2174:                        WikiContext dummyContext = new WikiContext(m_engine,
2175:                                m_context.getPage());
2176:                        m_cleanTranslator = new TranslatorReader(dummyContext,
2177:                                null, new TextRenderer());
2178:                        m_cleanTranslator.m_allowHTML = true;
2179:                    }
2180:
2181:                    return m_cleanTranslator;
2182:                }
2183:
2184:                public void doChar(StringBuffer buf, char ch) {
2185:                    if (ch == '<') {
2186:                        buf.append("&lt;");
2187:                    } else if (ch == '>') {
2188:                        buf.append("&gt;");
2189:                    } else if (ch == '&') {
2190:                        buf.append("&amp;");
2191:                    } else {
2192:                        buf.append(ch);
2193:                    }
2194:                }
2195:
2196:                public String openDiv(String style, String clazz) {
2197:                    StringBuffer sb = new StringBuffer();
2198:
2199:                    sb.append("<div");
2200:                    sb.append(style != null ? " style=\"" + style + "\"" : "");
2201:                    sb.append(clazz != null ? " class=\"" + clazz + "\"" : "");
2202:                    sb.append(">");
2203:
2204:                    return sb.toString();
2205:                }
2206:
2207:                public String openSpan(String style, String clazz) {
2208:                    StringBuffer sb = new StringBuffer();
2209:
2210:                    sb.append("<span");
2211:                    sb.append(style != null ? " style=\"" + style + "\"" : "");
2212:                    sb.append(clazz != null ? " class=\"" + clazz + "\"" : "");
2213:                    sb.append(">");
2214:
2215:                    return sb.toString();
2216:                }
2217:
2218:                public String closeDiv() {
2219:                    return "</div>";
2220:                }
2221:
2222:                public String closeSpan() {
2223:                    return "</span>";
2224:                }
2225:
2226:                public String openParagraph() {
2227:                    return "<p>";
2228:                }
2229:
2230:                public String closeParagraph() {
2231:                    return "</p>\n";
2232:                }
2233:
2234:                /**
2235:                 *  Writes out a text effect
2236:                 */
2237:                public String openTextEffect(int effect) {
2238:                    switch (effect) {
2239:                    case BOLD:
2240:                        return "<b>";
2241:                    case ITALIC:
2242:                        return "<i>";
2243:                    case TYPED:
2244:                        return "<tt>";
2245:                    }
2246:
2247:                    return "";
2248:                }
2249:
2250:                public String closeTextEffect(int effect) {
2251:                    switch (effect) {
2252:                    case BOLD:
2253:                        return "</b>";
2254:                    case ITALIC:
2255:                        return "</i>";
2256:                    case TYPED:
2257:                        return "</tt>";
2258:                    }
2259:
2260:                    return "";
2261:                }
2262:
2263:                public String openDefinitionItem() {
2264:                    return "<dd>";
2265:                }
2266:
2267:                public String closeDefinitionItem() {
2268:                    return "</dd>\n";
2269:                }
2270:
2271:                public String openDefinitionTitle() {
2272:                    return "<dt>";
2273:                }
2274:
2275:                public String closeDefinitionTitle() {
2276:                    return "</dt>";
2277:                }
2278:
2279:                public String openDefinitionList() {
2280:                    return "<dl>\n";
2281:                }
2282:
2283:                public String closeDefinitionList() {
2284:                    return "</dl>";
2285:                }
2286:
2287:                /**
2288:                 *  Write a HTMLized link depending on its type.
2289:                 *
2290:                 *  <p>This jsut calls makeLink() with "section" set to null.
2291:                 */
2292:                public String makeLink(int type, String link, String text) {
2293:                    return makeLink(type, link, text, null);
2294:                }
2295:
2296:                private final String getURL(String context, String link) {
2297:                    return m_context.getURL(context, link, null);
2298:                }
2299:
2300:                /**
2301:                 *  Write a HTMLized link depending on its type.
2302:                 *
2303:                 *  @param type Type of the link.
2304:                 *  @param link The actual link.
2305:                 *  @param text The user-visible text for the link.
2306:                 *  @param section Which named anchor to point to.  This may not have any
2307:                 *         effect on certain link types.  If null, will ignore it.
2308:                 */
2309:                public String makeLink(int type, String link, String text,
2310:                        String section) {
2311:                    String result;
2312:
2313:                    if (text == null)
2314:                        text = link;
2315:
2316:                    section = (section != null) ? ("#" + section) : "";
2317:
2318:                    // Make sure we make a link name that can be accepted
2319:                    // as a valid URL.
2320:
2321:                    String encodedlink = m_engine.encodeName(link);
2322:
2323:                    if (encodedlink.length() == 0) {
2324:                        type = EMPTY;
2325:                    }
2326:
2327:                    switch (type) {
2328:                    case READ:
2329:                        result = "<a class=\"wikipage\" href=\""
2330:                                + getURL(WikiContext.VIEW, link) + section
2331:                                + "\">" + text + "</a>";
2332:                        break;
2333:
2334:                    case EDIT:
2335:                        result = "<a class=\"editpage\" title=\"Create '"
2336:                                + link + "'\" href=\""
2337:                                + getURL(WikiContext.EDIT, link) + "\">" + text
2338:                                + "</a>";
2339:                        break;
2340:
2341:                    case EMPTY:
2342:                        result = "<u>" + text + "</u>";
2343:                        break;
2344:
2345:                    //
2346:                    //  These two are for local references - footnotes and 
2347:                    //  references to footnotes.
2348:                    //  We embed the page name (or whatever WikiContext gives us)
2349:                    //  to make sure the links are unique across Wiki.
2350:                    //
2351:                    case LOCALREF:
2352:                        result = "<a class=\"footnoteref\" href=\"#ref-"
2353:                                + m_context.getPage().getName() + "-" + link
2354:                                + "\">[" + text + "]</a>";
2355:                        break;
2356:
2357:                    case LOCAL:
2358:                        result = "<a class=\"footnote\" name=\"ref-"
2359:                                + m_context.getPage().getName() + "-"
2360:                                + link.substring(1) + "\">[" + text + "]</a>";
2361:                        break;
2362:
2363:                    //
2364:                    //  With the image, external and interwiki types we need to
2365:                    //  make sure nobody can put in Javascript or something else
2366:                    //  annoying into the links themselves.  We do this by preventing
2367:                    //  a haxor from stopping the link name short with quotes in 
2368:                    //  fillBuffer().
2369:                    //
2370:                    case IMAGE:
2371:                        result = "<img class=\"inline\" src=\"" + link
2372:                                + "\" alt=\"" + text + "\" />";
2373:                        break;
2374:
2375:                    case IMAGELINK:
2376:                        result = "<a href=\"" + text
2377:                                + "\"><img class=\"inline\" src=\"" + link
2378:                                + "\" alt=\"" + text + "\"/></a>";
2379:                        break;
2380:
2381:                    case IMAGEWIKILINK:
2382:                        String pagelink = getURL(WikiContext.VIEW, text);
2383:                        result = "<a class=\"wikipage\" href=\"" + pagelink
2384:                                + "\"><img class=\"inline\" src=\"" + link
2385:                                + "\" alt=\"" + text + "\" /></a>";
2386:                        break;
2387:
2388:                    case EXTERNAL:
2389:                        result = "<a class=\"external\" "
2390:                                + (m_useRelNofollow ? "rel=\"nofollow\" " : "")
2391:                                + "href=\"" + link + section + "\">" + text
2392:                                + "</a>";
2393:                        break;
2394:
2395:                    case INTERWIKI:
2396:                        result = "<a class=\"interwiki\" href=\"" + link
2397:                                + section + "\">" + text + "</a>";
2398:                        break;
2399:
2400:                    case ATTACHMENT:
2401:                        String attlink = getURL(WikiContext.ATTACH, link);
2402:
2403:                        String infolink = getURL(WikiContext.INFO, link);
2404:
2405:                        String imglink = getURL(WikiContext.NONE,
2406:                                "images/attachment_small.png");
2407:
2408:                        result = "<a class=\"attachment\" href=\"" + attlink
2409:                                + "\">" + text + "</a>" + "<a href=\""
2410:                                + infolink + "\"><img src=\"" + imglink
2411:                                + "\" border=\"0\" alt=\"(info)\"/></a>";
2412:                        break;
2413:
2414:                    default:
2415:                        result = "";
2416:                        break;
2417:                    }
2418:
2419:                    return result;
2420:                }
2421:
2422:                /**
2423:                 *  Writes HTML for error message.
2424:                 */
2425:
2426:                public String makeError(String error) {
2427:                    return "<span class=\"error\">" + error + "</span>";
2428:                }
2429:
2430:                /**
2431:                 *  Emits a vertical line.
2432:                 */
2433:
2434:                public String makeRuler() {
2435:                    return "<hr />";
2436:                }
2437:
2438:                /**
2439:                 *  Modifies the "hd" parameter to contain proper values.  Because
2440:                 *  an "id" tag may only contain [a-zA-Z0-9:_-], we'll replace the
2441:                 *  % after url encoding with '_'.
2442:                 */
2443:                private String makeHeadingAnchor(String baseName, String title,
2444:                        Heading hd) {
2445:                    hd.m_titleText = title;
2446:                    title = cleanLink(title);
2447:                    hd.m_titleSection = m_engine.encodeName(title);
2448:                    hd.m_titleAnchor = "section-"
2449:                            + m_engine.encodeName(baseName) + "-"
2450:                            + hd.m_titleSection;
2451:
2452:                    hd.m_titleAnchor = hd.m_titleAnchor.replace('%', '_');
2453:                    return hd.m_titleAnchor;
2454:                }
2455:
2456:                private String makeSectionTitle(String title) {
2457:                    title = title.trim();
2458:
2459:                    StringWriter outTitle = new StringWriter();
2460:
2461:                    try {
2462:                        TranslatorReader read = getCleanTranslator();
2463:                        read.setInputReader(new StringReader(title));
2464:                        FileUtil.copyContents(read, outTitle);
2465:                    } catch (IOException e) {
2466:                        log.fatal("CleanTranslator not working", e);
2467:                        throw new InternalWikiException(
2468:                                "CleanTranslator not working as expected, when cleaning title"
2469:                                        + e.getMessage());
2470:                    }
2471:
2472:                    return outTitle.toString();
2473:                }
2474:
2475:                /**
2476:                 *  Returns XHTML for the start of the heading.  Also sets the
2477:                 *  line-end emitter.
2478:                 *  @param level 
2479:                 *  @param headings A List to which heading should be added.
2480:                 */
2481:                public String makeHeading(int level, String title, Heading hd) {
2482:                    String res = "";
2483:
2484:                    String pageName = m_context.getPage().getName();
2485:
2486:                    String outTitle = makeSectionTitle(title);
2487:
2488:                    hd.m_level = level;
2489:
2490:                    switch (level) {
2491:                    case Heading.HEADING_SMALL:
2492:                        res = "<h4 id='"
2493:                                + makeHeadingAnchor(pageName, outTitle, hd)
2494:                                + "'>";
2495:                        m_closeTag = "</h4>";
2496:                        break;
2497:
2498:                    case Heading.HEADING_MEDIUM:
2499:                        res = "<h3 id='"
2500:                                + makeHeadingAnchor(pageName, outTitle, hd)
2501:                                + "'>";
2502:                        m_closeTag = "</h3>";
2503:                        break;
2504:
2505:                    case Heading.HEADING_LARGE:
2506:                        res = "<h2 id='"
2507:                                + makeHeadingAnchor(pageName, outTitle, hd)
2508:                                + "'>";
2509:                        m_closeTag = "</h2>";
2510:                        break;
2511:                    }
2512:
2513:                    return res;
2514:                }
2515:
2516:                /**
2517:                 *  @param bullet A character detailing which kind of a list
2518:                 *  we are dealing with here.  Options are '#' and '*'.
2519:                 */
2520:                public String openList(char bullet) {
2521:                    String res = "";
2522:
2523:                    if (bullet == '#')
2524:                        res = "<ol>\n";
2525:                    else if (bullet == '*')
2526:                        res = "<ul>\n";
2527:                    else
2528:                        log.info("Warning: unknown bullet character '" + bullet
2529:                                + "' at (+)");
2530:
2531:                    return res;
2532:                }
2533:
2534:                public String openListItem() {
2535:                    return "<li>";
2536:                }
2537:
2538:                public String closeListItem() {
2539:                    return "</li>\n";
2540:                }
2541:
2542:                /**
2543:                 *  @param bullet A character detailing which kind of a list
2544:                 *  we are dealing with here.  Options are '#' and '*'.
2545:                 */
2546:                public String closeList(char bullet) {
2547:                    String res = "";
2548:
2549:                    if (bullet == '#') {
2550:                        res = "</ol>\n";
2551:                    } else if (bullet == '*') {
2552:                        res = "</ul>\n";
2553:                    } else {
2554:                        //FIXME unknown character -> error
2555:                        log.info("Warning: unknown character in unwind '"
2556:                                + bullet + "'");
2557:                    }
2558:
2559:                    return res;
2560:                }
2561:
2562:                public String openTable() {
2563:                    return "<table class=\"wikitable\" border=\"1\">\n";
2564:                }
2565:
2566:                public String closeTable() {
2567:                    return "</table>\n";
2568:                }
2569:
2570:                public String openTableRow() {
2571:                    return "<tr>";
2572:                }
2573:
2574:                public String closeTableRow() {
2575:                    return "</tr>";
2576:                }
2577:
2578:                public String openTableItem() {
2579:                    return "<td>";
2580:                }
2581:
2582:                public String closeTableItem() {
2583:                    return "</td>";
2584:                }
2585:
2586:                public String openTableHeading() {
2587:                    return "<th>";
2588:                }
2589:
2590:                public String closeTableHeading() {
2591:                    return "</th>";
2592:                }
2593:
2594:                public String openPreformatted(boolean isBlock) {
2595:                    m_isPreBlock = isBlock;
2596:
2597:                    if (isBlock) {
2598:                        return "<pre>";
2599:                    }
2600:
2601:                    return "<span style=\"font-family:monospace; whitespace:pre;\">";
2602:                }
2603:
2604:                public String closePreformatted() {
2605:                    if (m_isPreBlock)
2606:                        return "</pre>\n";
2607:
2608:                    return "</span>";
2609:                }
2610:
2611:                /**
2612:                 *  If outlink images are turned on, returns a link to the outward
2613:                 *  linking image.
2614:                 */
2615:                public String outlinkImage() {
2616:                    if (m_useOutlinkImage) {
2617:                        return "<img class=\"outlink\" src=\""
2618:                                + getURL(WikiContext.NONE, "images/out.png")
2619:                                + "\" alt=\"\" />";
2620:                    }
2621:
2622:                    return "";
2623:                }
2624:
2625:                /**
2626:                 *  @param clear If true, then flushes all thingies.
2627:                 */
2628:                public String lineBreak(boolean clear) {
2629:                    if (clear)
2630:                        return "<br clear=\"all\" />";
2631:
2632:                    return "<br />";
2633:                }
2634:
2635:            } // HTMLRenderer
2636:
2637:            /**
2638:             *  A very simple class for outputting plain text with no
2639:             *  formatting.
2640:             */
2641:            private class TextRenderer {
2642:                public void doChar(StringBuffer buf, char ch) {
2643:                    buf.append(ch);
2644:                }
2645:
2646:                public String openDiv(String style, String clazz) {
2647:                    return "";
2648:                }
2649:
2650:                public String closeDiv() {
2651:                    return "";
2652:                }
2653:
2654:                public String openSpan(String style, String clazz) {
2655:                    return "";
2656:                }
2657:
2658:                public String closeSpan() {
2659:                    return "";
2660:                }
2661:
2662:                public String openParagraph() {
2663:                    return "";
2664:                }
2665:
2666:                public String closeParagraph() {
2667:                    return "\n\n";
2668:                }
2669:
2670:                /**
2671:                 *  Writes out a text effect
2672:                 */
2673:                public String openTextEffect(int effect) {
2674:                    return "";
2675:                }
2676:
2677:                public String closeTextEffect(int effect) {
2678:                    return "";
2679:                }
2680:
2681:                public String openDefinitionItem() {
2682:                    return " : ";
2683:                }
2684:
2685:                public String closeDefinitionItem() {
2686:                    return "\n";
2687:                }
2688:
2689:                public String openDefinitionTitle() {
2690:                    return "";
2691:                }
2692:
2693:                public String closeDefinitionTitle() {
2694:                    return "";
2695:                }
2696:
2697:                public String openDefinitionList() {
2698:                    return "";
2699:                }
2700:
2701:                public String closeDefinitionList() {
2702:                    return "\n";
2703:                }
2704:
2705:                /**
2706:                 *  Write a HTMLized link depending on its type.
2707:                 *
2708:                 *  <p>This jsut calls makeLink() with "section" set to null.
2709:                 */
2710:                public String makeLink(int type, String link, String text) {
2711:                    return text;
2712:                }
2713:
2714:                public String makeLink(int type, String link, String text,
2715:                        String section) {
2716:                    return text;
2717:                }
2718:
2719:                /**
2720:                 *  Writes HTML for error message.
2721:                 */
2722:
2723:                public String makeError(String error) {
2724:                    return "ERROR: " + error;
2725:                }
2726:
2727:                /**
2728:                 *  Emits a vertical line.
2729:                 */
2730:
2731:                public String makeRuler() {
2732:                    return "----------------------------------";
2733:                }
2734:
2735:                /**
2736:                 *  Returns XHTML for the start of the heading.  Also sets the
2737:                 *  line-end emitter.
2738:                 *  @param level 
2739:                 */
2740:                public String makeHeading(int level, String title, Heading hd) {
2741:                    String res = "";
2742:
2743:                    String pageName = m_context.getPage().getName();
2744:
2745:                    title = title.trim();
2746:
2747:                    hd.m_level = level;
2748:                    hd.m_titleText = title;
2749:                    hd.m_titleSection = "";
2750:                    hd.m_titleAnchor = "";
2751:
2752:                    switch (level) {
2753:                    case Heading.HEADING_SMALL:
2754:                        res = title;
2755:                        m_closeTag = "\n\n";
2756:                        break;
2757:
2758:                    case Heading.HEADING_MEDIUM:
2759:                        res = title;
2760:                        m_closeTag = "\n"
2761:                                + TextUtil.repeatString("-", title.length())
2762:                                + "\n\n";
2763:                        break;
2764:
2765:                    case Heading.HEADING_LARGE:
2766:                        res = title.toUpperCase();
2767:                        m_closeTag = "\n"
2768:                                + TextUtil.repeatString("=", title.length())
2769:                                + "\n\n";
2770:                        break;
2771:                    }
2772:
2773:                    return res;
2774:                }
2775:
2776:                /**
2777:                 *  @param bullet A character detailing which kind of a list
2778:                 *  we are dealing with here.  Options are '#' and '*'.
2779:                 */
2780:                // FIXME: Should really start a different kind of list depending
2781:                //        on the bullet type
2782:                public String openList(char bullet) {
2783:                    return "\n";
2784:                }
2785:
2786:                public String openListItem() {
2787:                    return "- ";
2788:                }
2789:
2790:                public String closeListItem() {
2791:                    return "\n";
2792:                }
2793:
2794:                /**
2795:                 *  @param bullet A character detailing which kind of a list
2796:                 *  we are dealing with here.  Options are '#' and '*'.
2797:                 */
2798:                public String closeList(char bullet) {
2799:                    return "\n\n";
2800:                }
2801:
2802:                public String openTable() {
2803:                    return "\n";
2804:                }
2805:
2806:                public String closeTable() {
2807:                    return "\n";
2808:                }
2809:
2810:                public String openTableRow() {
2811:                    return "";
2812:                }
2813:
2814:                public String closeTableRow() {
2815:                    return "\n";
2816:                }
2817:
2818:                public String openTableItem() {
2819:                    return "\t";
2820:                }
2821:
2822:                public String closeTableItem() {
2823:                    return "";
2824:                }
2825:
2826:                public String openTableHeading() {
2827:                    return "\t";
2828:                }
2829:
2830:                public String closeTableHeading() {
2831:                    return "";
2832:                }
2833:
2834:                public String openPreformatted(boolean isBlock) {
2835:                    return "";
2836:                }
2837:
2838:                public String closePreformatted() {
2839:                    return "\n";
2840:                }
2841:
2842:                /**
2843:                 *  If outlink images are turned on, returns a link to the outward
2844:                 *  linking image.
2845:                 */
2846:                public String outlinkImage() {
2847:                    return "";
2848:                }
2849:
2850:                /**
2851:                 *  @param clear If true, then flushes all thingies.
2852:                 */
2853:                public String lineBreak(boolean clear) {
2854:                    return "\n";
2855:                }
2856:
2857:            } // TextRenderer
2858:
2859:            /**
2860:             *  This class is used to store the headings in a manner which
2861:             *  allow the building of a Table Of Contents.
2862:             */
2863:            public class Heading {
2864:                public static final int HEADING_SMALL = 1;
2865:                public static final int HEADING_MEDIUM = 2;
2866:                public static final int HEADING_LARGE = 3;
2867:
2868:                public int m_level;
2869:                public String m_titleText;
2870:                public String m_titleAnchor;
2871:                public String m_titleSection;
2872:            }
2873:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.