You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jspwiki.apache.org by ju...@apache.org on 2021/10/16 16:06:13 UTC
[jspwiki] 06/08: First stab at refactor
This is an automated email from the ASF dual-hosted git repository.
juanpablo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jspwiki.git
commit b60ef16ed69f10634e6dca7a5515df1873b36f56
Author: Juan Pablo Santos RodrÃguez <ju...@gmail.com>
AuthorDate: Sat Oct 16 17:59:54 2021 +0200
First stab at refactor
so it'll be easier in the future to make it output other types of wiki syntaxes
* Deleted ForgetNullValuesLinkedHashMap, used only in one place, it's clearer to explicitly put if value not empty
* Deleted PersistentMapDecorator, used only in one place, Stream's api is clearer + avoid rethrowing meaningless IOException
* Print methods.. renamed to translate..
* Extracted case statements on printChildren to their own decorator methods
* Other parts of the class outputting wiki syntax extracted to their own de* Replaced privacorator methods
te LiStack by plain java.util.Stack
* Refactored private PreStack so it extends java.util.Stack
* Moved getXPathElement method to XmlUtil, as a static method
---
.../htmltowiki/ForgetNullValuesLinkedHashMap.java | 44 -
.../wiki/htmltowiki/PersistentMapDecorator.java | 181 ---
.../htmltowiki/XHtmlElementToWikiTranslator.java | 1516 +++++++++-----------
.../main/java/org/apache/wiki/util/XmlUtil.java | 9 +
.../java/org/apache/wiki/util/XmlUtilTest.java | 8 +
5 files changed, 723 insertions(+), 1035 deletions(-)
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/htmltowiki/ForgetNullValuesLinkedHashMap.java b/jspwiki-main/src/main/java/org/apache/wiki/htmltowiki/ForgetNullValuesLinkedHashMap.java
deleted file mode 100644
index e5b1973..0000000
--- a/jspwiki-main/src/main/java/org/apache/wiki/htmltowiki/ForgetNullValuesLinkedHashMap.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- KIND, either express or implied. See the License for the
- specific language governing permissions and limitations
- under the License.
- */
-package org.apache.wiki.htmltowiki;
-
-import java.util.LinkedHashMap;
-
-/**
- * A LinkedHashMap that does not put null values into the map.
- *
- */
-public class ForgetNullValuesLinkedHashMap<K,V> extends LinkedHashMap<K,V>
-{
- private static final long serialVersionUID = 0L;
-
- /**
- * {@inheritDoc}
- */
- @Override
- public V put(final K key, final V value )
- {
- if( value != null )
- {
- return super.put( key, value );
- }
-
- return null;
- }
-}
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/htmltowiki/PersistentMapDecorator.java b/jspwiki-main/src/main/java/org/apache/wiki/htmltowiki/PersistentMapDecorator.java
deleted file mode 100644
index 251e3d6..0000000
--- a/jspwiki-main/src/main/java/org/apache/wiki/htmltowiki/PersistentMapDecorator.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you under the Apache License, Version 2.0 (the
- "License"); you may not use this file except in compliance
- with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing,
- software distributed under the License is distributed on an
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- KIND, either express or implied. See the License for the
- specific language governing permissions and limitations
- under the License.
- */
-package org.apache.wiki.htmltowiki;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.Properties;
-import java.util.Set;
-
-/**
- * Adds the load / save - functionality known from the Properties - class to any
- * Map implementation.
- *
- */
-public class PersistentMapDecorator extends Properties
-{
- private static final long serialVersionUID = 0L;
-
- private final Map< Object, Object > m_delegate;
-
- /**
- * Creates a new decorator for a given map.
- *
- * @param delegate The map to create a decorator for.
- */
- public PersistentMapDecorator(final Map< Object, Object > delegate )
- {
- m_delegate = delegate;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void clear()
- {
- m_delegate.clear();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean containsKey(final Object key )
- {
- return m_delegate.containsKey( key );
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean containsValue(final Object value )
- {
- return m_delegate.containsValue( value );
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Set< Map.Entry< Object, Object > > entrySet()
- {
- return m_delegate.entrySet();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean equals(final Object obj )
- {
- return m_delegate.equals( obj );
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Object get(final Object key )
- {
- return m_delegate.get( key );
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int hashCode()
- {
- return m_delegate.hashCode();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean isEmpty()
- {
- return m_delegate.isEmpty();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Set< Object > keySet()
- {
- return m_delegate.keySet();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Object put(final Object arg0, final Object arg1 )
- {
- return m_delegate.put( arg0, arg1 );
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void putAll(final Map< ?, ? > arg0 )
- {
- m_delegate.putAll( arg0 );
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Object remove(final Object key )
- {
- return m_delegate.remove( key );
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int size()
- {
- return m_delegate.size();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String toString()
- {
- return m_delegate.toString();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Collection< Object > values()
- {
- return m_delegate.values();
- }
-}
diff --git a/jspwiki-main/src/main/java/org/apache/wiki/htmltowiki/XHtmlElementToWikiTranslator.java b/jspwiki-main/src/main/java/org/apache/wiki/htmltowiki/XHtmlElementToWikiTranslator.java
index c2d3bbf..78ffbf7 100644
--- a/jspwiki-main/src/main/java/org/apache/wiki/htmltowiki/XHtmlElementToWikiTranslator.java
+++ b/jspwiki-main/src/main/java/org/apache/wiki/htmltowiki/XHtmlElementToWikiTranslator.java
@@ -18,7 +18,9 @@
*/
package org.apache.wiki.htmltowiki;
+import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
+import org.apache.wiki.util.XmlUtil;
import org.jdom2.Attribute;
import org.jdom2.Content;
import org.jdom2.Element;
@@ -26,45 +28,39 @@ import org.jdom2.JDOMException;
import org.jdom2.Text;
import org.jdom2.xpath.XPathFactory;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
+import java.util.Stack;
/**
- * Converting XHtml to Wiki Markup. This is the class which does all of the heavy loading.
- *
+ * Converting XHtml to Wiki Markup. This is the class which does all the heavy loading.
*/
-public class XHtmlElementToWikiTranslator
-{
+public class XHtmlElementToWikiTranslator {
- private final XHtmlToWikiConfig m_config;
+ private final XHtmlToWikiConfig config;
- private final WhitespaceTrimWriter m_outTimmer;
+ private final WhitespaceTrimWriter outTrimmer = new WhitespaceTrimWriter();
- private final PrintWriter m_out;
+ private final PrintWriter out = new PrintWriter( outTrimmer );
- private final LiStack m_liStack = new LiStack();
+ private final Stack< String > liStack = new Stack<>();
- private final PreStack m_preStack = new PreStack();
+ private final Stack< String > preStack = new PreStack();
/**
* Create a new translator using the default config.
*
* @param base The base element from which to start translating.
- * @throws IOException If reading of the DOM tree fails.
* @throws JDOMException If the DOM tree is faulty.
*/
- public XHtmlElementToWikiTranslator(final Element base ) throws IOException, JDOMException
- {
+ public XHtmlElementToWikiTranslator(final Element base ) throws JDOMException {
this( base, new XHtmlToWikiConfig() );
}
@@ -73,941 +69,841 @@ public class XHtmlElementToWikiTranslator
*
* @param base The base element from which to start translating.
* @param config The config to use.
- * @throws IOException If reading of the DOM tree fails.
* @throws JDOMException If the DOM tree is faulty.
*/
- public XHtmlElementToWikiTranslator(final Element base, final XHtmlToWikiConfig config ) throws IOException, JDOMException
- {
- this.m_config = config;
- m_outTimmer = new WhitespaceTrimWriter();
- m_out = new PrintWriter( m_outTimmer );
- print( base );
+ public XHtmlElementToWikiTranslator( final Element base, final XHtmlToWikiConfig config ) throws JDOMException {
+ this.config = config;
+ translate( base );
}
/**
- * FIXME: I have no idea what this does...
+ * Outputs parsed wikitext.
*
- * @return Something.
+ * @return parsed wikitext.
*/
- public String getWikiString()
- {
- return m_outTimmer.toString();
- }
-
- private void print( String s )
- {
- s = StringEscapeUtils.unescapeHtml4( s );
- m_out.print( s );
- }
-
- private void print(final Object element ) throws IOException, JDOMException
- {
- if( element instanceof Text )
- {
- final Text t = (Text)element;
- String s = t.getText();
- if( m_preStack.isPreMode() )
- {
- m_out.print( s );
- }
- else
- {
- // remove all "line terminator" characters
- s = s.replaceAll( "[\\r\\n\\f\\u0085\\u2028\\u2029]", "" );
- m_out.print( s );
- }
- }
- else if( element instanceof Element )
- {
- final Element base = (Element)element;
- String n = base.getName().toLowerCase();
- if( "imageplugin".equals( base.getAttributeValue( "class" ) ) )
- {
- printImage( base );
- }
- else if( "wikiform".equals( base.getAttributeValue( "class" ) ) )
- {
+ public String getWikiString() {
+ return outTrimmer.toString();
+ }
+
+ private void translate( final Content element ) throws JDOMException {
+ if( element instanceof Text ) {
+ decorateMarkupForText( ( Text )element );
+ } else if( element instanceof Element ) {
+ final Element base = ( Element )element;
+ if( "imageplugin".equals( base.getAttributeValue( "class" ) ) ) {
+ translateImage( base );
+ } else if( "wikiform".equals( base.getAttributeValue( "class" ) ) ) {
// only print the children if the div's class="wikiform", but not the div itself.
- printChildren( base );
+ translateChildren( base );
+ } else {
+ final ElementDecoratorData dto = buildElementDecoratorDataFrom( base );
+ decorateMarkupForElementWith( dto );
}
- else
- {
- boolean bold = false;
- boolean italic = false;
- boolean monospace = false;
- String cssSpecial = null;
- final String cssClass = base.getAttributeValue( "class" );
-
- // accomodate a FCKeditor bug with Firefox: when a link is removed, it becomes <span class="wikipage">text</span>.
- final boolean ignoredCssClass = cssClass != null && cssClass.matches( "wikipage|createpage|external|interwiki|attachment|inline-code" );
-
- Map< Object, Object > styleProps = null;
-
- // Only get the styles if it's not a link element. Styles for link elements are
- // handled as an AugmentedWikiLink instead.
- if( !n.equals( "a" ) )
- {
- styleProps = getStylePropertiesLowerCase( base );
- }
-
- if("inline-code".equals(cssClass))
- {
- monospace = true;
- }
-
- if( styleProps != null )
- {
- final String weight = (String)styleProps.remove( "font-weight" );
- final String style = (String)styleProps.remove( "font-style" );
+ }
+ }
- if( n.equals( "p" ) )
- {
- // change it so we can print out the css styles for <p>
- n = "div";
- }
+ void decorateMarkupForString( final String s ) {
+ out.print( StringEscapeUtils.unescapeHtml4( s ) );
+ }
- italic = "oblique".equals( style ) || "italic".equals( style );
- bold = "bold".equals( weight ) || "bolder".equals( weight );
- if( !styleProps.isEmpty() )
- {
- cssSpecial = propsToStyleString( styleProps );
- }
- }
- if( cssClass != null && !ignoredCssClass )
- {
- if( n.equals( "div" ) )
- {
- m_out.print( "\n%%" + cssClass + " \n" );
- }
- else if( n.equals( "span" ) )
- {
- m_out.print( "%%" + cssClass + " " );
- }
- }
- if( bold )
- {
- m_out.print( "__" );
- }
- if( italic )
- {
- m_out.print( "''" );
- }
- if( monospace )
- {
- m_out.print( "{{{" );
- m_preStack.push();
- }
- if( cssSpecial != null )
- {
- if( n.equals( "div" ) )
- {
- m_out.print( "\n%%(" + cssSpecial + " )\n" );
- }
- else
- {
- m_out.print( "%%(" + cssSpecial + " )" );
- }
- }
- printChildren( base );
- if( cssSpecial != null )
- {
- if( n.equals( "div" ) )
- {
- m_out.print( "\n/%\n" );
- }
- else
- {
- m_out.print( "/%" );
- }
- }
- if( monospace )
- {
- m_preStack.pop();
- m_out.print( "}}}" );
- }
- if( italic )
- {
- m_out.print( "''" );
- }
- if( bold )
- {
- m_out.print( "__" );
- }
- if( cssClass != null && !ignoredCssClass )
- {
- if( n.equals( "div" ) )
- {
- m_out.print( "\n/%\n" );
- }
- else if( n.equals( "span" ) )
- {
- m_out.print( "/%" );
- }
- }
- }
+ void decorateMarkupForText( final Text text ) {
+ String s = text.getText();
+ if( preStack.isEmpty() ) {
+ // remove all "line terminator" characters
+ s = s.replaceAll( "[\\r\\n\\f\\u0085\\u2028\\u2029]", "" );
}
+ out.print( s );
}
- private void printChildren(final Element base ) throws IOException, JDOMException
- {
- for(final Iterator< Content > i = base.getContent().iterator(); i.hasNext(); )
- {
- final Content c = i.next();
- if( c instanceof Element )
- {
- final Element e = (Element)c;
- final String n = e.getName().toLowerCase();
- switch ( n ) {
- case "h1":
- m_out.print( "\n!!! " );
- print( e );
- m_out.println();
- break;
- case "h2":
- m_out.print( "\n!!! " );
- print( e );
- m_out.println();
- break;
- case "h3":
- m_out.print( "\n!! " );
- print( e );
- m_out.println();
- break;
- case "h4":
- m_out.print( "\n! " );
- print( e );
- m_out.println();
- break;
- case "p":
- if ( e.getContentSize() != 0 ) // we don't want to print empty elements: <p></p>
- {
- m_out.println();
- print( e );
- m_out.println();
- }
- break;
- case "br":
- if ( m_preStack.isPreMode() ) {
- m_out.println();
- } else {
- final String parentElementName = base.getName().toLowerCase();
-
- //
- // To beautify the generated wiki markup, we print a newline character after a linebreak.
- // It's only safe to do this when the parent element is a <p> or <div>; when the parent
- // element is a table cell or list item, a newline character would break the markup.
- // We also check that this isn't being done inside a plugin body.
- //
- if ( parentElementName.matches( "p|div" )
- && !base.getText().matches( "(?s).*\\[\\{.*\\}\\].*" ) ) {
- m_out.print( " \\\\\n" );
- } else {
- m_out.print( " \\\\" );
- }
- }
- print( e );
- break;
- case "hr":
- m_out.println();
- print( "----" );
- print( e );
- m_out.println();
- break;
- case "table":
- if ( !m_outTimmer.isCurrentlyOnLineBegin() ) {
- m_out.println();
- }
- print( e );
- break;
- case "tr":
- print( e );
- m_out.println();
- break;
- case "td":
- m_out.print( "| " );
- print( e );
- if ( !m_preStack.isPreMode() ) {
- print( " " );
- }
- break;
- case "th":
- m_out.print( "|| " );
- print( e );
- if ( !m_preStack.isPreMode() ) {
- print( " " );
- }
- break;
- case "a":
- if ( !isIgnorableWikiMarkupLink( e ) ) {
- if ( e.getChild( "IMG" ) != null ) {
- printImage( e );
- } else {
- String ref = e.getAttributeValue( "href" );
- if ( ref == null ) {
- if ( isUndefinedPageLink( e ) ) {
- m_out.print( "[" );
- print( e );
- m_out.print( "]" );
- } else {
- print( e );
- }
- } else {
- ref = trimLink( ref );
- if ( ref != null ) {
- if ( ref.startsWith( "#" ) ) // This is a link to a footnote.
- {
- // convert "#ref-PageName-1" to just "1"
- final String href = ref.replaceFirst( "#ref-.+-(\\d+)", "$1" );
-
- // remove the brackets around "[1]"
- final String textValue = e.getValue().substring( 1, ( e.getValue().length() - 1 ) );
-
- if ( href.equals( textValue ) ) { // handles the simplest case. Example: [1]
- print( e );
- } else { // handles the case where the link text is different from the href. Example: [something|1]
- m_out.print( "[" + textValue + "|" + href + "]" );
- }
- } else {
- final Map<String, String> augmentedWikiLinkAttributes = getAugmentedWikiLinkAttributes( e );
-
- m_out.print( "[" );
- print( e );
- if ( !e.getTextTrim().equalsIgnoreCase( ref ) ) {
- m_out.print( "|" );
- print( ref );
-
- if ( !augmentedWikiLinkAttributes.isEmpty() ) {
- m_out.print( "|" );
-
- final String augmentedWikiLink = augmentedWikiLinkMapToString( augmentedWikiLinkAttributes );
- m_out.print( augmentedWikiLink );
- }
- } else if ( !augmentedWikiLinkAttributes.isEmpty() ) {
- // If the ref has the same value as the text and also if there
- // are attributes, then just print: [ref|ref|attributes] .
- m_out.print( "|" + ref + "|" );
- final String augmentedWikiLink = augmentedWikiLinkMapToString( augmentedWikiLinkAttributes );
- m_out.print( augmentedWikiLink );
- }
-
- m_out.print( "]" );
- }
- }
- }
- }
- }
- break;
- case "b":
- case "strong":
- m_out.print( "__" );
- print( e );
- m_out.print( "__" );
- break;
- case "i":
- case "em":
- case "address":
- m_out.print( "''" );
- print( e );
- m_out.print( "''" );
- break;
- case "u":
- m_out.print( "%%( text-decoration:underline; )" );
- print( e );
- m_out.print( "/%" );
- break;
- case "strike":
- m_out.print( "%%strike " );
- print( e );
- m_out.print( "/%" );
- // NOTE: don't print a space before or after the double percents because that can break words into two.
- // For example: %%(color:red)ABC%%%%(color:green)DEF%% is different from %%(color:red)ABC%% %%(color:green)DEF%%
- break;
- case "sup":
- m_out.print( "%%sup " );
- print( e );
- m_out.print( "/%" );
- break;
- case "sub":
- m_out.print( "%%sub " );
- print( e );
- m_out.print( "/%" );
- break;
- case "dl":
- m_out.print( "\n" );
- print( e );
-
- // print a newline after the definition list. If we don't,
- // it may cause problems for the subsequent element.
- m_out.print( "\n" );
- break;
- case "dt":
- m_out.print( ";" );
- print( e );
- break;
- case "dd":
- m_out.print( ":" );
- print( e );
- break;
- case "ul":
- m_out.println();
- m_liStack.push( "*" );
- print( e );
- m_liStack.pop();
- break;
- case "ol":
- m_out.println();
- m_liStack.push( "#" );
- print( e );
- m_liStack.pop();
- break;
- case "li":
- m_out.print( m_liStack + " " );
- print( e );
-
- // The following line assumes that the XHTML has been "pretty-printed"
- // (newlines separate child elements from their parents).
- final boolean lastListItem = base.indexOf( e ) == ( base.getContentSize() - 2 );
- final boolean sublistItem = m_liStack.toString().length() > 1;
-
- // only print a newline if this <li> element is not the last item within a sublist.
- if ( !sublistItem || !lastListItem ) {
- m_out.println();
- }
- break;
- case "pre":
- m_out.print( "\n{{{" ); // start JSPWiki "code blocks" on its own line
-
- m_preStack.push();
- print( e );
- m_preStack.pop();
-
- // print a newline after the closing braces
- // to avoid breaking any subsequent wiki markup that follows.
- m_out.print( "}}}\n" );
- break;
- case "code":
- case "tt":
- m_out.print( "{{" );
- m_preStack.push();
- print( e );
- m_preStack.pop();
- m_out.print( "}}" );
- // NOTE: don't print a newline after the closing brackets because if the Text is inside
- // a table or list, it would break it if there was a subsequent row or list item.
- break;
- case "img":
- if ( !isIgnorableWikiMarkupLink( e ) ) {
- m_out.print( "[" );
- print( trimLink( e.getAttributeValue( "src" ) ) );
- m_out.print( "]" );
- }
- break;
- case "form": {
- // remove the hidden input where name="formname" since a new one will be generated again when the xhtml is rendered.
- final Element formName = getXPathElement( e, "INPUT[@name='formname']" );
- if ( formName != null ) {
- formName.detach();
- }
+ ElementDecoratorData buildElementDecoratorDataFrom( final Element base ) {
+ String n = base.getName().toLowerCase();
+ boolean bold = false;
+ boolean italic = false;
+ boolean monospace = false;
+ String cssSpecial = null;
+ final String cssClass = base.getAttributeValue( "class" );
- final String name = e.getAttributeValue( "name" );
+ // accomodate a FCKeditor bug with Firefox: when a link is removed, it becomes <span class="wikipage">text</span>.
+ final boolean ignoredCssClass = cssClass != null && cssClass.matches( "wikipage|createpage|external|interwiki|attachment|inline-code" );
- m_out.print( "\n[{FormOpen" );
+ Map< Object, Object > styleProps = null;
- if ( name != null ) {
- m_out.print( " form='" + name + "'" );
- }
+ // Only get the styles if it's not a link element. Styles for link elements are handled as an AugmentedWikiLink instead.
+ if( !n.equals( "a" ) ) {
+ styleProps = getStylePropertiesLowerCase( base );
+ }
- m_out.print( "}]\n" );
+ if( "inline-code".equals( cssClass ) ) {
+ monospace = true;
+ }
- print( e );
- m_out.print( "\n[{FormClose}]\n" );
- break;
- }
- case "input": {
- final String type = e.getAttributeValue( "type" );
- String name = e.getAttributeValue( "name" );
- final String value = e.getAttributeValue( "value" );
- final String checked = e.getAttributeValue( "checked" );
+ if( styleProps != null ) {
+ final String weight = ( String ) styleProps.remove( "font-weight" );
+ final String style = ( String ) styleProps.remove( "font-style" );
- m_out.print( "[{FormInput" );
+ if ( n.equals( "p" ) ) {
+ // change it, so we can print out the css styles for <p>
+ n = "div";
+ }
- if ( type != null ) {
- m_out.print( " type='" + type + "'" );
- }
- if ( name != null ) {
- // remove the "nbf_" that was prepended since new one will be generated again when the xhtml is rendered.
- if ( name.startsWith( "nbf_" ) ) {
- name = name.substring( 4, name.length() );
- }
- m_out.print( " name='" + name + "'" );
- }
- if ( value != null && !value.equals( "" ) ) {
- m_out.print( " value='" + value + "'" );
- }
- if ( checked != null ) {
- m_out.print( " checked='" + checked + "'" );
- }
+ italic = "oblique".equals( style ) || "italic".equals( style );
+ bold = "bold".equals( weight ) || "bolder".equals( weight );
+ if ( !styleProps.isEmpty() ) {
+ cssSpecial = propsToStyleString( styleProps );
+ }
+ }
- m_out.print( "}]" );
+ final ElementDecoratorData dto = new ElementDecoratorData();
+ dto.base = base;
+ dto.bold = bold;
+ dto.cssClass = cssClass;
+ dto.cssSpecial = cssSpecial;
+ dto.htmlBase = n;
+ dto.ignoredCssClass = ignoredCssClass;
+ dto.italic = italic;
+ dto.monospace = monospace;
+ return dto;
+ }
- print( e );
- break;
- }
- case "textarea": {
- String name = e.getAttributeValue( "name" );
- final String rows = e.getAttributeValue( "rows" );
- final String cols = e.getAttributeValue( "cols" );
+ private Map< Object, Object > getStylePropertiesLowerCase( final Element base ) {
+ final String n = base.getName().toLowerCase();
- m_out.print( "[{FormTextarea" );
+ // "font-weight: bold; font-style: italic;"
+ String style = base.getAttributeValue( "style" );
+ if( style == null ) {
+ style = "";
+ }
- if ( name != null ) {
- if ( name.startsWith( "nbf_" ) ) {
- name = name.substring( 4, name.length() );
- }
- m_out.print( " name='" + name + "'" );
- }
- if ( rows != null ) {
- m_out.print( " rows='" + rows + "'" );
- }
- if ( cols != null ) {
- m_out.print( " cols='" + cols + "'" );
- }
+ if( n.equals( "p" ) || n.equals( "div" ) ) {
+ final String align = base.getAttributeValue( "align" );
+ if( align != null ) {
+ // only add the value of the align attribute if the text-align style didn't already exist.
+ if( !style.contains( "text-align" ) ) {
+ style += ";text-align:" + align + ";";
+ }
+ }
+ }
- m_out.print( "}]" );
- print( e );
- break;
- }
- case "select": {
- String name = e.getAttributeValue( "name" );
+ if( n.equals( "font" ) ) {
+ final String color = base.getAttributeValue( "color" );
+ final String face = base.getAttributeValue( "face" );
+ final String size = base.getAttributeValue( "size" );
+ if( color != null ) {
+ style = style + "color:" + color + ";";
+ }
+ if( face != null ) {
+ style = style + "font-family:" + face + ";";
+ }
+ if( size != null ) {
+ switch ( size ) {
+ case "1": style += "font-size:xx-small;"; break;
+ case "2": style += "font-size:x-small;"; break;
+ case "3": style += "font-size:small;"; break;
+ case "4": style += "font-size:medium;"; break;
+ case "5": style += "font-size:large;"; break;
+ case "6": style += "font-size:x-large;"; break;
+ case "7": style += "font-size:xx-large;"; break;
+ }
+ }
+ }
- m_out.print( "[{FormSelect" );
+ if( style.equals( "" ) ) {
+ return null;
+ }
- if ( name != null ) {
- if ( name.startsWith( "nbf_" ) ) {
- name = name.substring( 4, name.length() );
- }
- m_out.print( " name='" + name + "'" );
- }
+ final Map< Object, Object > m = new LinkedHashMap<>();
+ Arrays.stream( style.toLowerCase().split( ";" ) )
+ .filter( StringUtils::isNotEmpty )
+ .forEach( prop -> m.put( prop.split( ":" )[ 0 ].trim(), prop.split( ":" )[ 1 ].trim() ) );
+ return m;
+ }
- m_out.print( " value='" );
- print( e );
- m_out.print( "'}]" );
- break;
- }
- case "option": {
- // If this <option> element isn't the second child element within the parent <select>
- // element, then we need to print a semicolon as a separator. (The first child element
- // is expected to be a newline character which is at index of 0).
- if ( base.indexOf( e ) != 1 ) {
- m_out.print( ";" );
- }
+ private String propsToStyleString( final Map< Object, Object > styleProps ) {
+ final StringBuilder style = new StringBuilder();
+ for( final Map.Entry< Object, Object > entry : styleProps.entrySet() ) {
+ style.append( " " ).append( entry.getKey() ).append( ": " ).append( entry.getValue() ).append( ";" );
+ }
+ return style.toString();
+ }
- final Attribute selected = e.getAttribute( "selected" );
- if ( selected != null ) {
- m_out.print( "*" );
- }
+ void decorateMarkupForElementWith( final ElementDecoratorData dto ) throws JDOMException {
+ decorateMarkupForCssClass( dto );
+ }
- final String value = e.getAttributeValue( "value" );
- if ( value != null ) {
- m_out.print( value );
- } else {
- print( e );
- }
- break;
- }
- default:
- print( e );
- break;
- }
+ void decorateMarkupForCssClass( final ElementDecoratorData dto ) throws JDOMException {
+ if( dto.cssClass != null && !dto.ignoredCssClass ) {
+ if ( dto.htmlBase.equals( "div" ) ) {
+ out.print( "\n%%" + dto.cssClass + " \n" );
+ } else if ( dto.htmlBase.equals( "span" ) ) {
+ out.print( "%%" + dto.cssClass + " " );
}
- else
- {
- print( c );
+ }
+ decorateMarkupForBold( dto );
+ if( dto.cssClass != null && !dto.ignoredCssClass ) {
+ if ( dto.htmlBase.equals( "div" ) ) {
+ out.print( "\n/%\n" );
+ } else if ( dto.htmlBase.equals( "span" ) ) {
+ out.print( "/%" );
}
}
}
- private void printImage(final Element base )
- {
- Element child = getXPathElement( base, "TBODY/TR/TD/*" );
- if( child == null )
- {
- child = base;
+ void decorateMarkupForBold( final ElementDecoratorData dto ) throws JDOMException {
+ if( dto.bold ) {
+ out.print( "__" );
}
- final Element img;
- final String href;
- final Map<Object,Object> map = new ForgetNullValuesLinkedHashMap<>();
- if( child.getName().equals( "A" ) )
- {
- img = child.getChild( "IMG" );
- href = child.getAttributeValue( "href" );
+ decorateMarkupForItalic( dto );
+ if( dto.bold ) {
+ out.print( "__" );
}
- else
- {
- img = child;
- href = null;
+ }
+
+ void decorateMarkupForItalic( final ElementDecoratorData dto ) throws JDOMException {
+ if( dto.italic ) {
+ out.print( "''" );
}
- if( img == null )
- {
- return;
+ decorateMarkupForMonospace( dto );
+ if( dto.italic ) {
+ out.print( "''" );
}
- final String src = trimLink( img.getAttributeValue( "src" ) );
- if( src == null )
- {
- return;
+ }
+
+ void decorateMarkupForMonospace( final ElementDecoratorData dto ) throws JDOMException {
+ if( dto.monospace ) {
+ out.print( "{{{" );
+ preStack.push( "{{{" );
+ }
+ decorateMarkupForCssSpecial( dto );
+ if( dto.monospace ) {
+ preStack.pop();
+ out.print( "}}}" );
+ }
+ }
+
+ void decorateMarkupForCssSpecial( final ElementDecoratorData dto ) throws JDOMException {
+ if( dto.cssSpecial != null ) {
+ if ( dto.htmlBase.equals( "div" ) ) {
+ out.print( "\n%%(" + dto.cssSpecial + " )\n" );
+ } else {
+ out.print( "%%(" + dto.cssSpecial + " )" );
+ }
+ }
+ translateChildren( dto.base );
+ if( dto.cssSpecial != null ) {
+ if ( dto.htmlBase.equals( "div" ) ) {
+ out.print( "\n/%\n" );
+ } else {
+ out.print( "/%" );
+ }
}
- map.put( "align", base.getAttributeValue( "align" ) );
- map.put( "height", img.getAttributeValue( "height" ) );
- map.put( "width", img.getAttributeValue( "width" ) );
- map.put( "alt", img.getAttributeValue( "alt" ) );
- map.put( "caption", emptyToNull( ( Element )XPathFactory.instance().compile( "CAPTION" ).evaluateFirst( base ) ) );
- map.put( "link", href );
- map.put( "border", img.getAttributeValue( "border" ) );
- map.put( "style", base.getAttributeValue( "style" ) );
- if( map.size() > 0 )
- {
- m_out.print( "[{Image src='" + src + "'" );
- for(final Iterator i = map.entrySet().iterator(); i.hasNext(); )
- {
- final Map.Entry entry = (Map.Entry)i.next();
- if( !entry.getValue().equals( "" ) )
- {
- m_out.print( " " + entry.getKey() + "='" + entry.getValue() + "'" );
+ }
+
+ private void translateChildren( final Element base ) throws JDOMException {
+ for( final Content c : base.getContent() ) {
+ if ( c instanceof Element ) {
+ final Element e = ( Element )c;
+ final String n = e.getName().toLowerCase();
+ switch( n ) {
+ case "h1": decorateMarkupForH1( e ); break;
+ case "h2": decorateMarkupForH2( e ); break;
+ case "h3": decorateMarkupForH3( e ); break;
+ case "h4": decorateMarkupForH4( e ); break;
+ case "p": decorateMarkupForP( e ); break;
+ case "br": decorateMarkupForBR( base, e ); break;
+ case "hr": decorateMarkupForHR( e ); break;
+ case "table": decorateMarkupForTable( e ); break;
+ case "tr": decorateMarkupForTR( e ); break;
+ case "td": decorateMarkupForTD( e ); break;
+ case "th": decorateMarkupForTH( e ); break;
+ case "a": decorateMarkupForA( e ); break;
+ case "b":
+ case "strong": decorateMarkupForStrong( e ); break;
+ case "i":
+ case "em":
+ case "address": decorateMarkupForEM( e ); break;
+ case "u": decorateMarkupForUnderline( e ); break;
+ case "strike": decorateMarkupForStrike( e ); break;
+ case "sup": decorateMarkupForSup( e ); break;
+ case "sub": decorateMarkupForSub( e ); break;
+ case "dl": decorateMarkupForDL( e ); break;
+ case "dt": decorateMarkupForDT( e ); break;
+ case "dd": decorateMarkupForDD( e ); break;
+ case "ul": decorateMarkupForUL( e ); break;
+ case "ol": decorateMarkupForOL( e ); break;
+ case "li": decorateMarkupForLI( base, e ); break;
+ case "pre": decorateMarkupForPre( e ); break;
+ case "code":
+ case "tt": decorateMarkupForCode( e ); break;
+ case "img": decorateMarkupForImg( e ); break;
+ case "form": decorateMarkupForForm( e ); break;
+ case "input": decorateMarkupForInput( e ); break;
+ case "textarea": decorateMarkupForTextarea( e ); break;
+ case "select": decorateMarkupForSelect( e ); break;
+ case "option": decorateMarkupForOption( base, e ); break;
+ default: translate( e ); break;
}
+ } else {
+ translate( c );
}
- m_out.print( "}]" );
}
- else
- {
- m_out.print( "[" + src + "]" );
+ }
+
+ void decorateMarkupForH1( final Element e ) throws JDOMException {
+ out.print( "\n!!! " );
+ translate( e );
+ out.println();
+ }
+
+ void decorateMarkupForH2( final Element e ) throws JDOMException {
+ out.print( "\n!!! " );
+ translate( e );
+ out.println();
+ }
+
+ void decorateMarkupForH3( final Element e ) throws JDOMException {
+ out.print( "\n!! " );
+ translate( e );
+ out.println();
+ }
+
+ void decorateMarkupForH4( final Element e ) throws JDOMException {
+ out.print( "\n! " );
+ translate( e );
+ out.println();
+ }
+
+ void decorateMarkupForP( final Element e ) throws JDOMException {
+ if ( e.getContentSize() != 0 ) { // we don't want to print empty elements: <p></p>
+ out.println();
+ translate( e );
+ out.println();
}
}
- Element getXPathElement( final Element base, final String expression ) {
- final List< ? > nodes = XPathFactory.instance().compile( expression ).evaluate( base );
- if( nodes == null || nodes.size() == 0 ) {
- return null;
+ void decorateMarkupForHR( final Element e ) throws JDOMException {
+ out.println();
+ decorateMarkupForString( "----" );
+ translate( e );
+ out.println();
+ }
+
+ void decorateMarkupForBR( final Element base, final Element e ) throws JDOMException {
+ if ( !preStack.isEmpty() ) {
+ out.println();
} else {
- return ( Element )nodes.get( 0 );
+ final String parentElementName = base.getName().toLowerCase();
+
+ // To beautify the generated wiki markup, we print a newline character after a linebreak.
+ // It's only safe to do this when the parent element is a <p> or <div>; when the parent
+ // element is a table cell or list item, a newline character would break the markup.
+ // We also check that this isn't being done inside a plugin body.
+ if ( parentElementName.matches( "p|div" ) && !base.getText().matches( "(?s).*\\[\\{.*\\}\\].*" ) ) {
+ out.print( " \\\\\n" );
+ } else {
+ out.print( " \\\\" );
+ }
}
+ translate( e );
}
- private String emptyToNull( final Element e ) {
- if( e == null ) {
- return null;
+ void decorateMarkupForTable( final Element e ) throws JDOMException {
+ if ( !outTrimmer.isCurrentlyOnLineBegin() ) {
+ out.println();
}
- final String s = e.getText();
- return s == null ? null : ( s.replaceAll( "\\s", "" ).isEmpty() ? null : s );
+ translate( e );
}
- private String propsToStyleString( final Map< Object, Object > styleProps ) {
- final StringBuilder style = new StringBuilder();
- for(final Iterator< Map.Entry< Object, Object > > i = styleProps.entrySet().iterator(); i.hasNext(); ) {
- final Map.Entry< Object, Object > entry = i.next();
- style.append( " " ).append( entry.getKey() ).append( ": " ).append( entry.getValue() ).append( ";" );
+ void decorateMarkupForTR( final Element e ) throws JDOMException {
+ translate( e );
+ out.println();
+ }
+
+ void decorateMarkupForTD( final Element e ) throws JDOMException {
+ out.print( "| " );
+ translate( e );
+ if( preStack.isEmpty() ) {
+ decorateMarkupForString( " " );
}
- return style.toString();
}
- private boolean isIgnorableWikiMarkupLink( final Element a ) {
- final String ref = a.getAttributeValue( "href" );
- final String clazz = a.getAttributeValue( "class" );
- return ( ref != null && ref.startsWith( m_config.getPageInfoJsp() ) )
- || ( clazz != null && clazz.trim().equalsIgnoreCase( m_config.getOutlink() ) );
+ void decorateMarkupForTH( final Element e ) throws JDOMException {
+ out.print( "|| " );
+ translate( e );
+ if( preStack.isEmpty() ) {
+ decorateMarkupForString( " " );
+ }
+ }
+
+ void decorateMarkupForA( final Element e ) throws JDOMException {
+ if( isNotIgnorableWikiMarkupLink( e ) ) {
+ if( e.getChild( "IMG" ) != null ) {
+ translateImage( e );
+ } else {
+ String ref = e.getAttributeValue( "href" );
+ if ( ref == null ) {
+ if ( isUndefinedPageLink( e ) ) {
+ out.print( "[" );
+ translate( e );
+ out.print( "]" );
+ } else {
+ translate( e );
+ }
+ } else {
+ ref = trimLink( ref );
+ if ( ref != null ) {
+ if ( ref.startsWith( "#" ) ) { // This is a link to a footnote.
+ // convert "#ref-PageName-1" to just "1"
+ final String href = ref.replaceFirst( "#ref-.+-(\\d+)", "$1" );
+
+ // remove the brackets around "[1]"
+ final String textValue = e.getValue().substring( 1, ( e.getValue().length() - 1 ) );
+
+ if ( href.equals( textValue ) ) { // handles the simplest case. Example: [1]
+ translate( e );
+ } else { // handles the case where the link text is different from the href. Example: [something|1]
+ out.print( "[" + textValue + "|" + href + "]" );
+ }
+ } else {
+ final Map< String, String > augmentedWikiLinkAttributes = getAugmentedWikiLinkAttributes( e );
+
+ out.print( "[" );
+ translate( e );
+ if ( !e.getTextTrim().equalsIgnoreCase( ref ) ) {
+ out.print( "|" );
+ decorateMarkupForString( ref );
+
+ if ( !augmentedWikiLinkAttributes.isEmpty() ) {
+ out.print( "|" );
+
+ final String augmentedWikiLink = augmentedWikiLinkMapToString( augmentedWikiLinkAttributes );
+ out.print( augmentedWikiLink );
+ }
+ } else if ( !augmentedWikiLinkAttributes.isEmpty() ) {
+ // If the ref has the same value as the text and also if there
+ // are attributes, then just print: [ref|ref|attributes] .
+ out.print( "|" + ref + "|" );
+ final String augmentedWikiLink = augmentedWikiLinkMapToString( augmentedWikiLinkAttributes );
+ out.print( augmentedWikiLink );
+ }
+
+ out.print( "]" );
+ }
+ }
+ }
+ }
+ }
}
/**
* Checks if the link points to an undefined page.
*/
- private boolean isUndefinedPageLink(final Element a)
- {
+ private boolean isUndefinedPageLink( final Element a ) {
final String classVal = a.getAttributeValue( "class" );
-
return "createpage".equals( classVal );
}
/**
* Returns a Map containing the valid augmented wiki link attributes.
*/
- private Map<String,String> getAugmentedWikiLinkAttributes(final Element a )
- {
- final Map<String,String> attributesMap = new HashMap<>();
-
- final String id = a.getAttributeValue( "id" );
- if( id != null && !id.equals( "" ) )
- {
- attributesMap.put( "id", id.replaceAll( "'", "\"" ) );
- }
-
+ private Map< String, String > getAugmentedWikiLinkAttributes( final Element a ) {
+ final Map< String, String > attributesMap = new HashMap<>();
final String cssClass = a.getAttributeValue( "class" );
- if( cssClass != null && !cssClass.equals( "" )
- && !cssClass.matches( "wikipage|createpage|external|interwiki|attachment" ) )
- {
+ if( StringUtils.isNotEmpty( cssClass )
+ && !cssClass.matches( "wikipage|createpage|external|interwiki|attachment" ) ) {
attributesMap.put( "class", cssClass.replaceAll( "'", "\"" ) );
}
+ addAttributeIfPresent( a, attributesMap, "accesskey" );
+ addAttributeIfPresent( a, attributesMap, "charset" );
+ addAttributeIfPresent( a, attributesMap, "dir" );
+ addAttributeIfPresent( a, attributesMap, "hreflang" );
+ addAttributeIfPresent( a, attributesMap, "id" );
+ addAttributeIfPresent( a, attributesMap, "lang" );
+ addAttributeIfPresent( a, attributesMap, "rel" );
+ addAttributeIfPresent( a, attributesMap, "rev" );
+ addAttributeIfPresent( a, attributesMap, "style" );
+ addAttributeIfPresent( a, attributesMap, "tabindex" );
+ addAttributeIfPresent( a, attributesMap, "target" );
+ addAttributeIfPresent( a, attributesMap, "title" );
+ addAttributeIfPresent( a, attributesMap, "type" );
+ return attributesMap;
+ }
- final String style = a.getAttributeValue( "style" );
- if( style != null && !style.equals( "" ) )
- {
- attributesMap.put( "style", style.replaceAll( "'", "\"" ) );
+ private void addAttributeIfPresent( final Element a, final Map< String, String > attributesMap, final String attribute ) {
+ final String attr = a.getAttributeValue( attribute );
+ if( StringUtils.isNotEmpty( attr ) ) {
+ attributesMap.put( attribute, attr.replaceAll( "'", "\"" ) );
}
+ }
- final String title = a.getAttributeValue( "title" );
- if( title != null && !title.equals( "" ) )
- {
- attributesMap.put( "title", title.replaceAll( "'", "\"" ) );
- }
+ /**
+ * Converts the entries in the map to a string for use in a wiki link.
+ */
+ private String augmentedWikiLinkMapToString( final Map< String, String > attributesMap ) {
+ final StringBuilder sb = new StringBuilder();
+ for( final Map.Entry< String, String > entry : attributesMap.entrySet() ) {
+ final String attributeName = entry.getKey();
+ final String attributeValue = entry.getValue();
- final String lang = a.getAttributeValue( "lang" );
- if( lang != null && !lang.equals( "" ) )
- {
- attributesMap.put( "lang", lang.replaceAll( "'", "\"" ) );
+ sb.append( " " ).append( attributeName ).append( "='" ).append( attributeValue ).append( "'" );
}
- final String dir = a.getAttributeValue( "dir" );
- if( dir != null && !dir.equals( "" ) )
- {
- attributesMap.put( "dir", dir.replaceAll( "'", "\"" ) );
- }
+ return sb.toString().trim();
+ }
- final String charset = a.getAttributeValue( "charset" );
- if( charset != null && !charset.equals("") )
- {
- attributesMap.put( "charset", charset.replaceAll( "'", "\"" ) );
- }
+ void decorateMarkupForStrong( final Element e ) throws JDOMException {
+ out.print( "__" );
+ translate( e );
+ out.print( "__" );
+ }
- final String type = a.getAttributeValue( "type" );
- if( type != null && !type.equals( "" ) )
- {
- attributesMap.put( "type", type.replaceAll( "'", "\"" ) );
- }
+ void decorateMarkupForEM( final Element e ) throws JDOMException {
+ out.print( "''" );
+ translate( e );
+ out.print( "''" );
+ }
+
+ void decorateMarkupForUnderline( final Element e ) throws JDOMException {
+ out.print( "%%( text-decoration:underline; )" );
+ translate( e );
+ out.print( "/%" );
+ }
+
+ void decorateMarkupForStrike( final Element e ) throws JDOMException {
+ out.print( "%%strike " );
+ translate( e );
+ out.print( "/%" );
+ // NOTE: don't print a space before or after the double percents because that can break words into two.
+ // For example: %%(color:red)ABC%%%%(color:green)DEF%% is different from %%(color:red)ABC%% %%(color:green)DEF%%
+ }
+
+ void decorateMarkupForSup( final Element e ) throws JDOMException {
+ out.print( "%%sup " );
+ translate( e );
+ out.print( "/%" );
+ }
+
+ void decorateMarkupForSub( final Element e ) throws JDOMException {
+ out.print( "%%sub " );
+ translate( e );
+ out.print( "/%" );
+ }
+
+ void decorateMarkupForDL( final Element e ) throws JDOMException {
+ out.print( "\n" );
+ translate( e );
+
+ // print a newline after the definition list. If we don't,
+ // it may cause problems for the subsequent element.
+ out.print( "\n" );
+ }
+
+ void decorateMarkupForDT( final Element e ) throws JDOMException {
+ out.print( ";" );
+ translate( e );
+ }
+
+ void decorateMarkupForDD( final Element e ) throws JDOMException {
+ out.print( ":" );
+ translate( e );
+ }
+
+ void decorateMarkupForUL( final Element e ) throws JDOMException {
+ out.println();
+ liStack.push( "*" );
+ translate( e );
+ liStack.pop();
+ }
+
+ void decorateMarkupForOL( final Element e ) throws JDOMException {
+ out.println();
+ liStack.push( "#" );
+ translate( e );
+ liStack.pop();
+ }
- final String hreflang = a.getAttributeValue( "hreflang" );
- if( hreflang != null && !hreflang.equals( "" ) )
- {
- attributesMap.put( "hreflang", hreflang.replaceAll( "'", "\"" ) );
+ void decorateMarkupForLI( final Element base, final Element e ) throws JDOMException {
+ out.print( String.join( "", liStack ) + " " );
+ translate( e );
+
+ // The following line assumes that the XHTML has been "pretty-printed"
+ // (newlines separate child elements from their parents).
+ final boolean lastListItem = base.indexOf( e ) == ( base.getContentSize() - 2 );
+ final boolean sublistItem = liStack.size() > 1;
+
+ // only print a newline if this <li> element is not the last item within a sublist.
+ if ( !sublistItem || !lastListItem ) {
+ out.println();
}
+ }
+
+ void decorateMarkupForPre( final Element e ) throws JDOMException {
+ out.print( "\n{{{" ); // start JSPWiki "code blocks" on its own line
+
+ preStack.push( "\n{{{" );
+ translate( e );
+ preStack.pop();
- final String rel = a.getAttributeValue( "rel" );
- if( rel != null && !rel.equals( "" ) )
- {
- attributesMap.put( "rel", rel.replaceAll( "'", "\"" ) );
+ // print a newline after the closing braces to avoid breaking any subsequent wiki markup that follows.
+ out.print( "}}}\n" );
+ }
+
+ void decorateMarkupForCode( final Element e ) throws JDOMException {
+ out.print( "{{" );
+ preStack.push( "{{" );
+ translate( e );
+ preStack.pop();
+ out.print( "}}" );
+ // NOTE: don't print a newline after the closing brackets because if the Text is inside
+ // a table or list, it would break it if there was a subsequent row or list item.
+ }
+
+ void decorateMarkupForImg( final Element e ) {
+ if( isNotIgnorableWikiMarkupLink( e ) ) {
+ out.print( "[" );
+ decorateMarkupForString( trimLink( e.getAttributeValue( "src" ) ) );
+ out.print( "]" );
}
+ }
- final String rev = a.getAttributeValue( "rev" );
- if( rev != null && !rev.equals( "" ) )
- {
- attributesMap.put( "rev", rev.replaceAll( "'", "\"" ) );
+ void decorateMarkupForForm( final Element e ) throws JDOMException {
+ // remove the hidden input where name="formname" since a new one will be generated again when the xhtml is rendered.
+ final Element formName = XmlUtil.getXPathElement( e, "INPUT[@name='formname']" );
+ if ( formName != null ) {
+ formName.detach();
}
- final String accesskey = a.getAttributeValue( "accesskey" );
- if( accesskey != null && !accesskey.equals( "" ) )
- {
- attributesMap.put( "accesskey", accesskey.replaceAll( "'", "\"" ) );
+ final String name = e.getAttributeValue( "name" );
+
+ out.print( "\n[{FormOpen" );
+
+ if( name != null ) {
+ out.print( " form='" + name + "'" );
}
- final String tabindex = a.getAttributeValue( "tabindex" );
- if( tabindex != null && !tabindex.equals( "" ) )
- {
- attributesMap.put( "tabindex", tabindex.replaceAll( "'", "\"" ) );
+ out.print( "}]\n" );
+
+ translate( e );
+ out.print( "\n[{FormClose}]\n" );
+ }
+
+ void decorateMarkupForInput( final Element e ) throws JDOMException {
+ final String type = e.getAttributeValue( "type" );
+ String name = e.getAttributeValue( "name" );
+ final String value = e.getAttributeValue( "value" );
+ final String checked = e.getAttributeValue( "checked" );
+
+ out.print( "[{FormInput" );
+
+ if ( type != null ) {
+ out.print( " type='" + type + "'" );
+ }
+ if ( name != null ) {
+ // remove the "nbf_" that was prepended since new one will be generated again when the xhtml is rendered.
+ if ( name.startsWith( "nbf_" ) ) {
+ name = name.substring( 4 );
+ }
+ out.print( " name='" + name + "'" );
+ }
+ if ( value != null && !value.equals( "" ) ) {
+ out.print( " value='" + value + "'" );
}
+ if ( checked != null ) {
+ out.print( " checked='" + checked + "'" );
+ }
+
+ out.print( "}]" );
+
+ translate( e );
+ }
+
+ void decorateMarkupForTextarea( final Element e ) throws JDOMException {
+ String name = e.getAttributeValue( "name" );
+ final String rows = e.getAttributeValue( "rows" );
+ final String cols = e.getAttributeValue( "cols" );
- final String target = a.getAttributeValue( "target" );
- if( target != null && !target.equals( "" ) )
- {
- attributesMap.put( "target", target.replaceAll( "'", "\"" ) );
+ out.print( "[{FormTextarea" );
+
+ if ( name != null ) {
+ if ( name.startsWith( "nbf_" ) ) {
+ name = name.substring( 4 );
+ }
+ out.print( " name='" + name + "'" );
+ }
+ if ( rows != null ) {
+ out.print( " rows='" + rows + "'" );
+ }
+ if ( cols != null ) {
+ out.print( " cols='" + cols + "'" );
}
- return attributesMap;
+ out.print( "}]" );
+ translate( e );
}
- /**
- * Converts the entries in the map to a string for use in a wiki link.
- */
- private String augmentedWikiLinkMapToString(final Map attributesMap )
- {
- final StringBuilder sb = new StringBuilder();
+ void decorateMarkupForSelect( final Element e ) throws JDOMException {
+ String name = e.getAttributeValue( "name" );
- for (final Iterator itr = attributesMap.entrySet().iterator(); itr.hasNext(); )
- {
- final Map.Entry entry = (Map.Entry)itr.next();
- final String attributeName = (String)entry.getKey();
- final String attributeValue = (String)entry.getValue();
+ out.print( "[{FormSelect" );
- sb.append( " " + attributeName + "='" + attributeValue + "'" );
+ if ( name != null ) {
+ if ( name.startsWith( "nbf_" ) ) {
+ name = name.substring( 4 );
+ }
+ out.print( " name='" + name + "'" );
}
- return sb.toString().trim();
+ out.print( " value='" );
+ translate( e );
+ out.print( "'}]" );
}
- private Map< Object, Object > getStylePropertiesLowerCase(final Element base ) throws IOException
- {
- final String n = base.getName().toLowerCase();
+ void decorateMarkupForOption( final Element base, final Element e ) throws JDOMException {
+ // If this <option> element isn't the second child element within the parent <select>
+ // element, then we need to print a semicolon as a separator. (The first child element
+ // is expected to be a newline character which is at index of 0).
+ if ( base.indexOf( e ) != 1 ) {
+ out.print( ";" );
+ }
- //"font-weight: bold; font-style: italic;"
- String style = base.getAttributeValue( "style" );
- if( style == null )
- {
- style = "";
+ final Attribute selected = e.getAttribute( "selected" );
+ if ( selected != null ) {
+ out.print( "*" );
}
- if( n.equals( "p" ) || n.equals( "div" ) )
- {
- final String align = base.getAttributeValue( "align" );
- if( align != null )
- {
- // only add the value of the align attribute if the text-align style didn't already exist.
- if( style.indexOf( "text-align" ) == -1 )
- {
- style = style + ";text-align:" + align + ";";
- }
- }
+ final String value = e.getAttributeValue( "value" );
+ if ( value != null ) {
+ out.print( value );
+ } else {
+ translate( e );
}
+ }
+ private void translateImage( final Element base ) {
+ Element child = XmlUtil.getXPathElement( base, "TBODY/TR/TD/*" );
+ if( child == null ) {
+ child = base;
+ }
+ final Element img;
+ final String href;
+ if( child.getName().equals( "A" ) ) {
+ img = child.getChild( "IMG" );
+ href = child.getAttributeValue( "href" );
+ } else {
+ img = child;
+ href = null;
+ }
+ if( img == null ) {
+ return;
+ }
+ final String src = trimLink( img.getAttributeValue( "src" ) );
+ if( src == null ) {
+ return;
+ }
+ final Map< String, Object > imageAttrs = new LinkedHashMap<>();
+ putIfNotEmpty( imageAttrs, "align", base.getAttributeValue( "align" ) );
+ putIfNotEmpty( imageAttrs, "height", img.getAttributeValue( "height" ) );
+ putIfNotEmpty( imageAttrs, "width", img.getAttributeValue( "width" ) );
+ putIfNotEmpty( imageAttrs, "alt", img.getAttributeValue( "alt" ) );
+ putIfNotEmpty( imageAttrs, "caption", emptyToNull( ( Element )XPathFactory.instance().compile( "CAPTION" ).evaluateFirst( base ) ) );
+ putIfNotEmpty( imageAttrs, "link", href );
+ putIfNotEmpty( imageAttrs, "border", img.getAttributeValue( "border" ) );
+ putIfNotEmpty( imageAttrs, "style", base.getAttributeValue( "style" ) );
+ decorateMarkupforImage( src, imageAttrs );
+ }
- if( n.equals( "font" ) )
- {
- final String color = base.getAttributeValue( "color" );
- final String face = base.getAttributeValue( "face" );
- final String size = base.getAttributeValue( "size" );
- if( color != null )
- {
- style = style + "color:" + color + ";";
- }
- if( face != null )
- {
- style = style + "font-family:" + face + ";";
- }
- if( size != null )
- {
- switch ( size ) {
- case "1":
- style = style + "font-size:xx-small;";
- break;
- case "2":
- style = style + "font-size:x-small;";
- break;
- case "3":
- style = style + "font-size:small;";
- break;
- case "4":
- style = style + "font-size:medium;";
- break;
- case "5":
- style = style + "font-size:large;";
- break;
- case "6":
- style = style + "font-size:x-large;";
- break;
- case "7":
- style = style + "font-size:xx-large;";
- break;
- }
- }
+ private void putIfNotEmpty( final Map< String, Object > map, final String key, final Object value ) {
+ if( value != null ) {
+ map.put( key, value );
}
+ }
- if( style.equals( "" ) )
- {
+ private String emptyToNull( final Element e ) {
+ if( e == null ) {
return null;
}
+ final String s = e.getText();
+ return s == null ? null : ( s.replaceAll( "\\s", "" ).isEmpty() ? null : s );
+ }
- style = style.replace( ';', '\n' ).toLowerCase();
- final LinkedHashMap< Object, Object > m = new LinkedHashMap<>();
- new PersistentMapDecorator( m ).load( new ByteArrayInputStream( style.getBytes() ) );
- return m;
+ void decorateMarkupforImage( final String src, final Map< String, Object > imageAttrs ) {
+ if( imageAttrs.isEmpty() ) {
+ out.print( "[" + src + "]" );
+ } else {
+ out.print( "[{Image src='" + src + "'" );
+ for( final Map.Entry< String, Object > objectObjectEntry : imageAttrs.entrySet() ) {
+ if ( !objectObjectEntry.getValue().equals( "" ) ) {
+ out.print( " " + objectObjectEntry.getKey() + "='" + objectObjectEntry.getValue() + "'" );
+ }
+ }
+ out.print( "}]" );
+ }
+ }
+
+ private boolean isNotIgnorableWikiMarkupLink( final Element a ) {
+ final String ref = a.getAttributeValue( "href" );
+ final String clazz = a.getAttributeValue( "class" );
+ return ( ref == null || !ref.startsWith( config.getPageInfoJsp() ) )
+ && ( clazz == null || !clazz.trim().equalsIgnoreCase( config.getOutlink() ) );
}
- private String trimLink( String ref )
- {
- if( ref == null )
- {
+ private String trimLink( String ref ) {
+ if( ref == null ) {
return null;
}
- try
- {
+ try {
ref = URLDecoder.decode( ref, StandardCharsets.UTF_8.name() );
ref = ref.trim();
- if( ref.startsWith( m_config.getAttachPage() ) )
- {
- ref = ref.substring( m_config.getAttachPage().length() );
+ if( ref.startsWith( config.getAttachPage() ) ) {
+ ref = ref.substring( config.getAttachPage().length() );
}
- if( ref.startsWith( m_config.getWikiJspPage() ) )
- {
- ref = ref.substring( m_config.getWikiJspPage().length() );
+ if( ref.startsWith( config.getWikiJspPage() ) ) {
+ ref = ref.substring( config.getWikiJspPage().length() );
// Handle links with section anchors.
// For example, we need to translate the html string "TargetPage#section-TargetPage-Heading2"
// to this wiki string "TargetPage#Heading2".
ref = ref.replaceFirst( ".+#section-(.+)-(.+)", "$1#$2" );
}
- if( ref.startsWith( m_config.getEditJspPage() ) )
- {
- ref = ref.substring( m_config.getEditJspPage().length() );
+ if( ref.startsWith( config.getEditJspPage() ) ) {
+ ref = ref.substring( config.getEditJspPage().length() );
}
- if( m_config.getPageName() != null )
- {
- if( ref.startsWith( m_config.getPageName() ) )
- {
- ref = ref.substring( m_config.getPageName().length() );
+ if( config.getPageName() != null ) {
+ if( ref.startsWith( config.getPageName() ) ) {
+ ref = ref.substring( config.getPageName().length() );
}
}
- }
- catch ( final UnsupportedEncodingException e )
- {
+ } catch ( final UnsupportedEncodingException e ) {
// Shouldn't happen...
}
return ref;
}
- // FIXME: These should probably be better used with java.util.Stack
-
- private static class LiStack
- {
+ private class PreStack extends Stack< String > {
- private StringBuffer m_li = new StringBuffer();
-
- public void push(final String c )
- {
- m_li.append( c );
+ public String push( final String item ) {
+ final String push = super.push( item );
+ outTrimmer.setWhitespaceTrimMode( isEmpty() );
+ return push;
}
- public void pop()
- {
- m_li = m_li.deleteCharAt( m_li.length()-1 );
- // m_li = m_li.substring( 0, m_li.length() - 1 );
- }
-
- @Override
- public String toString()
- {
- return m_li.toString();
+ public String pop() {
+ final String pop = super.pop();
+ outTrimmer.setWhitespaceTrimMode( isEmpty() );
+ return pop;
}
}
- private class PreStack
- {
-
- private int m_pre;
-
- public boolean isPreMode()
- {
- return m_pre > 0;
- }
-
- public void push()
- {
- m_pre++;
- m_outTimmer.setWhitespaceTrimMode( !isPreMode() );
- }
-
- public void pop()
- {
- m_pre--;
- m_outTimmer.setWhitespaceTrimMode( !isPreMode() );
- }
-
+ static class ElementDecoratorData {
+ Element base;
+ String htmlBase;
+ String cssClass;
+ String cssSpecial;
+ boolean monospace;
+ boolean bold;
+ boolean italic;
+ boolean ignoredCssClass;
}
}
diff --git a/jspwiki-util/src/main/java/org/apache/wiki/util/XmlUtil.java b/jspwiki-util/src/main/java/org/apache/wiki/util/XmlUtil.java
index 3da8bf7..1b48cb6 100644
--- a/jspwiki-util/src/main/java/org/apache/wiki/util/XmlUtil.java
+++ b/jspwiki-util/src/main/java/org/apache/wiki/util/XmlUtil.java
@@ -134,4 +134,13 @@ public final class XmlUtil {
return sb.toString();
}
+ public static Element getXPathElement( final Element base, final String expression ) {
+ final List< ? > nodes = XPathFactory.instance().compile( expression ).evaluate( base );
+ if( nodes == null || nodes.size() == 0 ) {
+ return null;
+ } else {
+ return ( Element )nodes.get( 0 );
+ }
+ }
+
}
\ No newline at end of file
diff --git a/jspwiki-util/src/test/java/org/apache/wiki/util/XmlUtilTest.java b/jspwiki-util/src/test/java/org/apache/wiki/util/XmlUtilTest.java
index 68aa485..dc955ff 100644
--- a/jspwiki-util/src/test/java/org/apache/wiki/util/XmlUtilTest.java
+++ b/jspwiki-util/src/test/java/org/apache/wiki/util/XmlUtilTest.java
@@ -97,4 +97,12 @@ public class XmlUtilTest {
}
}
+ @Test
+ public void testGetXPathElement() {
+ final Element base = XmlUtil.parse( "ini/jspwiki_module.xml", "/modules" ).get( 0 );
+ Assertions.assertNull( XmlUtil.getXPathElement( base, "folter" ) );
+ final Element element = XmlUtil.getXPathElement( base, "filter" );
+ Assertions.assertEquals( "org.apache.wiki.filters.SpamFilter", element.getAttributeValue( "class" ) );
+ }
+
}