/*
*
* * This program is free software; you can redistribute it and/or modify it under the
* * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* * Foundation.
* *
* * You should have received a copy of the GNU Lesser General Public License along with this
* * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* * or from the Free Software Foundation, Inc.,
* * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
* *
* * This program 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 Lesser General Public License for more details.
* *
* * Copyright (c) 2006 - 2009 Pentaho Corporation.. All rights reserved.
*
*/
package org.pentaho.reporting.engine.classic.core.layout.process.text;
import org.pentaho.reporting.engine.classic.core.ReportAttributeMap;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderNode;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderableComplexText;
import org.pentaho.reporting.engine.classic.core.style.StyleSheet;
import org.pentaho.reporting.engine.classic.core.style.TextDirection;
import org.pentaho.reporting.engine.classic.core.util.InstanceID;
import org.pentaho.reporting.libraries.base.util.ArgumentNullException;
import java.awt.font.TextAttribute;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
public class RichTextSpec {
public static class StyledChunk {
private int start;
private int end;
private RenderNode originatingTextNode;
private String text;
private Map<AttributedCharacterIterator.Attribute, Object> attributes;
private ReportAttributeMap<Object> originalAttributes;
private StyleSheet styleSheet;
private InstanceID instanceID;
public StyledChunk( final int start, final int end, final RenderNode originatingTextNode,
final Map<AttributedCharacterIterator.Attribute, Object> attributes,
final ReportAttributeMap<Object> originalAttributes, final StyleSheet styleSheet, final InstanceID instanceID,
final String text ) {
ArgumentNullException.validate( "originatingTextNode", originatingTextNode );
ArgumentNullException.validate( "attributes", attributes );
ArgumentNullException.validate( "text", text );
ArgumentNullException.validate( "originalAttributes", originalAttributes );
ArgumentNullException.validate( "styleSheet", styleSheet );
ArgumentNullException.validate( "instanceID", instanceID );
this.instanceID = instanceID;
this.start = start;
this.end = end;
this.originatingTextNode = originatingTextNode;
this.attributes = Collections.unmodifiableMap( attributes );
this.originalAttributes = originalAttributes;
this.styleSheet = styleSheet;
this.text = text;
}
public Map<AttributedCharacterIterator.Attribute, Object> getAttributes() {
return attributes;
}
public ReportAttributeMap<Object> getOriginalAttributes() {
return originalAttributes;
}
public StyleSheet getStyleSheet() {
return styleSheet;
}
public String getText() {
return text;
}
public int getStart() {
return start;
}
public int getEnd() {
return end;
}
public RenderNode getOriginatingTextNode() {
return originatingTextNode;
}
public boolean equals( final Object o ) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final StyledChunk that = (StyledChunk) o;
if ( instanceID != that.instanceID ) {
return false;
}
if ( end != that.end ) {
return false;
}
if ( start != that.start ) {
return false;
}
if ( !attributes.equals( that.attributes ) ) {
return false;
}
if ( !originatingTextNode.equals( that.originatingTextNode ) ) {
return false;
}
if ( !text.equals( that.text ) ) {
return false;
}
return true;
}
public InstanceID getInstanceID() {
return instanceID;
}
public int hashCode() {
int result = start;
result = 31 * result + end;
result = 31 * result + instanceID.hashCode();
result = 31 * result + originatingTextNode.hashCode();
result = 31 * result + text.hashCode();
result = 31 * result + attributes.hashCode();
return result;
}
}
private String text;
private AttributedString attributedString;
private List<StyledChunk> styleChunks;
private TextDirection runDirection;
public RichTextSpec( final String text, final TextDirection runDirection, final List<StyledChunk> styleChunks ) {
ArgumentNullException.validate( "text", text );
ArgumentNullException.validate( "styleChunks", styleChunks );
ArgumentNullException.validate( "runDirection", runDirection );
if ( styleChunks.isEmpty() ) {
throw new IllegalStateException( "Need at least one style chunk" );
}
if ( text.length() == 0 ) {
throw new IllegalStateException( "Text must not be empty." );
}
this.runDirection = runDirection;
this.text = text;
this.styleChunks = Collections.unmodifiableList( new ArrayList<StyledChunk>( styleChunks ) );
this.attributedString = createText();
}
private AttributedString createText() {
AttributedString str = new AttributedString( text );
str.addAttribute( TextAttribute.RUN_DIRECTION, TextDirection.RTL.equals( runDirection ) );
int startPosition = 0;
for ( final StyledChunk chunk : styleChunks ) {
int length = chunk.getText().length();
int endIndex = startPosition + length;
str.addAttributes( chunk.getAttributes(), startPosition, endIndex );
startPosition = endIndex;
}
return str;
}
public String getText() {
return text;
}
public AttributedString getAttributedString() {
return attributedString;
}
public AttributedCharacterIterator createAttributedCharacterIterator() {
return attributedString.getIterator();
}
public List<StyledChunk> getStyleChunks() {
return styleChunks;
}
public RichTextSpec substring( final int start, final int end ) {
List<StyledChunk> clippedChunks = new ArrayList<StyledChunk>();
for ( final StyledChunk chunk : styleChunks ) {
if ( chunk.end <= start ) {
continue;
}
if ( chunk.start >= end ) {
continue;
}
int chunkStart;
int textStart;
if ( chunk.start < start ) {
chunkStart = 0;
textStart = start;
} else {
chunkStart = chunk.start - start;
textStart = chunk.start;
}
int chunkEnd;
int textEnd;
if ( chunk.end < end ) {
chunkEnd = chunk.end - start;
textEnd = chunk.end;
} else {
chunkEnd = end - start;
textEnd = end;
}
String clippedText = text.substring( textStart, textEnd );
clippedChunks.add( new StyledChunk( chunkStart, chunkEnd, chunk.originatingTextNode, chunk.attributes,
chunk.originalAttributes, chunk.styleSheet, chunk.instanceID, clippedText ) );
}
return new RichTextSpec( text.substring( start, end ), runDirection, clippedChunks );
}
public RenderableComplexText create( final RenderBox lineBoxContainer ) {
AttributedCharacterIterator ci = createAttributedCharacterIterator();
return create( lineBoxContainer, ci.getBeginIndex(), ci.getEndIndex() );
}
public RenderableComplexText create( final RenderBox lineBoxContainer, final int start, final int end ) {
return new RenderableComplexText( lineBoxContainer.getStyleSheet(), lineBoxContainer.getInstanceId(),
lineBoxContainer.getElementType(), lineBoxContainer.getAttributes(), this.substring( start, end ) );
}
public boolean equals( final Object o ) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
final RichTextSpec that = (RichTextSpec) o;
if ( !styleChunks.equals( that.styleChunks ) ) {
return false;
}
if ( !text.equals( that.text ) ) {
return false;
}
return true;
}
public int hashCode() {
int result = text.hashCode();
result = 31 * result + styleChunks.hashCode();
return result;
}
public int length() {
return text.length();
}
public boolean isEmpty() {
return styleChunks.isEmpty();
}
}