com.greenpepper.html.HtmlExample Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2006 Pyxis Technologies inc.
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA,
* or see the FSF site: http://www.fsf.org.
*/
package com.greenpepper.html;
import com.greenpepper.AbstractExample;
import com.greenpepper.Example;
import com.greenpepper.Text;
import com.greenpepper.annotation.Annotation;
import com.greenpepper.annotation.Status;
import com.greenpepper.util.CollectionUtil;
import com.greenpepper.shaded.org.apache.commons.lang3.StringUtils;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.greenpepper.shaded.org.apache.commons.lang3.StringUtils.startsWithIgnoreCase;
/**
* HtmlExample class.
*
* @author oaouattara
* @version $Id: $Id
*/
public class HtmlExample extends AbstractExample implements Text
{
private String lead;
private String startTag;
private String text;
private String endTag;
private String tail;
private String tag;
private List childTags;
private Example sibling;
private Example child;
private final Map styles = new LinkedHashMap();
private final Set cssClasses = new TreeSet();
private Status status;
/**
* Constructor for HtmlExample.
*
* @param lead a {@link java.lang.String} object.
* @param startTag a {@link java.lang.String} object.
* @param tag a {@link java.lang.String} object.
* @param content a {@link java.lang.String} object.
* @param endTag a {@link java.lang.String} object.
* @param tail a {@link java.lang.String} object.
* @param childTags a {@link java.util.List} object.
* @param child a {@link com.greenpepper.Example} object.
* @param sibling a {@link com.greenpepper.Example} object.
*/
public HtmlExample( String lead,
String startTag,
String tag,
String content,
String endTag,
String tail,
List childTags,
Example child,
Example sibling )
{
this.tag = tag;
this.tail = tail;
this.endTag = endTag;
this.startTag = startTag;
this.lead = lead;
this.text = content;
this.childTags = childTags;
this.child = child;
this.sibling = sibling;
}
/**
* firstChild.
*
* @return a {@link com.greenpepper.Example} object.
*/
public Example firstChild()
{
return child;
}
/**
* nextSibling.
*
* @return a {@link com.greenpepper.Example} object.
*/
public Example nextSibling()
{
return sibling;
}
/** {@inheritDoc} */
public void print( PrintWriter out )
{
out.write( lead );
printStartTag( out );
if (child != null) child.print( out );
else out.write( text );
out.write( endTag );
if (sibling != null) sibling.print( out );
else out.write( tail );
}
// Should we return null or empty string when we have children?
/**
* getContent.
*
* @return a {@link java.lang.String} object.
*/
public String getContent()
{
String content = normalizeLineBreaks( text );
content = removeNonLineBreaks( content );
content = condenseWhitespace( content );
content = decodeMarkup( content );
return content.trim();
}
private String firstPattern(String tags) {
Scanner scanner = new Scanner(tags);
try {
return scanner.next();
} finally {
scanner.close();
}
}
private HtmlExample createSpecification( String tag, List moreTags )
{
return new HtmlExample( "", start( tag ), tag, "", end( tag ), "", moreTags, null, null );
}
private String start( String tag )
{
return String.format( "<%s>", tag );
}
private String end( String tag )
{
return String.format( "%s>", tag );
}
/**
* addChild.
*
* @return a {@link com.greenpepper.Example} object.
*/
public Example addChild()
{
if (hasChild())
{
return child.addSibling();
}
else
{
if (childTags.isEmpty()) throw new IllegalStateException( "No child tag" );
List moreTags = new ArrayList( childTags );
String childTag = firstPattern( CollectionUtil.shift( moreTags ) );
child = createSpecification( childTag, moreTags );
return child;
}
}
/**
* addSibling.
*
* @return a {@link com.greenpepper.Example} object.
*/
public Example addSibling()
{
if (hasSibling())
{
return sibling.addSibling();
}
else
{
sibling = createSpecification( tag, childTags );
return sibling;
}
}
private String condenseWhitespace( String s )
{
// non breaking space is decimal character 160 (hex A0)
return s.replace( (char) 160, ' ' ).replaceAll( " ", " " ).replaceAll( "\\s+", " " );
}
private String decodeMarkup( String s )
{
return new HtmlEntitiesDecoder( s ).decode();
}
/** {@inheritDoc} */
public void annotate( Annotation annotation )
{
annotation.writeDown( this );
}
private String normalizeLineBreaks( String s )
{
return s.replaceAll( "<\\s*br(\\s+.*?)*>", "
" );
}
private void printStartTag( PrintWriter out )
{
boolean styleProcessed = false;
boolean classProcessed = false;
boolean statusProcessed = false;
StringWriter temp = new StringWriter();
temp.write("<");
// Tokenize the tag
Pattern tagAttibutePattern = Pattern.compile("[\\w\\-]+(=(\"[^\"]*\"|'[^']*'))?");
ArrayList tagsInfo = new ArrayList();
Matcher matcher = tagAttibutePattern.matcher(startTag);
while (matcher.find()) {
String attribute = matcher.group();
if (attribute.endsWith("\"") || attribute.endsWith("'")) { // a value was set
char closingChar = attribute.charAt(attribute.length() - 1);
String attributeNotClosed = attribute.substring(0, attribute.length() - 1);
if (startsWithIgnoreCase(attribute, "style") && !styles.isEmpty()) {
appendStyles(temp, attributeNotClosed, closingChar);
styleProcessed = true;
} else if (startsWithIgnoreCase(attribute, "class") && !cssClasses.isEmpty()) {
appendClasses(temp, attributeNotClosed, closingChar);
classProcessed = true;
} else if (startsWithIgnoreCase(attribute, "data-gp-status") && status != null) {
temp.write( String.format( "data-gp-status=\"%s\"", status.getGpStatusTag()));
statusProcessed = true;
} else {
temp.write(attribute);
}
} else {
temp.write(attribute);
}
temp.write(" ");
}
if (!styles.isEmpty() && !styleProcessed)
temp.write( String.format( "style=\"%s\" ", inlineStyle(false) ) );
if (!cssClasses.isEmpty() && !classProcessed)
temp.write( String.format( "class=\"%s\" ", StringUtils.join(cssClasses, " ")));
if (status != null && !statusProcessed) {
temp.write( String.format( "data-gp-status=\"%s\" ", status.getGpStatusTag()));
}
temp.flush();
String tagStr = temp.toString();
out.write(tagStr.trim());
out.write( ">" );
}
private void appendClasses(StringWriter out, String attributeNotClosed, char closing) {
out.write(attributeNotClosed);
out.write(StringUtils.join(cssClasses, " "));
out.write(closing);
}
private void appendStyles(StringWriter out, String attributeNotClosed, char closing) {
out.write(attributeNotClosed);
if (!attributeNotClosed.trim().endsWith(";")) {
out.write(";");
}
out.write(" " + inlineStyle(true));
out.write(closing);
}
private String inlineStyle(boolean important)
{
StringBuilder style = new StringBuilder();
for (String attr : styles.keySet())
{
style.append( String.format( "%s: %s%s; ", attr, styles.get( attr ),
important ? " !important" : "" ) );
}
return style.toString().trim();
}
private String removeNonLineBreaks( String s )
{
return s.replaceAll( "<" + notBr() + ">", "" );
}
private String notBr()
{
return String.format( "(?!%s).*?", "br/>");
}
/** {@inheritDoc} */
@Override
public void setCssClasses(String... classes) {
Collections.addAll(cssClasses, classes);
}
/** {@inheritDoc} */
@Override
public String[] getCssClasses() {
return cssClasses.toArray(new String[cssClasses.size()]);
}
/** {@inheritDoc} */
public void setStyle( String property, String value )
{
styles.put( property, value );
}
/** {@inheritDoc} */
public String getStyle( String property)
{
return styles.get( property);
}
/** {@inheritDoc} */
@Override
public void setStatus(Status value)
{
status = value;
}
/** {@inheritDoc} */
public void setContent( String content )
{
text = content;
}
}