/*
* Copyright 2001-2008 Geert Bevin <gbevin[remove] at uwyn dot com>
* Licensed under the Apache License, Version 2.0 (the "License")
* $Id: InternalValue.java 3918 2008-04-14 17:35:35Z gbevin $
*/
package com.uwyn.rife.template;
import com.uwyn.rife.template.exceptions.BlockUnknownException;
import com.uwyn.rife.template.exceptions.CircularContructionException;
import com.uwyn.rife.template.exceptions.TemplateException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.text.NumberFormat;
/**
* An anonymous value inside a template, which is not referenced anywhere in
* the template, but can be used to produce intermediate strings using the
* template engine. To obtain an <code>InternalValue</code>, you should use
* {@link Template#createInternalValue()}.
*
* @author Keith Lea (keith[remove] at cs dot oswego dot edu)
* @author Geert Bevin (gbevin[remove] at uwyn dot com)
* @version $Revision: 3918 $
* @since 1.0
*/
public class InternalValue
{
private AbstractTemplate mTemplate = null;
private ArrayList<CharSequence> mConstruction = new ArrayList<CharSequence>();
private ArrayList<CharSequence> mValueIds = new ArrayList<CharSequence>();
private ArrayList<CharSequence> mValueTags = new ArrayList<CharSequence>();
/**
* Appends the content of a block to this value. The values used by the
* block will be captured when this method is called, so any future
* changes to template values will not affect text which was appended when
* this method is called.
*
* @param blockId the ID of the block whose value should be appended to
* the specified value
* @exception TemplateException if the specified block does not exist in
* the corresponding template
* @since 1.0
*/
public void appendBlock(String blockId)
throws TemplateException
{
if (null == blockId) throw new IllegalArgumentException("blockId can't be null.");
if (0 == blockId.length()) throw new IllegalArgumentException("blockId can't be empty.");
if (!mTemplate.appendBlockInternalForm(blockId, this))
{
throw new BlockUnknownException(blockId);
}
}
/**
* Appends to this value the value of the given internal value.
*
* @param value an internal value
* @since 1.0
*/
public void appendValue(InternalValue value)
{
if (null == value) throw new IllegalArgumentException("value can't be null.");
appendConstructedValue(value);
}
/**
* Appends the result of calling {@link String#valueOf(Object)
* String.valueOf} on the given <code>value</code> to this value in this
* template.
*
* @param value an object
* @since 1.0
*/
public void appendValue(Object value)
{
appendValue(String.valueOf(value));
}
/**
* Appends <code>"true"</code> or <code>"false"</code> to this value,
* depending on the given <code>value</code>.
*
* @param value a boolean value
* @since 1.0
*/
public void appendValue(boolean value)
{
appendValue(String.valueOf(value));
}
/**
* Appends the single specified character to this value.
*
* @param value a character
* @since 1.0
*/
public void appendValue(char value)
{
appendValue(String.valueOf(value));
}
/**
* Appends the given characters to this value.
*
* @param value a string of characters
* @since 1.0
*/
public void appendValue(char[] value)
{
appendValue(String.valueOf(value));
}
/**
* Appends the specified range of the given character string to this
* value. The specified number of bytes from <code>value</code> will be
* used, starting at the character specified by <code>offset</code>.
*
* @param value a character string
* @param offset the index in <code>value</code> of the first character to
* use
* @param count the number of characters to use
* @since 1.0
*/
public void appendValue(char[] value, int offset, int count)
{
appendValue(String.valueOf(value, offset, count));
}
/**
* Appends the given double precision floating point value to this value.
* This method uses the {@linkplain String#valueOf(double) String.valueOf}
* method to print the given value, which probably prints more digits than
* you like. You probably want {@link String#format String.format} or
* {@link NumberFormat} instead.
*
* @param value a floating point value
* @since 1.0
*/
public void appendValue(double value)
{
appendValue(String.valueOf(value));
}
/**
* Appends the given floating point value to this value. This method uses
* the {@linkplain String#valueOf(float) String.valueOf} method to print
* the given value, which probably prints more digits than you like. You
* probably want {@link String#format String.format} or {@link
* NumberFormat} instead.
*
* @param value a floating point value
* @since 1.0
*/
public void appendValue(float value)
{
appendValue(String.valueOf(value));
}
/**
* Appends the given integer to this value.
*
* @param value an integer
* @since 1.0
*/
public void appendValue(int value)
{
appendValue(String.valueOf(value));
}
/**
* Appends the given long to this value.
*
* @param value a long
* @since 1.0
*/
public void appendValue(long value)
{
appendValue(String.valueOf(value));
}
/**
* Appends the given string to this value. The given string cannot be
* null.
*
* @param value a string
* @since 1.0
*/
public void appendValue(String value)
{
if (null == value) throw new IllegalArgumentException("value can't be null.");
appendText(value);
}
/**
* Appends the given character sequence to this value. The given
* character sequence cannot be null.
*
* @param value a character sequence
* @since 1.5
*/
public void appendValue(CharSequence value)
{
if (null == value) throw new IllegalArgumentException("value can't be null.");
appendText(value);
}
InternalValue(AbstractTemplate template)
{
super();
mTemplate = template;
}
InternalValue(AbstractTemplate template, List<CharSequence> deferredContent)
{
super();
mTemplate = template;
if (deferredContent != null)
{
mConstruction.addAll(deferredContent);
}
}
void increasePartsCapacity(int size)
{
mConstruction.ensureCapacity(size + mConstruction.size());
}
void increaseValuesCapacity(int size)
{
mValueIds.ensureCapacity(size + mValueIds.size());
mValueTags.ensureCapacity(size + mValueTags.size());
}
int partsSize()
{
return mConstruction.size();
}
int valuesSize()
{
return mValueIds.size();
}
void appendExternalForm(ExternalValue result)
{
String value_id = null;
String value_tag = null;
int value_count = 0;
for (CharSequence part : mConstruction)
{
// part is a value
if (null == part)
{
value_id = mValueIds.get(value_count).toString();
value_tag = mValueTags.get(value_count).toString();
value_count++;
// check if the template contains content for the value
mTemplate.appendValueExternalForm(value_id, value_tag, result);
}
// part is just text
else
{
result.add(part);
}
}
}
void appendText(CharSequence text)
{
mConstruction.add(text);
}
void appendValueId(String id, String tag)
{
mConstruction.add(null);
mValueIds.add(id);
mValueTags.add(tag);
}
void appendConstructedValue(InternalValue constructedValue)
{
// prevent concurrent modification errors
if (this == constructedValue ||
mValueIds == constructedValue.mValueIds)
{
throw new CircularContructionException();
}
increasePartsCapacity(constructedValue.partsSize());
increaseValuesCapacity(constructedValue.valuesSize());
for (CharSequence charsequence : constructedValue.mConstruction)
{
mConstruction.add(charsequence);
}
for (CharSequence charsequence : constructedValue.mValueIds)
{
mValueIds.add(charsequence);
}
for (CharSequence charsequence : constructedValue.mValueTags)
{
mValueTags.add(charsequence);
}
}
/**
* Returns whether this value contains no cnotent. This method will return
* <code>false</code> for newly created values as well as values for whom
* {@link #clear} has just been called.
*
* @return whether this value has no contents
* @since 1.0
*/
public boolean isEmpty()
{
return 0 == mConstruction.size();
}
/**
* Removes all content from this value.
*/
public void clear()
{
mConstruction = new ArrayList<CharSequence>();
mValueIds = new ArrayList<CharSequence>();
mValueTags = new ArrayList<CharSequence>();
}
public boolean equals(Object object)
{
if (null == object)
{
return false;
}
if (object == this)
{
return true;
}
if (object.getClass() != this.getClass())
{
return false;
}
InternalValue other = (InternalValue)object;
if (this.mConstruction.size() != other.mConstruction.size())
{
return false;
}
Iterator<CharSequence> this_it = null;
Iterator<CharSequence> other_it = null;
CharSequence this_value = null;
CharSequence other_value = null;
this_it = this.mConstruction.iterator();
other_it = other.mConstruction.iterator();
while (this_it.hasNext())
{
if (!other_it.hasNext())
{
return false;
}
this_value = this_it.next();
other_value = other_it.next();
if (null == this_value && null == other_value)
{
continue;
}
if (null == this_value || null == other_value)
{
return false;
}
if (!this_value.equals(other_value))
{
return false;
}
}
CharSequence this_valueid = null;
CharSequence other_valueid = null;
this_it = this.mValueIds.iterator();
other_it = other.mValueIds.iterator();
while (this_it.hasNext())
{
if (!other_it.hasNext())
{
return false;
}
this_valueid = this_it.next();
other_valueid = other_it.next();
if (null == this_valueid && null == other_valueid)
{
continue;
}
if (null == this_valueid || null == other_valueid)
{
return false;
}
if (!this_valueid.equals(other_valueid))
{
return false;
}
}
CharSequence this_valuetag = null;
CharSequence other_valuetag = null;
this_it = this.mValueTags.iterator();
other_it = other.mValueTags.iterator();
while (this_it.hasNext())
{
if (!other_it.hasNext())
{
return false;
}
this_valuetag = this_it.next();
other_valuetag = other_it.next();
if (null == this_valuetag && null == other_valuetag)
{
continue;
}
if (null == this_valuetag || null == other_valuetag)
{
return false;
}
if (!this_valuetag.equals(other_valuetag))
{
return false;
}
}
return true;
}
}