blob: 392722b4196c6a58be753e8c99ef4a9a4cdb8dfe [file] [log] [blame]
/*
* @(#)$Id$
*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 2001, Sun
* Microsystems., http://www.sun.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* @author Jacek Ambroziak
* @author Santiago Pericas-Geertsen
*
*/
package org.apache.xalan.xsltc.dom;
import java.util.Vector;
import org.apache.xalan.xsltc.DOM;
import org.apache.xalan.xsltc.Translet;
import org.apache.xalan.xsltc.NodeIterator;
import org.apache.xalan.xsltc.dom.Axis;
public abstract class NodeCounter implements Axis {
public static final int END = DOM.NULL;
protected int _node = END;
protected int _nodeType = DOM.FIRST_TYPE - 1;
protected int _value = Integer.MIN_VALUE;
public final DOM _document;
public final NodeIterator _iterator;
public final Translet _translet;
protected String _format;
protected String _lang;
protected String _letterValue;
protected String _groupSep;
protected int _groupSize;
private static String[] Thousands =
{"", "m", "mm", "mmm" };
private static String[] Hundreds =
{"", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm"};
private static String[] Tens =
{"", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc"};
private static String[] Ones =
{"", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix"};
protected NodeCounter(Translet translet,
DOM document, NodeIterator iterator) {
_translet = translet;
_document = document;
_iterator = iterator;
}
/**
* Set the start node for this counter. The same <tt>NodeCounter</tt>
* object can be used multiple times by resetting the starting node.
*/
abstract public NodeCounter setStartNode(int node);
/**
* If the user specified a value attribute, use this instead of
* counting nodes.
*/
public NodeCounter setValue(int value) {
_value = value;
return this;
}
/**
* Sets formatting fields before calling formatNumbers().
*/
protected void setFormatting(String format, String lang, String letterValue,
String groupSep, String groupSize) {
_lang = lang;
_format = format;
_groupSep = groupSep;
_letterValue = letterValue;
try {
_groupSize = Integer.parseInt(groupSize);
}
catch (NumberFormatException e) {
_groupSize = 0;
}
}
/**
* Sets formatting fields to their default values.
*/
public NodeCounter setDefaultFormatting() {
setFormatting("1", "en", "alphabetic", null, null);
return this;
}
/**
* Returns the position of <tt>node</tt> according to the level and
* the from and count patterns.
*/
abstract public String getCounter();
/**
* Returns the position of <tt>node</tt> according to the level and
* the from and count patterns. This position is converted into a
* string based on the arguments passed.
*/
public String getCounter(String format, String lang, String letterValue,
String groupSep, String groupSize) {
setFormatting(format, lang, letterValue, groupSep, groupSize);
return getCounter();
}
/**
* Returns true if <tt>node</tt> matches the count pattern. By
* default a node matches the count patterns if it is of the
* same type as the starting node.
*/
public boolean matchesCount(int node) {
return _nodeType == _document.getType(node);
}
/**
* Returns true if <tt>node</tt> matches the from pattern. By default,
* no node matches the from pattern.
*/
public boolean matchesFrom(int node) {
return false;
}
/**
* Format a single value according to the format parameters.
*/
protected String formatNumbers(int value) {
return formatNumbers(new int[] { value });
}
/**
* Format a sequence of values according to the format paramaters
* set by calling setFormatting().
*/
protected String formatNumbers(int[] values) {
final int nValues = values.length;
final int length = _format.length();
final Vector separToks = new Vector();
final Vector formatToks = new Vector();
boolean isFirst = true;
boolean separFirst = true;
boolean isEmpty = true;
for (int t=0; t<nValues; t++)
if (values[t] != Integer.MIN_VALUE) isEmpty = false;
if (isEmpty) return("");
/*
* Tokenize the format string into alphanumeric and non-alphanumeric
* tokens as described in M. Kay page 241.
*/
for (int j = 0, i = 0; i < length;) {
char c = _format.charAt(i);
for (j = i; Character.isLetterOrDigit(c);) {
if (++i == length) break;
c = _format.charAt(i);
}
if (i > j) {
if (isFirst) {
separToks.addElement(".");
isFirst = separFirst = false;
}
formatToks.addElement(_format.substring(j, i));
}
if (i == length) break;
c = _format.charAt(i);
for (j = i; !Character.isLetterOrDigit(c);) {
if (++i == length) break;
c = _format.charAt(i);
isFirst = false;
}
if (i > j) {
separToks.addElement(_format.substring(j, i));
}
}
/*
* Format the output string using the values array and the tokens
* just parsed.
*/
isFirst = true;
int t = 0, n = 0;
final StringBuffer buffer = new StringBuffer();
if (separFirst) {
buffer.append((String) separToks.elementAt(t));
}
while (n < nValues) {
final int value = values[n];
if (value != Integer.MIN_VALUE) {
if (!isFirst) {
buffer.append((String) separToks.elementAt(t));
}
formatValue(value, (String) formatToks.elementAt(t++), buffer);
if (t == formatToks.size()) {
t--;
}
isFirst = false;
}
n++;
}
final int nSepars = separToks.size();
if (nSepars > formatToks.size()) {
buffer.append((String) separToks.elementAt(nSepars - 1));
}
return buffer.toString();
}
/**
* Format a single value based on the appropriate formatting token.
* This method is based on saxon (Michael Kay) and only implements
* lang="en".
*/
private void formatValue(int value, String format, StringBuffer buffer) {
char c = format.charAt(0);
if (Character.isDigit(c)) {
char zero = (char)(c - Character.getNumericValue(c));
StringBuffer temp = buffer;
if (_groupSize > 0) {
temp = new StringBuffer();
}
String s = "";
int n = value;
while (n > 0) {
s = (char) ((int) zero + (n % 10)) + s;
n = n / 10;
}
for (int i = 0; i < format.length() - s.length(); i++) {
temp.append(zero);
}
temp.append(s);
if (_groupSize > 0) {
for (int i = 0; i < temp.length(); i++) {
if (i != 0 && ((temp.length() - i) % _groupSize) == 0) {
buffer.append(_groupSep);
}
buffer.append(temp.charAt(i));
}
}
}
else if (c == 'i' && !_letterValue.equals("alphabetic")) {
buffer.append(romanValue(value));
}
else if (c == 'I' && !_letterValue.equals("alphabetic")) {
buffer.append(romanValue(value).toUpperCase());
}
else {
int min = (int) c;
int max = (int) c;
while (Character.isLetterOrDigit((char) (max+1))) {
max++;
}
buffer.append(alphaValue(value, min, max));
}
}
private String alphaValue(int value, int min, int max) {
if (value <= 0) {
return "" + value;
}
int range = max - min + 1;
char last = (char)(((value-1) % range) + min);
if (value > range) {
return alphaValue((value-1) / range, min, max) + last;
}
else {
return "" + last;
}
}
private String romanValue(int n) {
if (n <= 0 || n > 4000) {
return "" + n;
}
return
Thousands[n / 1000] +
Hundreds[(n / 100) % 10] +
Tens[(n/10) % 10] +
Ones[n % 10];
}
}