aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrkidd <rkidd@138bc75d-0d04-0410-961f-82ee72b054a4>2006-12-05 00:34:00 +0000
committerrkidd <rkidd@138bc75d-0d04-0410-961f-82ee72b054a4>2006-12-05 00:34:00 +0000
commitc9f22723241aab413a5801f6f95d0088a321168e (patch)
treedc48c41660205a0c3b3df6f2d5f7fca5687a9f94
parent65b6ffdfbb63c937e9bd52be7d9df16c1f6dd082 (diff)
2006-12-04 Robert Kidd <rkidd@crhc.uiuc.edu>ia64-improvements
* libjava/java/util/logging/LogManager.java, libjava/classpath/tools/.cvsignore, libjava/classpath/java/text/DateFormatSymbols.java, libjava/classpath/java/text/SimpleDateFormat.java, libjava/classpath/java/text/NumberFormat.java, libjava/classpath/java/text/RuleBasedCollator.java, libjava/classpath/java/text/Bidi.java, libjava/classpath/java/text/DecimalFormat.java, libjava/classpath/java/text/Collator.java, libjava/classpath/java/text/class-dependencies.conf, libjava/classpath/java/text/AttributedString.java, libjava/classpath/java/text/AttributedCharacterIterator.java: Manually merging changes to mainline through revision 119296. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/ia64-improvements@119527 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--ChangeLog.ia64-improvements16
-rw-r--r--libjava/classpath/java/text/AttributedCharacterIterator.java6
-rw-r--r--libjava/classpath/java/text/AttributedString.java11
-rw-r--r--libjava/classpath/java/text/Bidi.java936
-rw-r--r--libjava/classpath/java/text/Collator.java13
-rw-r--r--libjava/classpath/java/text/DateFormatSymbols.java16
-rw-r--r--libjava/classpath/java/text/DecimalFormat.java6
-rw-r--r--libjava/classpath/java/text/NumberFormat.java2
-rw-r--r--libjava/classpath/java/text/RuleBasedCollator.java3
-rw-r--r--libjava/classpath/java/text/SimpleDateFormat.java5
-rw-r--r--libjava/classpath/java/text/class-dependencies.conf220
-rw-r--r--libjava/classpath/tools/.cvsignore6
-rw-r--r--libjava/java/util/logging/LogManager.java981
13 files changed, 2189 insertions, 32 deletions
diff --git a/ChangeLog.ia64-improvements b/ChangeLog.ia64-improvements
index c91aa70b1ee..93a567263d3 100644
--- a/ChangeLog.ia64-improvements
+++ b/ChangeLog.ia64-improvements
@@ -1,5 +1,21 @@
2006-12-04 Robert Kidd <rkidd@crhc.uiuc.edu>
+ * libjava/java/util/logging/LogManager.java,
+ libjava/classpath/tools/.cvsignore,
+ libjava/classpath/java/text/DateFormatSymbols.java,
+ libjava/classpath/java/text/SimpleDateFormat.java,
+ libjava/classpath/java/text/NumberFormat.java,
+ libjava/classpath/java/text/RuleBasedCollator.java,
+ libjava/classpath/java/text/Bidi.java,
+ libjava/classpath/java/text/DecimalFormat.java,
+ libjava/classpath/java/text/Collator.java,
+ libjava/classpath/java/text/class-dependencies.conf,
+ libjava/classpath/java/text/AttributedString.java,
+ libjava/classpath/java/text/AttributedCharacterIterator.java:
+ Manually merging changes to mainline through revision 119296.
+
+2006-12-04 Robert Kidd <rkidd@crhc.uiuc.edu>
+
* Merging changes to mainline through revision 119296
2006-04-12 Robert Kidd <rkidd@crhc.uiuc.edu>
diff --git a/libjava/classpath/java/text/AttributedCharacterIterator.java b/libjava/classpath/java/text/AttributedCharacterIterator.java
index 8413c203130..37c7450b2c8 100644
--- a/libjava/classpath/java/text/AttributedCharacterIterator.java
+++ b/libjava/classpath/java/text/AttributedCharacterIterator.java
@@ -127,13 +127,13 @@ public interface AttributedCharacterIterator extends CharacterIterator
*/
protected Object readResolve() throws InvalidObjectException
{
- if (this.equals (READING))
+ if (getName().equals(READING.getName()))
return READING;
- if (this.equals (LANGUAGE))
+ if (getName().equals(LANGUAGE.getName()))
return LANGUAGE;
- if (this.equals (INPUT_METHOD_SEGMENT))
+ if (getName().equals(INPUT_METHOD_SEGMENT.getName()))
return INPUT_METHOD_SEGMENT;
throw new InvalidObjectException ("Can't resolve Attribute: "
diff --git a/libjava/classpath/java/text/AttributedString.java b/libjava/classpath/java/text/AttributedString.java
index c751ab43cf8..497b557fcf3 100644
--- a/libjava/classpath/java/text/AttributedString.java
+++ b/libjava/classpath/java/text/AttributedString.java
@@ -221,16 +221,13 @@ public class AttributedString
// If the attribute run starts before the beginning index, we
// need to junk it if it is an Annotation.
Object attrib_obj = aci.getAttribute(attrib);
- if (rs < begin)
+ rs -= begin;
+ if (rs < 0)
{
if (attrib_obj instanceof Annotation)
continue;
- rs = begin;
- }
- else
- {
- rs -= begin;
+ rs = 0;
}
// Create a map object. Yes this will only contain one attribute
@@ -243,7 +240,7 @@ public class AttributedString
c = aci.next();
}
- while(c != CharacterIterator.DONE);
+ while( aci.getIndex() < end );
attribs = new AttributeRange[accum.size()];
attribs = (AttributeRange[]) accum.toArray(attribs);
diff --git a/libjava/classpath/java/text/Bidi.java b/libjava/classpath/java/text/Bidi.java
index 57b9a88dfa7..05b10f52d6a 100644
--- a/libjava/classpath/java/text/Bidi.java
+++ b/libjava/classpath/java/text/Bidi.java
@@ -1,5 +1,5 @@
/* Bidi.java -- Bidirectional Algorithm implementation
- Copyright (C) 2005 Free Software Foundation, Inc.
+ Copyright (C) 2005, 2006 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -38,18 +38,940 @@ exception statement from your version. */
package java.text;
+import java.awt.font.NumericShaper;
+import java.awt.font.TextAttribute;
+import java.util.ArrayList;
+
+
/**
* Bidirectional Algorithm implementation.
*
- * TODO/FIXME Only one method <code>requiresBidi</code> is implemented
- * for now by using <code>Character</code>. The full algorithm is <a
- * href="http://www.unicode.org/unicode/reports/tr9/">Unicode Standard
- * Annex #9: The Bidirectional Algorithm</a>. A full implementation is
- * <a href="http://fribidi.org/">GNU FriBidi</a>.
+ * The full algorithm is
+ * <a href="http://www.unicode.org/unicode/reports/tr9/">Unicode Standard
+ * Annex #9: The Bidirectional Algorithm</a>.
+ *
+ * @since 1.4
*/
-public class Bidi
+public final class Bidi
{
/**
+ * This indicates that a strongly directional character in the text should
+ * set the initial direction, but if no such character is found, then the
+ * initial direction will be left-to-right.
+ */
+ public static final int DIRECTION_DEFAULT_LEFT_TO_RIGHT = -2;
+
+ /**
+ * This indicates that a strongly directional character in the text should
+ * set the initial direction, but if no such character is found, then the
+ * initial direction will be right-to-left.
+ */
+ public static final int DIRECTION_DEFAULT_RIGHT_TO_LEFT = -1;
+
+ /**
+ * This indicates that the initial direction should be left-to-right.
+ */
+ public static final int DIRECTION_LEFT_TO_RIGHT = 0;
+
+ /**
+ * This indicates that the initial direction should be right-to-left.
+ */
+ public static final int DIRECTION_RIGHT_TO_LEFT = 1;
+
+ // Flags used when computing the result.
+ private static final int LTOR = 1 << DIRECTION_LEFT_TO_RIGHT;
+ private static final int RTOL = 1 << DIRECTION_RIGHT_TO_LEFT;
+
+ // The text we are examining, and the starting offset.
+ // If we had a better way to handle createLineBidi, we wouldn't
+ // need this at all -- which for the String case would be an
+ // efficiency win.
+ private char[] text;
+ private int textOffset;
+ // The embeddings corresponding to the text, and the starting offset.
+ private byte[] embeddings;
+ private int embeddingOffset;
+ // The length of the text (and embeddings) to use.
+ private int length;
+ // The flags.
+ private int flags;
+
+ // All instance fields following this point are initialized
+ // during analysis. Fields before this must be set by the constructor.
+
+ // The initial embedding level.
+ private int baseEmbedding;
+ // The type of each character in the text.
+ private byte[] types;
+ // The levels we compute.
+ private byte[] levels;
+
+ // A list of indices where a formatting code was found. These
+ // are indicies into the original text -- not into the text after
+ // the codes have been removed.
+ private ArrayList formatterIndices;
+
+ // Indices of the starts of runs in the text.
+ private int[] runs;
+
+ // A convenience field where we keep track of what kinds of runs
+ // we've seen.
+ private int resultFlags;
+
+ /**
+ * Create a new Bidi object given an attributed character iterator.
+ * This constructor will examine various attributes of the text:
+ * <ul>
+ * <li> {@link TextAttribute#RUN_DIRECTION} is used to determine the
+ * paragraph's base embedding level. This constructor will recognize
+ * either {@link TextAttribute#RUN_DIRECTION_LTR} or
+ * {@link TextAttribute#RUN_DIRECTION_RTL}. If neither is given,
+ * {@link #DIRECTION_DEFAULT_LEFT_TO_RIGHT} is assumed.
+ * </li>
+ *
+ * <li> If {@link TextAttribute#NUMERIC_SHAPING} is seen, then numeric
+ * shaping will be done before the Bidi algorithm is run.
+ * </li>
+ *
+ * <li> If {@link TextAttribute#BIDI_EMBEDDING} is seen on a given
+ * character, then the value of this attribute will be used as an
+ * embedding level override.
+ * </li>
+ * </ul>
+ * @param iter the attributed character iterator to use
+ */
+ public Bidi(AttributedCharacterIterator iter)
+ {
+ // If set, this attribute should be set on all characters.
+ // We don't check this (should we?) but we do assume that we
+ // can simply examine the first character.
+ Object val = iter.getAttribute(TextAttribute.RUN_DIRECTION);
+ if (val == TextAttribute.RUN_DIRECTION_LTR)
+ this.flags = DIRECTION_LEFT_TO_RIGHT;
+ else if (val == TextAttribute.RUN_DIRECTION_RTL)
+ this.flags = DIRECTION_RIGHT_TO_LEFT;
+ else
+ this.flags = DIRECTION_DEFAULT_LEFT_TO_RIGHT;
+
+ // Likewise this attribute should be specified on the whole text.
+ // We read it here and then, if it is set, we apply the numeric shaper
+ // to the text before processing it.
+ NumericShaper shaper = null;
+ val = iter.getAttribute(TextAttribute.NUMERIC_SHAPING);
+ if (val instanceof NumericShaper)
+ shaper = (NumericShaper) val;
+
+ char[] text = new char[iter.getEndIndex() - iter.getBeginIndex()];
+ this.embeddings = new byte[this.text.length];
+ this.embeddingOffset = 0;
+ this.length = text.length;
+ for (int i = 0; i < this.text.length; ++i)
+ {
+ this.text[i] = iter.current();
+
+ val = iter.getAttribute(TextAttribute.BIDI_EMBEDDING);
+ if (val instanceof Integer)
+ {
+ int ival = ((Integer) val).intValue();
+ byte bval;
+ if (ival < -62 || ival > 62)
+ bval = 0;
+ else
+ bval = (byte) ival;
+ this.embeddings[i] = bval;
+ }
+ }
+
+ // Invoke the numeric shaper, if specified.
+ if (shaper != null)
+ shaper.shape(this.text, 0, this.length);
+
+ runBidi();
+ }
+
+ /**
+ * Create a new Bidi object with the indicated text and, possibly, explicit
+ * embedding settings.
+ *
+ * If the embeddings array is null, it is ignored. Otherwise it is taken to
+ * be explicit embedding settings corresponding to the text. Positive values
+ * from 1 to 61 are embedding levels, and negative values from -1 to -61 are
+ * embedding overrides. (FIXME: not at all clear what this really means.)
+ *
+ * @param text the text to use
+ * @param offset the offset of the first character of the text
+ * @param embeddings the explicit embeddings, or null if there are none
+ * @param embedOffset the offset of the first embedding value to use
+ * @param length the length of both the text and the embeddings
+ * @param flags a flag indicating the base embedding direction
+ */
+ public Bidi(char[] text, int offset, byte[] embeddings, int embedOffset,
+ int length, int flags)
+ {
+ if (flags != DIRECTION_DEFAULT_LEFT_TO_RIGHT
+ && flags != DIRECTION_DEFAULT_RIGHT_TO_LEFT
+ && flags != DIRECTION_LEFT_TO_RIGHT
+ && flags != DIRECTION_RIGHT_TO_LEFT)
+ throw new IllegalArgumentException("unrecognized 'flags' argument: "
+ + flags);
+ this.text = text;
+ this.textOffset = offset;
+ this.embeddings = embeddings;
+ this.embeddingOffset = embedOffset;
+ this.length = length;
+ this.flags = flags;
+
+ runBidi();
+ }
+
+ /**
+ * Create a new Bidi object using the contents of the given String
+ * as the text.
+ * @param text the text to use
+ * @param flags a flag indicating the base embedding direction
+ */
+ public Bidi(String text, int flags)
+ {
+ if (flags != DIRECTION_DEFAULT_LEFT_TO_RIGHT
+ && flags != DIRECTION_DEFAULT_RIGHT_TO_LEFT
+ && flags != DIRECTION_LEFT_TO_RIGHT
+ && flags != DIRECTION_RIGHT_TO_LEFT)
+ throw new IllegalArgumentException("unrecognized 'flags' argument: "
+ + flags);
+
+ // This is inefficient, but it isn't clear whether it matters.
+ // If it does we can change our implementation a bit to allow either
+ // a String or a char[].
+ this.text = text.toCharArray();
+ this.textOffset = 0;
+ this.embeddings = null;
+ this.embeddingOffset = 0;
+ this.length = text.length();
+ this.flags = flags;
+
+ runBidi();
+ }
+
+ /**
+ * Implementation function which computes the initial type of
+ * each character in the input.
+ */
+ private void computeTypes()
+ {
+ types = new byte[length];
+ for (int i = 0; i < length; ++i)
+ types[i] = Character.getDirectionality(text[textOffset + i]);
+ }
+
+ /**
+ * An internal function which implements rules P2 and P3.
+ * This computes the base embedding level.
+ * @return the paragraph's base embedding level
+ */
+ private int computeParagraphEmbeddingLevel()
+ {
+ // First check to see if the user supplied a directionality override.
+ if (flags == DIRECTION_LEFT_TO_RIGHT
+ || flags == DIRECTION_RIGHT_TO_LEFT)
+ return flags;
+
+ // This implements rules P2 and P3.
+ // (Note that we don't need P1, as the user supplies
+ // a paragraph.)
+ for (int i = 0; i < length; ++i)
+ {
+ int dir = types[i];
+ if (dir == Character.DIRECTIONALITY_LEFT_TO_RIGHT)
+ return DIRECTION_LEFT_TO_RIGHT;
+ if (dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT
+ || dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT)
+ return DIRECTION_RIGHT_TO_LEFT;
+ }
+ return (flags == DIRECTION_DEFAULT_LEFT_TO_RIGHT
+ ? DIRECTION_LEFT_TO_RIGHT
+ : DIRECTION_RIGHT_TO_LEFT);
+ }
+
+ /**
+ * An internal function which implements rules X1 through X9.
+ * This computes the initial levels for the text, handling
+ * explicit overrides and embeddings.
+ */
+ private void computeExplicitLevels()
+ {
+ levels = new byte[length];
+ byte currentEmbedding = (byte) baseEmbedding;
+ // The directional override is a Character directionality
+ // constant. -1 means there is no override.
+ byte directionalOverride = -1;
+ // The stack of pushed embeddings, and the stack pointer.
+ // Note that because the direction is inherent in the depth,
+ // and because we have a bit left over in a byte, we can encode
+ // the override, if any, directly in this value on the stack.
+ final int MAX_DEPTH = 62;
+ byte[] embeddingStack = new byte[MAX_DEPTH];
+ int sp = 0;
+
+ for (int i = 0; i < length; ++i)
+ {
+ // If we see an explicit embedding, we use that, even if
+ // the current character is itself a directional override.
+ if (embeddings != null && embeddings[embeddingOffset + i] != 0)
+ {
+ // It isn't at all clear what we're supposed to do here.
+ // What does a negative value really mean?
+ // Should we push on the embedding stack here?
+ currentEmbedding = embeddings[embeddingOffset + i];
+ if (currentEmbedding < 0)
+ {
+ currentEmbedding = (byte) -currentEmbedding;
+ directionalOverride
+ = (((currentEmbedding % 2) == 0)
+ ? Character.DIRECTIONALITY_LEFT_TO_RIGHT
+ : Character.DIRECTIONALITY_RIGHT_TO_LEFT);
+ }
+ else
+ directionalOverride = -1;
+ continue;
+ }
+ // No explicit embedding.
+ boolean isLtoR = false;
+ boolean isSpecial = true;
+ switch (types[i])
+ {
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT_EMBEDDING:
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE:
+ isLtoR = true;
+ // Fall through.
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING:
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE:
+ {
+ byte newEmbedding;
+ if (isLtoR)
+ {
+ // Least greater even.
+ newEmbedding = (byte) ((currentEmbedding & ~1) + 2);
+ }
+ else
+ {
+ // Least greater odd.
+ newEmbedding = (byte) ((currentEmbedding + 1) | 1);
+ }
+ // FIXME: we don't properly handle invalid pushes.
+ if (newEmbedding < MAX_DEPTH)
+ {
+ // The new level is valid. Push the old value.
+ // See above for a comment on the encoding here.
+ if (directionalOverride != -1)
+ currentEmbedding |= Byte.MIN_VALUE;
+ embeddingStack[sp++] = currentEmbedding;
+ currentEmbedding = newEmbedding;
+ if (types[i] == Character.DIRECTIONALITY_LEFT_TO_RIGHT_OVERRIDE)
+ directionalOverride = Character.DIRECTIONALITY_LEFT_TO_RIGHT;
+ else if (types[i] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE)
+ directionalOverride = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
+ else
+ directionalOverride = -1;
+ }
+ }
+ break;
+ case Character.DIRECTIONALITY_POP_DIRECTIONAL_FORMAT:
+ {
+ // FIXME: we don't properly handle a pop with a corresponding
+ // invalid push.
+ if (sp == 0)
+ {
+ // We saw a pop without a push. Just ignore it.
+ break;
+ }
+ byte newEmbedding = embeddingStack[--sp];
+ currentEmbedding = (byte) (newEmbedding & 0x7f);
+ if (newEmbedding < 0)
+ directionalOverride
+ = (((newEmbedding & 1) == 0)
+ ? Character.DIRECTIONALITY_LEFT_TO_RIGHT
+ : Character.DIRECTIONALITY_RIGHT_TO_LEFT);
+ else
+ directionalOverride = -1;
+ }
+ break;
+ default:
+ isSpecial = false;
+ break;
+ }
+ levels[i] = currentEmbedding;
+ if (isSpecial)
+ {
+ // Mark this character for removal.
+ if (formatterIndices == null)
+ formatterIndices = new ArrayList();
+ formatterIndices.add(Integer.valueOf(i));
+ }
+ else if (directionalOverride != -1)
+ types[i] = directionalOverride;
+ }
+
+ // Remove the formatting codes and update both the arrays
+ // and 'length'. It would be more efficient not to remove
+ // these codes, but it is also more complicated. Also, the
+ // Unicode algorithm reference does not properly describe
+ // how this is to be done -- from what I can tell, their suggestions
+ // in this area will not yield the correct results.
+ if (formatterIndices == null)
+ return;
+ int output = 0, input = 0;
+ final int size = formatterIndices.size();
+ for (int i = 0; i <= size; ++i)
+ {
+ int nextFmt;
+ if (i == size)
+ nextFmt = length;
+ else
+ nextFmt = ((Integer) formatterIndices.get(i)).intValue();
+ // Non-formatter codes are from 'input' to 'nextFmt'.
+ int len = nextFmt - input;
+ System.arraycopy(levels, input, levels, output, len);
+ System.arraycopy(types, input, types, output, len);
+ output += len;
+ input = nextFmt + 1;
+ }
+ length -= formatterIndices.size();
+ }
+
+ /**
+ * An internal function to compute the boundaries of runs
+ * in the text. It isn't strictly necessary to do this, but
+ * it lets us write some following passes in a less complicated
+ * way. Also it lets us efficiently implement some of the public
+ * methods. A run is simply a sequence of characters at the
+ * same level.
+ */
+ private void computeRuns()
+ {
+ int runCount = 0;
+ int currentEmbedding = baseEmbedding;
+ for (int i = 0; i < length; ++i)
+ {
+ if (levels[i] != currentEmbedding)
+ {
+ currentEmbedding = levels[i];
+ ++runCount;
+ }
+ }
+
+ // This may be called multiple times. If so, and if
+ // the number of runs has not changed, then don't bother
+ // allocating a new array.
+ if (runs == null || runs.length != runCount + 1)
+ runs = new int[runCount + 1];
+ int where = 0;
+ int lastRunStart = 0;
+ currentEmbedding = baseEmbedding;
+ for (int i = 0; i < length; ++i)
+ {
+ if (levels[i] != currentEmbedding)
+ {
+ runs[where++] = lastRunStart;
+ lastRunStart = i;
+ currentEmbedding = levels[i];
+ }
+ }
+ runs[where++] = lastRunStart;
+ }
+
+ /**
+ * An internal method to resolve weak types. This implements
+ * rules W1 through W7.
+ */
+ private void resolveWeakTypes()
+ {
+ final int runCount = getRunCount();
+
+ int previousLevel = baseEmbedding;
+ for (int run = 0; run < runCount; ++run)
+ {
+ int start = getRunStart(run);
+ int end = getRunLimit(run);
+ int level = getRunLevel(run);
+
+ // These are the names used in the Bidi algorithm.
+ byte sor = (((Math.max(previousLevel, level) % 2) == 0)
+ ? Character.DIRECTIONALITY_LEFT_TO_RIGHT
+ : Character.DIRECTIONALITY_RIGHT_TO_LEFT);
+ int nextLevel;
+ if (run == runCount - 1)
+ nextLevel = baseEmbedding;
+ else
+ nextLevel = getRunLevel(run + 1);
+ byte eor = (((Math.max(level, nextLevel) % 2) == 0)
+ ? Character.DIRECTIONALITY_LEFT_TO_RIGHT
+ : Character.DIRECTIONALITY_RIGHT_TO_LEFT);
+
+ byte prevType = sor;
+ byte prevStrongType = sor;
+ for (int i = start; i < end; ++i)
+ {
+ final byte nextType = (i == end - 1) ? eor : types[i + 1];
+
+ // Rule W1: change NSM to the prevailing direction.
+ if (types[i] == Character.DIRECTIONALITY_NONSPACING_MARK)
+ types[i] = prevType;
+ else
+ prevType = types[i];
+
+ // Rule W2: change EN to AN in some cases.
+ if (types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
+ {
+ if (prevStrongType == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
+ types[i] = Character.DIRECTIONALITY_ARABIC_NUMBER;
+ }
+ else if (types[i] == Character.DIRECTIONALITY_LEFT_TO_RIGHT
+ || types[i] == Character.DIRECTIONALITY_RIGHT_TO_LEFT
+ || types[i] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
+ prevStrongType = types[i];
+
+ // Rule W3: change AL to R.
+ if (types[i] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC)
+ types[i] = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
+
+ // Rule W4: handle separators between two numbers.
+ if (prevType == Character.DIRECTIONALITY_EUROPEAN_NUMBER
+ && nextType == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
+ {
+ if (types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR
+ || types[i] == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR)
+ types[i] = nextType;
+ }
+ else if (prevType == Character.DIRECTIONALITY_ARABIC_NUMBER
+ && nextType == Character.DIRECTIONALITY_ARABIC_NUMBER
+ && types[i] == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR)
+ types[i] = nextType;
+
+ // Rule W5: change a sequence of european terminators to
+ // european numbers, if they are adjacent to european numbers.
+ // We also include BN characters in this.
+ if (types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR
+ || types[i] == Character.DIRECTIONALITY_BOUNDARY_NEUTRAL)
+ {
+ if (prevType == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
+ types[i] = prevType;
+ else
+ {
+ // Look ahead to see if there is an EN terminating this
+ // sequence of ETs.
+ int j = i + 1;
+ while (j < end
+ && (types[j] == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR
+ || types[j] == Character.DIRECTIONALITY_BOUNDARY_NEUTRAL))
+ ++j;
+ if (j < end
+ && types[j] == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
+ {
+ // Change them all to EN now.
+ for (int k = i; k < j; ++k)
+ types[k] = Character.DIRECTIONALITY_EUROPEAN_NUMBER;
+ }
+ }
+ }
+
+ // Rule W6: separators and terminators change to ON.
+ // Again we include BN.
+ if (types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR
+ || types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR
+ || types[i] == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR
+ || types[i] == Character.DIRECTIONALITY_BOUNDARY_NEUTRAL)
+ types[i] = Character.DIRECTIONALITY_OTHER_NEUTRALS;
+
+ // Rule W7: change european number types.
+ if (prevStrongType == Character.DIRECTIONALITY_LEFT_TO_RIGHT
+ && types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
+ types[i] = prevStrongType;
+ }
+
+ previousLevel = level;
+ }
+ }
+
+ /**
+ * An internal method to resolve neutral types. This implements
+ * rules N1 and N2.
+ */
+ private void resolveNeutralTypes()
+ {
+ // This implements rules N1 and N2.
+ final int runCount = getRunCount();
+
+ int previousLevel = baseEmbedding;
+ for (int run = 0; run < runCount; ++run)
+ {
+ int start = getRunStart(run);
+ int end = getRunLimit(run);
+ int level = getRunLevel(run);
+
+ byte embeddingDirection
+ = (((level % 2) == 0) ? Character.DIRECTIONALITY_LEFT_TO_RIGHT
+ : Character.DIRECTIONALITY_RIGHT_TO_LEFT);
+ // These are the names used in the Bidi algorithm.
+ byte sor = (((Math.max(previousLevel, level) % 2) == 0)
+ ? Character.DIRECTIONALITY_LEFT_TO_RIGHT
+ : Character.DIRECTIONALITY_RIGHT_TO_LEFT);
+ int nextLevel;
+ if (run == runCount - 1)
+ nextLevel = baseEmbedding;
+ else
+ nextLevel = getRunLevel(run + 1);
+ byte eor = (((Math.max(level, nextLevel) % 2) == 0)
+ ? Character.DIRECTIONALITY_LEFT_TO_RIGHT
+ : Character.DIRECTIONALITY_RIGHT_TO_LEFT);
+
+ byte prevStrong = sor;
+ int neutralStart = -1;
+ for (int i = start; i <= end; ++i)
+ {
+ byte newStrong = -1;
+ byte thisType = i == end ? eor : types[i];
+ switch (thisType)
+ {
+ case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
+ newStrong = Character.DIRECTIONALITY_LEFT_TO_RIGHT;
+ break;
+ case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
+ case Character.DIRECTIONALITY_ARABIC_NUMBER:
+ case Character.DIRECTIONALITY_EUROPEAN_NUMBER:
+ newStrong = Character.DIRECTIONALITY_RIGHT_TO_LEFT;
+ break;
+ case Character.DIRECTIONALITY_BOUNDARY_NEUTRAL:
+ case Character.DIRECTIONALITY_OTHER_NEUTRALS:
+ case Character.DIRECTIONALITY_SEGMENT_SEPARATOR:
+ case Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR:
+ case Character.DIRECTIONALITY_WHITESPACE:
+ if (neutralStart == -1)
+ neutralStart = i;
+ break;
+ }
+ // If we see a strong character, update all the neutrals.
+ if (newStrong != -1)
+ {
+ if (neutralStart != -1)
+ {
+ byte override = (prevStrong == newStrong
+ ? prevStrong
+ : embeddingDirection);
+ for (int j = neutralStart; j < i; ++j)
+ types[j] = override;
+ }
+ prevStrong = newStrong;
+ neutralStart = -1;
+ }
+ }
+
+ previousLevel = level;
+ }
+ }
+
+ /**
+ * An internal method to resolve implicit levels.
+ * This implements rules I1 and I2.
+ */
+ private void resolveImplicitLevels()
+ {
+ // This implements rules I1 and I2.
+ for (int i = 0; i < length; ++i)
+ {
+ if ((levels[i] & 1) == 0)
+ {
+ if (types[i] == Character.DIRECTIONALITY_RIGHT_TO_LEFT)
+ ++levels[i];
+ else if (types[i] == Character.DIRECTIONALITY_ARABIC_NUMBER
+ || types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
+ levels[i] += 2;
+ }
+ else
+ {
+ if (types[i] == Character.DIRECTIONALITY_LEFT_TO_RIGHT
+ || types[i] == Character.DIRECTIONALITY_ARABIC_NUMBER
+ || types[i] == Character.DIRECTIONALITY_EUROPEAN_NUMBER)
+ ++levels[i];
+ }
+
+ // Update the result flags.
+ resultFlags |= 1 << (levels[i] & 1);
+ }
+ // One final update of the result flags, using the base level.
+ resultFlags |= 1 << baseEmbedding;
+ }
+
+ /**
+ * This reinserts the formatting codes that we removed early on.
+ * Actually it does not insert formatting codes per se, but rather
+ * simply inserts new levels at the appropriate locations in the
+ * 'levels' array.
+ */
+ private void reinsertFormattingCodes()
+ {
+ if (formatterIndices == null)
+ return;
+ int input = length;
+ int output = levels.length;
+ // Process from the end as we are copying the array over itself here.
+ for (int index = formatterIndices.size() - 1; index >= 0; --index)
+ {
+ int nextFmt = ((Integer) formatterIndices.get(index)).intValue();
+
+ // nextFmt points to a location in the original array. So,
+ // nextFmt+1 is the target of our copying. output is the location
+ // to which we last copied, thus we can derive the length of the
+ // copy from it.
+ int len = output - nextFmt - 1;
+ output = nextFmt;
+ input -= len;
+ // Note that we no longer need 'types' at this point, so we
+ // only edit 'levels'.
+ if (nextFmt + 1 < levels.length)
+ System.arraycopy(levels, input, levels, nextFmt + 1, len);
+
+ // Now set the level at the reinsertion point.
+ int rightLevel;
+ if (output == levels.length - 1)
+ rightLevel = baseEmbedding;
+ else
+ rightLevel = levels[output + 1];
+ int leftLevel;
+ if (input == 0)
+ leftLevel = baseEmbedding;
+ else
+ leftLevel = levels[input];
+ levels[output] = (byte) Math.max(leftLevel, rightLevel);
+ }
+ length = levels.length;
+ }
+
+ /**
+ * This is the main internal entry point. After a constructor
+ * has initialized the appropriate local state, it will call
+ * this method to do all the work.
+ */
+ private void runBidi()
+ {
+ computeTypes();
+ baseEmbedding = computeParagraphEmbeddingLevel();
+ computeExplicitLevels();
+ computeRuns();
+ resolveWeakTypes();
+ resolveNeutralTypes();
+ resolveImplicitLevels();
+ // We're done with the types. Let the GC clean up.
+ types = null;
+ reinsertFormattingCodes();
+ // After resolving the implicit levels, the number
+ // of runs may have changed.
+ computeRuns();
+ }
+
+ /**
+ * Return true if the paragraph base embedding is left-to-right,
+ * false otherwise.
+ */
+ public boolean baseIsLeftToRight()
+ {
+ return baseEmbedding == DIRECTION_LEFT_TO_RIGHT;
+ }
+
+ /**
+ * Create a new Bidi object for a single line of text, taken
+ * from the text used when creating the current Bidi object.
+ * @param start the index of the first character of the line
+ * @param end the index of the final character of the line
+ * @return a new Bidi object for the indicated line of text
+ */
+ public Bidi createLineBidi(int start, int end)
+ {
+ // This isn't the most efficient implementation possible.
+ // This probably does not matter, so we choose simplicity instead.
+ int level = getLevelAt(start);
+ int flag = (((level % 2) == 0)
+ ? DIRECTION_LEFT_TO_RIGHT
+ : DIRECTION_RIGHT_TO_LEFT);
+ return new Bidi(text, textOffset + start,
+ embeddings, embeddingOffset + start,
+ end - start, flag);
+ }
+
+ /**
+ * Return the base embedding level of the paragraph.
+ */
+ public int getBaseLevel()
+ {
+ return baseEmbedding;
+ }
+
+ /**
+ * Return the length of the paragraph, in characters.
+ */
+ public int getLength()
+ {
+ return length;
+ }
+
+ /**
+ * Return the level at the indicated character. If the
+ * supplied index is less than zero or greater than the length
+ * of the text, then the paragraph's base embedding level will
+ * be returned.
+ * @param offset the character to examine
+ * @return the level of that character
+ */
+ public int getLevelAt(int offset)
+ {
+ if (offset < 0 || offset >= length)
+ return getBaseLevel();
+ return levels[offset];
+ }
+
+ /**
+ * Return the number of runs in the result. A run is
+ * a sequence of characters at the same embedding level.
+ */
+ public int getRunCount()
+ {
+ return runs.length;
+ }
+
+ /**
+ * Return the level of the indicated run.
+ * @param which the run to examine
+ * @return the level of that run
+ */
+ public int getRunLevel(int which)
+ {
+ return levels[runs[which]];
+ }
+
+ /**
+ * Return the index of the character just following the end
+ * of the indicated run.
+ * @param which the run to examine
+ * @return the index of the character after the final character
+ * of the run
+ */
+ public int getRunLimit(int which)
+ {
+ if (which == runs.length - 1)
+ return length;
+ return runs[which + 1];
+ }
+
+ /**
+ * Return the index of the first character in the indicated run.
+ * @param which the run to examine
+ * @return the index of the first character of the run
+ */
+ public int getRunStart(int which)
+ {
+ return runs[which];
+ }
+
+ /**
+ * Return true if the text is entirely left-to-right, and the
+ * base embedding is also left-to-right.
+ */
+ public boolean isLeftToRight()
+ {
+ return resultFlags == LTOR;
+ }
+
+ /**
+ * Return true if the text consists of mixed left-to-right and
+ * right-to-left runs, or if the text consists of one kind of run
+ * which differs from the base embedding direction.
+ */
+ public boolean isMixed()
+ {
+ return resultFlags == (LTOR | RTOL);
+ }
+
+ /**
+ * Return true if the text is entirely right-to-left, and the
+ * base embedding is also right-to-left.
+ */
+ public boolean isRightToLeft()
+ {
+ return resultFlags == RTOL;
+ }
+
+ /**
+ * Return a String describing the internal state of this object.
+ * This is only useful for debugging.
+ */
+ public String toString()
+ {
+ return "Bidi Bidi Bidi I like you, Buck!";
+ }
+
+ /**
+ * Reorder objects according to the levels passed in. This implements
+ * reordering as defined by the Unicode bidirectional layout specification.
+ * The levels are integers from 0 to 62; even numbers represent left-to-right
+ * runs, and odd numbers represent right-to-left runs.
+ *
+ * @param levels the levels associated with each object
+ * @param levelOffset the index of the first level to use
+ * @param objs the objects to reorder according to the levels
+ * @param objOffset the index of the first object to use
+ * @param count the number of objects (and levels) to manipulate
+ */
+ public static void reorderVisually(byte[] levels, int levelOffset,
+ Object[] objs, int objOffset, int count)
+ {
+ // We need a copy of the 'levels' array, as we are going to modify it.
+ // This is unfortunate but difficult to avoid.
+ byte[] levelCopy = new byte[count];
+ // Do this explicitly so we can also find the maximum depth at the
+ // same time.
+ int max = 0;
+ int lowestOdd = 63;
+ for (int i = 0; i < count; ++i)
+ {
+ levelCopy[i] = levels[levelOffset + i];
+ max = Math.max(levelCopy[i], max);
+ if (levelCopy[i] % 2 != 0)
+ lowestOdd = Math.min(lowestOdd, levelCopy[i]);
+ }
+
+ // Reverse the runs starting with the deepest.
+ for (int depth = max; depth >= lowestOdd; --depth)
+ {
+ int start = 0;
+ while (start < count)
+ {
+ // Find the start of a run >= DEPTH.
+ while (start < count && levelCopy[start] < depth)
+ ++start;
+ if (start == count)
+ break;
+ // Find the end of the run.
+ int end = start + 1;
+ while (end < count && levelCopy[end] >= depth)
+ ++end;
+
+ // Reverse this run.
+ for (int i = 0; i < (end - start) / 2; ++i)
+ {
+ byte tmpb = levelCopy[end - i - 1];
+ levelCopy[end - i - 1] = levelCopy[start + i];
+ levelCopy[start + i] = tmpb;
+ Object tmpo = objs[objOffset + end - i - 1];
+ objs[objOffset + end - i - 1] = objs[objOffset + start + i];
+ objs[objOffset + start + i] = tmpo;
+ }
+
+ // Handle the next run.
+ start = end + 1;
+ }
+ }
+ }
+
+ /**
* Returns false if all characters in the text between start and end
* are all left-to-right text. This implementation is just calls
* <code>Character.getDirectionality(char)</code> on all characters
diff --git a/libjava/classpath/java/text/Collator.java b/libjava/classpath/java/text/Collator.java
index 7dea83f0a5a..08739163a64 100644
--- a/libjava/classpath/java/text/Collator.java
+++ b/libjava/classpath/java/text/Collator.java
@@ -38,6 +38,8 @@ exception statement from your version. */
package java.text;
+import gnu.java.locale.LocaleHelper;
+
import java.util.Comparator;
import java.util.Locale;
import java.util.MissingResourceException;
@@ -68,10 +70,6 @@ import java.util.ResourceBundle;
* @author Aaron M. Renn (arenn@urbanophile.com)
* @date March 18, 1999
*/
-/* Written using "Java Class Libraries", 2nd edition, plus online
- * API docs for JDK 1.2 from http://www.javasoft.com.
- * Status: Mostly complete, but parts stubbed out. Look for FIXME.
- */
public abstract class Collator implements Comparator, Cloneable
{
/**
@@ -244,10 +242,7 @@ public abstract class Collator implements Comparator, Cloneable
*/
public static synchronized Locale[] getAvailableLocales ()
{
- // FIXME
- Locale[] l = new Locale[1];
- l[0] = Locale.US;
- return l;
+ return LocaleHelper.getCollatorLocales();
}
/**
@@ -292,7 +287,7 @@ public abstract class Collator implements Comparator, Cloneable
* specified locale. If no <code>Collator</code> exists for the desired
* locale, a <code>Collator</code> for the default locale will be returned.
*
- * @param loc The desired localed to load a <code>Collator</code> for.
+ * @param loc The desired locale to load a <code>Collator</code> for.
*
* @return A <code>Collator</code> for the requested locale
*/
diff --git a/libjava/classpath/java/text/DateFormatSymbols.java b/libjava/classpath/java/text/DateFormatSymbols.java
index 6a20b4c2a36..bffd31fb6a7 100644
--- a/libjava/classpath/java/text/DateFormatSymbols.java
+++ b/libjava/classpath/java/text/DateFormatSymbols.java
@@ -292,6 +292,8 @@ public class DateFormatSymbols implements java.io.Serializable, Cloneable
*/
public void setAmPmStrings (String[] value)
{
+ if(value==null)
+ throw new NullPointerException();
ampms = value;
}
@@ -305,6 +307,8 @@ public class DateFormatSymbols implements java.io.Serializable, Cloneable
*/
public void setEras (String[] labels)
{
+ if(labels==null)
+ throw new NullPointerException();
eras = labels;
}
@@ -343,6 +347,8 @@ public class DateFormatSymbols implements java.io.Serializable, Cloneable
*/
public void setLocalPatternChars (String chars)
{
+ if(chars==null)
+ throw new NullPointerException();
localPatternChars = chars;
}
@@ -357,6 +363,8 @@ public class DateFormatSymbols implements java.io.Serializable, Cloneable
*/
public void setMonths (String[] labels)
{
+ if(labels==null)
+ throw new NullPointerException();
months = labels;
}
@@ -372,6 +380,8 @@ public class DateFormatSymbols implements java.io.Serializable, Cloneable
*/
public void setShortMonths (String[] labels)
{
+ if(labels==null)
+ throw new NullPointerException();
shortMonths = labels;
}
@@ -387,6 +397,8 @@ public class DateFormatSymbols implements java.io.Serializable, Cloneable
*/
public void setShortWeekdays (String[] labels)
{
+ if(labels==null)
+ throw new NullPointerException();
shortWeekdays = labels;
}
@@ -401,6 +413,8 @@ public class DateFormatSymbols implements java.io.Serializable, Cloneable
*/
public void setWeekdays (String[] labels)
{
+ if(labels==null)
+ throw new NullPointerException();
weekdays = labels;
}
@@ -421,6 +435,8 @@ public class DateFormatSymbols implements java.io.Serializable, Cloneable
*/
public void setZoneStrings (String[][] zones)
{
+ if(zones==null)
+ throw new NullPointerException();
zoneStrings = zones;
}
diff --git a/libjava/classpath/java/text/DecimalFormat.java b/libjava/classpath/java/text/DecimalFormat.java
index a9ec7767f94..f64249b6755 100644
--- a/libjava/classpath/java/text/DecimalFormat.java
+++ b/libjava/classpath/java/text/DecimalFormat.java
@@ -542,9 +542,9 @@ public class DecimalFormat extends NumberFormat
// Compute exponent.
long exponent = 0;
double baseNumber;
- if (useExponentialNotation)
+ if (useExponentialNotation && number > 0)
{
- exponent = (long) Math.floor (Math.log(number) / Math.log(10));
+ exponent = (long) Math.floor (Math.log10(number));
exponent = exponent - (exponent % exponentRound);
if (minimumIntegerDigits > 0)
exponent -= minimumIntegerDigits - 1;
@@ -654,7 +654,7 @@ public class DecimalFormat extends NumberFormat
index = dest.length();
dest.setDefaultAttribute(NumberFormat.Field.EXPONENT);
String exponentString = Long.toString ((long) exponent);
-
+
for (count = 0; count < minExponentDigits-exponentString.length();
count++)
dest.append((char) symbols.getZeroDigit());
diff --git a/libjava/classpath/java/text/NumberFormat.java b/libjava/classpath/java/text/NumberFormat.java
index 5fede9ee37a..a42e9b3d9c8 100644
--- a/libjava/classpath/java/text/NumberFormat.java
+++ b/libjava/classpath/java/text/NumberFormat.java
@@ -481,7 +481,7 @@ public abstract class NumberFormat extends Format implements Cloneable
*/
public static NumberFormat getIntegerInstance(Locale locale)
{
- NumberFormat format = computeInstance (locale, "numberFormat", "#,##0");
+ NumberFormat format = computeInstance (locale, "integerFormat", "#,##0");
format.setMaximumFractionDigits(0);
format.setParseIntegerOnly (true);
return format;
diff --git a/libjava/classpath/java/text/RuleBasedCollator.java b/libjava/classpath/java/text/RuleBasedCollator.java
index 5756e9aa791..4bffcaf2905 100644
--- a/libjava/classpath/java/text/RuleBasedCollator.java
+++ b/libjava/classpath/java/text/RuleBasedCollator.java
@@ -38,6 +38,8 @@ exception statement from your version. */
package java.text;
+import gnu.classpath.NotImplementedException;
+
import java.util.ArrayList;
import java.util.HashMap;
@@ -921,6 +923,7 @@ element_loop:
* @return A <code>CollationElementIterator</code> for the specified <code>String</code>.
*/
public CollationElementIterator getCollationElementIterator(CharacterIterator source)
+ throws NotImplementedException // Because decomposeCharacter does not work
{
StringBuffer expand = new StringBuffer("");
diff --git a/libjava/classpath/java/text/SimpleDateFormat.java b/libjava/classpath/java/text/SimpleDateFormat.java
index c0c4cf68cea..2825c7bed24 100644
--- a/libjava/classpath/java/text/SimpleDateFormat.java
+++ b/libjava/classpath/java/text/SimpleDateFormat.java
@@ -307,7 +307,7 @@ public class SimpleDateFormat extends DateFormat
// Not a valid letter
throw new IllegalArgumentException("Invalid letter "
+ thisChar +
- "encountered at character "
+ " encountered at character "
+ i + ".");
}
else if (thisChar == '\'')
@@ -805,7 +805,8 @@ public class SimpleDateFormat extends DateFormat
buffer.setDefaultAttribute(DateFormat.Field.RFC822_TIME_ZONE);
int pureMinutes = (calendar.get(Calendar.ZONE_OFFSET) +
calendar.get(Calendar.DST_OFFSET)) / (1000 * 60);
- String sign = (pureMinutes < 0) ? "-" : "+";
+ String sign = (pureMinutes < 0) ? "-" : "+";
+ pureMinutes = Math.abs(pureMinutes);
int hours = pureMinutes / 60;
int minutes = pureMinutes % 60;
buffer.append(sign);
diff --git a/libjava/classpath/java/text/class-dependencies.conf b/libjava/classpath/java/text/class-dependencies.conf
new file mode 100644
index 00000000000..011b146ce10
--- /dev/null
+++ b/libjava/classpath/java/text/class-dependencies.conf
@@ -0,0 +1,220 @@
+# This property file contains dependencies of classes, methods, and
+# field on other methods or classes.
+#
+# Syntax:
+#
+# <used>: <needed 1> [... <needed N>]
+#
+# means that when <used> is included, <needed 1> (... <needed N>) must
+# be included as well.
+#
+# <needed X> and <used> are of the form
+#
+# <class.methodOrField(signature)>
+#
+# or just
+#
+# <class>
+#
+# Within dependencies, variables can be used. A variable is defined as
+# follows:
+#
+# {variable}: value1 value2 ... value<n>
+#
+# variables can be used on the right side of dependencies as follows:
+#
+# <used>: com.bla.blu.{variable}.Class.m()V
+#
+# The use of the variable will expand to <n> dependencies of the form
+#
+# <used>: com.bla.blu.value1.Class.m()V
+# <used>: com.bla.blu.value2.Class.m()V
+# ...
+# <used>: com.bla.blu.value<n>.Class.m()V
+#
+# Variables can be redefined when building a system to select the
+# required support for features like encodings, protocols, etc.
+#
+# Hints:
+#
+# - For methods and fields, the signature is mandatory. For
+# specification, please see the Java Virtual Machine Specification by
+# SUN. Unlike in the spec, field signatures (types) are in brackets.
+#
+# - Package names must be separated by '/' (and not '.'). E.g.,
+# java/lang/Class (this is necessary, because the '.' is used to
+# separate method or field names from classes)
+#
+# - In case <needed> refers to a class, only the class itself will be
+# included in the resulting binary, NOT necessarily all its methods
+# and fields. If you want to refer to all methods and fields, you can
+# write class.* as an abbreviation.
+#
+# - Abbreviations for packages are also possible: my/package/* means all
+# methods and fields of all classes in my/package.
+#
+# - A line with a trailing '\' continues in the next line.
+
+# end of file
+
+# All locales supported are loaded via classes from java.text (see below)
+# from class gnu/java/locale/LocaleInformation_<locale_id>
+#
+# This introduces a dependency for all locales. To allow an easy selection
+# and addition of locales, the library variable {text_locales} can be set to
+# the set of supported locales.
+#
+
+{text_locales}: \
+ af_ZA \
+ ar_AE \
+ ar_BH \
+ ar_DZ \
+ ar_EG \
+ ar_IN \
+ ar_IQ \
+ ar_JO \
+ ar_KW \
+ ar_LB \
+ ar_LY \
+ ar_MA \
+ ar_OM \
+ ar_QA \
+ ar_SD \
+ ar_SY \
+ ar_TN \
+ ar_YE \
+ be_BY \
+ bn_IN \
+ br_FR \
+ bs_BA \
+ ca_ES \
+ cs_CZ \
+ cy_GB \
+ da_DK \
+ de \
+ de_AT \
+ de_BE \
+ de_CH \
+ de_DE \
+ de_LU \
+ el_GR \
+ en \
+ en_AU \
+ en_BW \
+ en_CA \
+ en_DK \
+ en_GB \
+ en_HK \
+ en_IE \
+ en_IN \
+ en_NZ \
+ en_PH \
+ en_SG \
+ en_US \
+ en_ZA \
+ en_ZW \
+ es_AR \
+ es_BO \
+ es_CL \
+ es_CO \
+ es_CR \
+ es_DO \
+ es_EC \
+ es_ES \
+ es_GT \
+ es_HN \
+ es_MX \
+ es_NI \
+ es_PA \
+ es_PE \
+ es_PR \
+ es_PY \
+ es_SV \
+ es_US \
+ es_UY \
+ es_VE \
+ et_EE \
+ eu_ES \
+ fa_IR \
+ fi_FI \
+ fo_FO \
+ fr_BE \
+ fr_CA \
+ fr_CH \
+ fr_FR \
+ fr_LU \
+ ga_IE \
+ gd_GB \
+ gl_ES \
+ gv_GB \
+ he_IL \
+ hi_IN \
+ hr_HR \
+ hu_HU \
+ id_ID \
+ it_CH \
+ it_IT \
+ iw_IL \
+ ja_JP \
+ ka_GE \
+ kl_GL \
+ ko_KR \
+ kw_GB \
+ lt_LT \
+ lv_LV \
+ mi_NZ \
+ mk_MK \
+ mr_IN \
+ mt_MT \
+ nl \
+ nl_BE \
+ nl_NL \
+ nn_NO \
+ no_NO \
+ oc_FR \
+ pl_PL \
+ pt_BR \
+ pt_PT \
+ ro_RO \
+ ru_RU \
+ ru_UA \
+ se_NO \
+ sk_SK \
+ sl_SI \
+ sq_AL \
+ sr_YU \
+ sv_FI \
+ sv_SE \
+ ta_IN \
+ te_IN \
+ tg_TJ \
+ tl_PH \
+ tr_TR \
+ uk_UA \
+ ur_PK \
+ uz_UZ \
+ vi_VN \
+ yi_US \
+ zh_CN \
+ zh_HK \
+ zh_SG \
+ zh_TW
+
+java/text/Collator.getInstance(Ljava/util/Locale;)Ljava/text/Collator;: \
+ gnu/java/locale/LocaleInformation_{text_locales}.*
+
+java/text/DateFormatSymbols.<init>(Ljava/util/Locale;)V: \
+ gnu/java/locale/LocaleInformation_{text_locales}.*
+
+java/text/DecimalFormatSymbols.<init>(Ljava/util/Locale;)V: \
+ gnu/java/locale/LocaleInformation_{text_locales}.*
+
+java/text/BreakIterator.getInstance(Ljava/lang/String;Ljava/util/Locale;)Ljava/text/BreakIterator;: \
+ gnu/java/locale/LocaleInformation_{text_locales}.*
+
+java/text/NumberFormat.computeInstance(Ljava/util/Locale;Ljava/lang/String;Ljava/lang/String;)Ljava/text/NumberFormat;: \
+ gnu/java/locale/LocaleInformation_{text_locales}.*
+
+java/text/DateFormat.computeInstance(IILjava/util/Locale;ZZ)Ljava/text/DateFormat;: \
+ gnu/java/locale/LocaleInformation_{text_locales}.*
diff --git a/libjava/classpath/tools/.cvsignore b/libjava/classpath/tools/.cvsignore
new file mode 100644
index 00000000000..cf3688511ad
--- /dev/null
+++ b/libjava/classpath/tools/.cvsignore
@@ -0,0 +1,6 @@
+jarsigner
+keytool
+Makefile.in
+Makefile
+tools.zip
+appletviewer
diff --git a/libjava/java/util/logging/LogManager.java b/libjava/java/util/logging/LogManager.java
new file mode 100644
index 00000000000..b8b31c43cb6
--- /dev/null
+++ b/libjava/java/util/logging/LogManager.java
@@ -0,0 +1,981 @@
+/* LogManager.java -- a class for maintaining Loggers and managing
+ configuration properties
+ Copyright (C) 2002, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath 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, or (at your option)
+any later version.
+
+GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.util.logging;
+
+import gnu.classpath.SystemProperties;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+import java.net.URL;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+/**
+ * The <code>LogManager</code> maintains a hierarchical namespace
+ * of Logger objects and manages properties for configuring the logging
+ * framework. There exists only one single <code>LogManager</code>
+ * per virtual machine. This instance can be retrieved using the
+ * static method {@link #getLogManager()}.
+ *
+ * <p><strong>Configuration Process:</strong> The global LogManager
+ * object is created and configured when the class
+ * <code>java.util.logging.LogManager</code> is initialized.
+ * The configuration process includes the subsequent steps:
+ *
+ * <ul>
+ * <li>If the system property <code>java.util.logging.manager</code>
+ * is set to the name of a subclass of
+ * <code>java.util.logging.LogManager</code>, an instance of
+ * that subclass is created and becomes the global LogManager.
+ * Otherwise, a new instance of LogManager is created.</li>
+ * <li>The <code>LogManager</code> constructor tries to create
+ * a new instance of the class specified by the system
+ * property <code>java.util.logging.config.class</code>.
+ * Typically, the constructor of this class will call
+ * <code>LogManager.getLogManager().readConfiguration(java.io.InputStream)</code>
+ * for configuring the logging framework.
+ * The configuration process stops at this point if
+ * the system property <code>java.util.logging.config.class</code>
+ * is set (irrespective of whether the class constructor
+ * could be called or an exception was thrown).</li>
+ *
+ * <li>If the system property <code>java.util.logging.config.class</code>
+ * is <em>not</em> set, the configuration parameters are read in from
+ * a file and passed to
+ * {@link #readConfiguration(java.io.InputStream)}.
+ * The name and location of this file are specified by the system
+ * property <code>java.util.logging.config.file</code>.</li>
+ * <li>If the system property <code>java.util.logging.config.file</code>
+ * is not set, however, the contents of the URL
+ * "{gnu.classpath.home.url}/logging.properties" are passed to
+ * {@link #readConfiguration(java.io.InputStream)}.
+ * Here, "{gnu.classpath.home.url}" stands for the value of
+ * the system property <code>gnu.classpath.home.url</code>.</li>
+ * </ul>
+ *
+ * <p>The <code>LogManager</code> has a level of <code>INFO</code> by
+ * default, and this will be inherited by <code>Logger</code>s unless they
+ * override it either by properties or programmatically.
+ *
+ * @author Sascha Brawer (brawer@acm.org)
+ */
+public class LogManager
+{
+ /**
+ * The object name for the logging management bean.
+ * @since 1.5
+ */
+ public static final String LOGGING_MXBEAN_NAME
+ = "java.util.logging:type=Logging";
+
+ /**
+ * The singleton LogManager instance.
+ */
+ private static LogManager logManager;
+
+ /**
+ * The singleton logging bean.
+ */
+ private static LoggingMXBean loggingBean;
+
+ /**
+ * The registered named loggers; maps the name of a Logger to
+ * a WeakReference to it.
+ */
+ private Map loggers;
+
+ /**
+ * The properties for the logging framework which have been
+ * read in last.
+ */
+ private Properties properties;
+
+ /**
+ * A delegate object that provides support for handling
+ * PropertyChangeEvents. The API specification does not
+ * mention which bean should be the source in the distributed
+ * PropertyChangeEvents, but Mauve test code has determined that
+ * the Sun J2SE 1.4 reference implementation uses the LogManager
+ * class object. This is somewhat strange, as the class object
+ * is not the bean with which listeners have to register, but
+ * there is no reason for the GNU Classpath implementation to
+ * behave differently from the reference implementation in
+ * this case.
+ */
+ private final PropertyChangeSupport pcs = new PropertyChangeSupport( /* source bean */
+ LogManager.class);
+
+ protected LogManager()
+ {
+ loggers = new HashMap();
+ }
+
+ /**
+ * Returns the globally shared LogManager instance.
+ */
+ public static synchronized LogManager getLogManager()
+ {
+ if (logManager == null)
+ {
+ logManager = makeLogManager();
+ initLogManager();
+ }
+ return logManager;
+ }
+
+ private static final String MANAGER_PROPERTY = "java.util.logging.manager";
+
+ private static LogManager makeLogManager()
+ {
+ String managerClassName = SystemProperties.getProperty(MANAGER_PROPERTY);
+ LogManager manager = (LogManager) createInstance
+ (managerClassName, LogManager.class, MANAGER_PROPERTY);
+ if (manager == null)
+ manager = new LogManager();
+ return manager;
+ }
+
+ private static final String CONFIG_PROPERTY = "java.util.logging.config.class";
+
+ private static void initLogManager()
+ {
+ LogManager manager = getLogManager();
+ Logger.root.setLevel(Level.INFO);
+ manager.addLogger(Logger.root);
+
+ /* The Javadoc description of the class explains
+ * what is going on here.
+ */
+ Object configurator = createInstance(System.getProperty(CONFIG_PROPERTY),
+ /* must be instance of */ Object.class,
+ CONFIG_PROPERTY);
+
+ try
+ {
+ if (configurator == null)
+ manager.readConfiguration();
+ }
+ catch (IOException ex)
+ {
+ /* FIXME: Is it ok to ignore exceptions here? */
+ }
+ }
+
+ /**
+ * Registers a listener which will be notified when the
+ * logging properties are re-read.
+ */
+ public synchronized void addPropertyChangeListener(PropertyChangeListener listener)
+ {
+ /* do not register null. */
+ listener.getClass();
+
+ pcs.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Unregisters a listener.
+ *
+ * If <code>listener</code> has not been registered previously,
+ * nothing happens. Also, no exception is thrown if
+ * <code>listener</code> is <code>null</code>.
+ */
+ public synchronized void removePropertyChangeListener(PropertyChangeListener listener)
+ {
+ if (listener != null)
+ pcs.removePropertyChangeListener(listener);
+ }
+
+ /**
+ * Adds a named logger. If a logger with the same name has
+ * already been registered, the method returns <code>false</code>
+ * without adding the logger.
+ *
+ * <p>The <code>LogManager</code> only keeps weak references
+ * to registered loggers. Therefore, names can become available
+ * after automatic garbage collection.
+ *
+ * @param logger the logger to be added.
+ *
+ * @return <code>true</code>if <code>logger</code> was added,
+ * <code>false</code> otherwise.
+ *
+ * @throws NullPointerException if <code>name</code> is
+ * <code>null</code>.
+ */
+ public synchronized boolean addLogger(Logger logger)
+ {
+ /* To developers thinking about to remove the 'synchronized'
+ * declaration from this method: Please read the comment
+ * in java.util.logging.Logger.getLogger(String, String)
+ * and make sure that whatever you change wrt. synchronization
+ * does not endanger thread-safety of Logger.getLogger.
+ * The current implementation of Logger.getLogger assumes
+ * that LogManager does its synchronization on the globally
+ * shared instance of LogManager.
+ */
+ String name;
+ WeakReference ref;
+
+ /* This will throw a NullPointerException if logger is null,
+ * as required by the API specification.
+ */
+ name = logger.getName();
+
+ ref = (WeakReference) loggers.get(name);
+ if (ref != null)
+ {
+ if (ref.get() != null)
+ return false;
+
+ /* There has been a logger under this name in the past,
+ * but it has been garbage collected.
+ */
+ loggers.remove(ref);
+ }
+
+ /* Adding a named logger requires a security permission. */
+ if ((name != null) && ! name.equals(""))
+ checkAccess();
+
+ Logger parent = findAncestor(logger);
+ loggers.put(name, new WeakReference(logger));
+ if (parent != logger.getParent())
+ logger.setParent(parent);
+
+ // The level of the newly added logger must be specified.
+ // The easiest case is if there is a level for exactly this logger
+ // in the properties. If no such level exists the level needs to be
+ // searched along the hirachy. So if there is a new logger 'foo.blah.blub'
+ // and an existing parent logger 'foo' the properties 'foo.blah.blub.level'
+ // and 'foo.blah.level' need to be checked. If both do not exist in the
+ // properties the level of the new logger is set to 'null' (i.e. it uses the
+ // level of its parent 'foo').
+ Level logLevel = logger.getLevel();
+ String searchName = name;
+ String parentName = parent != null ? parent.getName() : "";
+ while (logLevel == null && ! searchName.equals(parentName))
+ {
+ logLevel = getLevelProperty(searchName + ".level", logLevel);
+ int index = searchName.lastIndexOf('.');
+ if(index > -1)
+ searchName = searchName.substring(0,index);
+ else
+ searchName = "";
+ }
+ logger.setLevel(logLevel);
+
+ /* It can happen that existing loggers should be children of
+ * the newly added logger. For example, assume that there
+ * already exist loggers under the names "", "foo", and "foo.bar.baz".
+ * When adding "foo.bar", the logger "foo.bar.baz" should change
+ * its parent to "foo.bar".
+ */
+ for (Iterator iter = loggers.keySet().iterator(); iter.hasNext();)
+ {
+ Logger possChild = (Logger) ((WeakReference) loggers.get(iter.next()))
+ .get();
+ if ((possChild == null) || (possChild == logger)
+ || (possChild.getParent() != parent))
+ continue;
+
+ if (! possChild.getName().startsWith(name))
+ continue;
+
+ if (possChild.getName().charAt(name.length()) != '.')
+ continue;
+
+ possChild.setParent(logger);
+ }
+
+ return true;
+ }
+
+ /**
+ * Finds the closest ancestor for a logger among the currently
+ * registered ones. For example, if the currently registered
+ * loggers have the names "", "foo", and "foo.bar", the result for
+ * "foo.bar.baz" will be the logger whose name is "foo.bar".
+ *
+ * @param child a logger for whose name no logger has been
+ * registered.
+ *
+ * @return the closest ancestor for <code>child</code>,
+ * or <code>null</code> if <code>child</code>
+ * is the root logger.
+ *
+ * @throws NullPointerException if <code>child</code>
+ * is <code>null</code>.
+ */
+ private synchronized Logger findAncestor(Logger child)
+ {
+ String childName = child.getName();
+ int childNameLength = childName.length();
+ Logger best = Logger.root;
+ int bestNameLength = 0;
+
+ Logger cand;
+ String candName;
+ int candNameLength;
+
+ if (child == Logger.root)
+ return null;
+
+ for (Iterator iter = loggers.keySet().iterator(); iter.hasNext();)
+ {
+ candName = (String) iter.next();
+ candNameLength = candName.length();
+
+ if (candNameLength > bestNameLength
+ && childNameLength > candNameLength
+ && childName.startsWith(candName)
+ && childName.charAt(candNameLength) == '.')
+ {
+ cand = (Logger) ((WeakReference) loggers.get(candName)).get();
+ if ((cand == null) || (cand == child))
+ continue;
+
+ bestNameLength = candName.length();
+ best = cand;
+ }
+ }
+
+ return best;
+ }
+
+ /**
+ * Returns a Logger given its name.
+ *
+ * @param name the name of the logger.
+ *
+ * @return a named Logger, or <code>null</code> if there is no
+ * logger with that name.
+ *
+ * @throw java.lang.NullPointerException if <code>name</code>
+ * is <code>null</code>.
+ */
+ public synchronized Logger getLogger(String name)
+ {
+ WeakReference ref;
+
+ /* Throw a NullPointerException if name is null. */
+ name.getClass();
+
+ ref = (WeakReference) loggers.get(name);
+ if (ref != null)
+ return (Logger) ref.get();
+ else
+ return null;
+ }
+
+ /**
+ * Returns an Enumeration of currently registered Logger names.
+ * Since other threads can register loggers at any time, the
+ * result could be different any time this method is called.
+ *
+ * @return an Enumeration with the names of the currently
+ * registered Loggers.
+ */
+ public synchronized Enumeration getLoggerNames()
+ {
+ return Collections.enumeration(loggers.keySet());
+ }
+
+ /**
+ * Resets the logging configuration by removing all handlers for
+ * registered named loggers and setting their level to <code>null</code>.
+ * The level of the root logger will be set to <code>Level.INFO</code>.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ */
+ public synchronized void reset() throws SecurityException
+ {
+ /* Throw a SecurityException if the caller does not have the
+ * permission to control the logging infrastructure.
+ */
+ checkAccess();
+
+ properties = new Properties();
+
+ Iterator iter = loggers.values().iterator();
+ while (iter.hasNext())
+ {
+ WeakReference ref;
+ Logger logger;
+
+ ref = (WeakReference) iter.next();
+ if (ref != null)
+ {
+ logger = (Logger) ref.get();
+
+ if (logger == null)
+ iter.remove();
+ else if (logger != Logger.root)
+ {
+ logger.resetLogger();
+ logger.setLevel(null);
+ }
+ }
+ }
+
+ Logger.root.setLevel(Level.INFO);
+ Logger.root.resetLogger();
+ }
+
+ /**
+ * Configures the logging framework by reading a configuration file.
+ * The name and location of this file are specified by the system
+ * property <code>java.util.logging.config.file</code>. If this
+ * property is not set, the URL
+ * "{gnu.classpath.home.url}/logging.properties" is taken, where
+ * "{gnu.classpath.home.url}" stands for the value of the system
+ * property <code>gnu.classpath.home.url</code>.
+ *
+ * <p>The task of configuring the framework is then delegated to
+ * {@link #readConfiguration(java.io.InputStream)}, which will
+ * notify registered listeners after having read the properties.
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure, or if the caller is
+ * not granted the permission to read the configuration
+ * file.
+ *
+ * @throws IOException if there is a problem reading in the
+ * configuration file.
+ */
+ public synchronized void readConfiguration()
+ throws IOException, SecurityException
+ {
+ String path;
+ InputStream inputStream;
+
+ path = System.getProperty("java.util.logging.config.file");
+ if ((path == null) || (path.length() == 0))
+ {
+ String url = (System.getProperty("gnu.classpath.home.url")
+ + "/logging.properties");
+ try
+ {
+ inputStream = new URL(url).openStream();
+ }
+ catch (Exception e)
+ {
+ inputStream=null;
+ }
+
+ // If no config file could be found use a default configuration.
+ if(inputStream == null)
+ {
+ String defaultConfig = "handlers = java.util.logging.ConsoleHandler \n"
+ + ".level=INFO \n";
+ inputStream = new ByteArrayInputStream(defaultConfig.getBytes());
+ }
+ }
+ else
+ inputStream = new java.io.FileInputStream(path);
+
+ try
+ {
+ readConfiguration(inputStream);
+ }
+ finally
+ {
+ // Close the stream in order to save
+ // resources such as file descriptors.
+ inputStream.close();
+ }
+ }
+
+ public synchronized void readConfiguration(InputStream inputStream)
+ throws IOException, SecurityException
+ {
+ Properties newProperties;
+ Enumeration keys;
+
+ checkAccess();
+ newProperties = new Properties();
+ newProperties.load(inputStream);
+ reset();
+ this.properties = newProperties;
+ keys = newProperties.propertyNames();
+
+ while (keys.hasMoreElements())
+ {
+ String key = ((String) keys.nextElement()).trim();
+ String value = newProperties.getProperty(key);
+
+ if (value == null)
+ continue;
+
+ value = value.trim();
+
+ if ("handlers".equals(key))
+ {
+ StringTokenizer tokenizer = new StringTokenizer(value);
+ while (tokenizer.hasMoreTokens())
+ {
+ String handlerName = tokenizer.nextToken();
+ Handler handler = (Handler)
+ createInstance(handlerName, Handler.class, key);
+ Logger.root.addHandler(handler);
+ }
+ }
+
+ if (key.endsWith(".level"))
+ {
+ String loggerName = key.substring(0, key.length() - 6);
+ Logger logger = getLogger(loggerName);
+
+ if (logger == null)
+ {
+ logger = Logger.getLogger(loggerName);
+ addLogger(logger);
+ }
+ Level level = null;
+ try
+ {
+ level = Level.parse(value);
+ }
+ catch (IllegalArgumentException e)
+ {
+ warn("bad level \'" + value + "\'", e);
+ }
+ if (level != null)
+ {
+ logger.setLevel(level);
+ }
+ continue;
+ }
+ }
+
+ /* The API specification does not talk about the
+ * property name that is distributed with the
+ * PropertyChangeEvent. With test code, it could
+ * be determined that the Sun J2SE 1.4 reference
+ * implementation uses null for the property name.
+ */
+ pcs.firePropertyChange(null, null, null);
+ }
+
+ /**
+ * Returns the value of a configuration property as a String.
+ */
+ public synchronized String getProperty(String name)
+ {
+ if (properties != null)
+ return properties.getProperty(name);
+ else
+ return null;
+ }
+
+ /**
+ * Returns the value of a configuration property as an integer.
+ * This function is a helper used by the Classpath implementation
+ * of java.util.logging, it is <em>not</em> specified in the
+ * logging API.
+ *
+ * @param name the name of the configuration property.
+ *
+ * @param defaultValue the value that will be returned if the
+ * property is not defined, or if its value is not an integer
+ * number.
+ */
+ static int getIntProperty(String name, int defaultValue)
+ {
+ try
+ {
+ return Integer.parseInt(getLogManager().getProperty(name));
+ }
+ catch (Exception ex)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value of a configuration property as an integer,
+ * provided it is inside the acceptable range.
+ * This function is a helper used by the Classpath implementation
+ * of java.util.logging, it is <em>not</em> specified in the
+ * logging API.
+ *
+ * @param name the name of the configuration property.
+ *
+ * @param minValue the lowest acceptable value.
+ *
+ * @param maxValue the highest acceptable value.
+ *
+ * @param defaultValue the value that will be returned if the
+ * property is not defined, or if its value is not an integer
+ * number, or if it is less than the minimum value,
+ * or if it is greater than the maximum value.
+ */
+ static int getIntPropertyClamped(String name, int defaultValue,
+ int minValue, int maxValue)
+ {
+ int val = getIntProperty(name, defaultValue);
+ if ((val < minValue) || (val > maxValue))
+ val = defaultValue;
+ return val;
+ }
+
+ /**
+ * Returns the value of a configuration property as a boolean.
+ * This function is a helper used by the Classpath implementation
+ * of java.util.logging, it is <em>not</em> specified in the
+ * logging API.
+ *
+ * @param name the name of the configuration property.
+ *
+ * @param defaultValue the value that will be returned if the
+ * property is not defined, or if its value is neither
+ * <code>"true"</code> nor <code>"false"</code>.
+ */
+ static boolean getBooleanProperty(String name, boolean defaultValue)
+ {
+ try
+ {
+ return (Boolean.valueOf(getLogManager().getProperty(name))).booleanValue();
+ }
+ catch (Exception ex)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value of a configuration property as a Level.
+ * This function is a helper used by the Classpath implementation
+ * of java.util.logging, it is <em>not</em> specified in the
+ * logging API.
+ *
+ * @param propertyName the name of the configuration property.
+ *
+ * @param defaultValue the value that will be returned if the
+ * property is not defined, or if
+ * {@link Level#parse(java.lang.String)} does not like
+ * the property value.
+ */
+ static Level getLevelProperty(String propertyName, Level defaultValue)
+ {
+ try
+ {
+ return Level.parse(getLogManager().getProperty(propertyName));
+ }
+ catch (Exception ex)
+ {
+ return defaultValue;
+ }
+ }
+
+ /**
+ * Returns the value of a configuration property as a Class.
+ * This function is a helper used by the Classpath implementation
+ * of java.util.logging, it is <em>not</em> specified in the
+ * logging API.
+ *
+ * @param propertyName the name of the configuration property.
+ *
+ * @param defaultValue the value that will be returned if the
+ * property is not defined, or if it does not specify
+ * the name of a loadable class.
+ */
+ static final Class getClassProperty(String propertyName, Class defaultValue)
+ {
+ String propertyValue = logManager.getProperty(propertyName);
+
+ if (propertyValue != null)
+ try
+ {
+ return locateClass(propertyValue);
+ }
+ catch (ClassNotFoundException e)
+ {
+ warn(propertyName + " = " + propertyValue, e);
+ }
+
+ return defaultValue;
+ }
+
+ static final Object getInstanceProperty(String propertyName, Class ofClass,
+ Class defaultClass)
+ {
+ Class klass = getClassProperty(propertyName, defaultClass);
+ if (klass == null)
+ return null;
+
+ try
+ {
+ Object obj = klass.newInstance();
+ if (ofClass.isInstance(obj))
+ return obj;
+ }
+ catch (InstantiationException e)
+ {
+ warn(propertyName + " = " + klass.getName(), e);
+ }
+ catch (IllegalAccessException e)
+ {
+ warn(propertyName + " = " + klass.getName(), e);
+ }
+
+ if (defaultClass == null)
+ return null;
+
+ try
+ {
+ return defaultClass.newInstance();
+ }
+ catch (java.lang.InstantiationException ex)
+ {
+ throw new RuntimeException(ex.getMessage());
+ }
+ catch (java.lang.IllegalAccessException ex)
+ {
+ throw new RuntimeException(ex.getMessage());
+ }
+ }
+
+ /**
+ * An instance of <code>LoggingPermission("control")</code>
+ * that is shared between calls to <code>checkAccess()</code>.
+ */
+ private static final LoggingPermission controlPermission = new LoggingPermission("control",
+ null);
+
+ /**
+ * Checks whether the current security context allows changing
+ * the configuration of the logging framework. For the security
+ * context to be trusted, it has to be granted
+ * a LoggingPermission("control").
+ *
+ * @throws SecurityException if a security manager exists and
+ * the caller is not granted the permission to control
+ * the logging infrastructure.
+ */
+ public void checkAccess() throws SecurityException
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(controlPermission);
+ }
+
+ /**
+ * Creates a new instance of a class specified by name and verifies
+ * that it is an instance (or subclass of) a given type.
+ *
+ * @param className the name of the class of which a new instance
+ * should be created.
+ *
+ * @param type the object created must be an instance of
+ * <code>type</code> or any subclass of <code>type</code>
+ *
+ * @param property the system property to reference in error
+ * messages
+ *
+ * @return the new instance, or <code>null</code> if
+ * <code>className</code> is <code>null</code>, if no class
+ * with that name could be found, if there was an error
+ * loading that class, or if the constructor of the class
+ * has thrown an exception.
+ */
+ private static final Object createInstance(String className, Class type,
+ String property)
+ {
+ Class klass = null;
+
+ if ((className == null) || (className.length() == 0))
+ return null;
+
+ try
+ {
+ klass = locateClass(className);
+ if (type.isAssignableFrom(klass))
+ return klass.newInstance();
+ warn(property, className, "not an instance of " + type.getName());
+ }
+ catch (ClassNotFoundException e)
+ {
+ warn(property, className, "class not found", e);
+ }
+ catch (IllegalAccessException e)
+ {
+ warn(property, className, "illegal access", e);
+ }
+ catch (InstantiationException e)
+ {
+ warn(property, className, e);
+ }
+ catch (java.lang.LinkageError e)
+ {
+ warn(property, className, "linkage error", e);
+ }
+
+ return null;
+ }
+
+ private static final void warn(String property, String klass, Throwable t)
+ {
+ warn(property, klass, null, t);
+ }
+
+ private static final void warn(String property, String klass, String msg)
+ {
+ warn(property, klass, msg, null);
+ }
+
+ private static final void warn(String property, String klass, String msg,
+ Throwable t)
+ {
+ warn("error instantiating '" + klass + "' referenced by " + property +
+ (msg == null ? "" : ", " + msg), t);
+ }
+
+ /**
+ * All debug warnings go through this method.
+ */
+
+ private static final void warn(String msg, Throwable t)
+ {
+ System.err.println("WARNING: " + msg);
+ if (t != null)
+ t.printStackTrace(System.err);
+ }
+
+ /**
+ * Locates a class by first checking the system class loader and
+ * then checking the context class loader.
+ *
+ * @param name the fully qualified name of the Class to locate
+ * @return Class the located Class
+ */
+
+ private static Class locateClass(String name) throws ClassNotFoundException
+ {
+ // GCJ LOCAL
+ // Unfortunately this can be called during bootstrap when
+ // Thread.currentThread() will return null.
+ // See bug #27658
+ Thread t = Thread.currentThread();
+ ClassLoader loader = (t == null) ? null : t.getContextClassLoader();
+ try
+ {
+ return Class.forName(name, true, loader);
+ }
+ catch (ClassNotFoundException e)
+ {
+ loader = ClassLoader.getSystemClassLoader();
+ return Class.forName(name, true, loader);
+ }
+ }
+
+ /**
+ * Return the logging bean. There is a single logging bean per
+ * VM instance.
+ * @since 1.5
+ */
+ public static synchronized LoggingMXBean getLoggingMXBean()
+ {
+ if (loggingBean == null)
+ {
+ loggingBean = new LoggingMXBean()
+ {
+ public String getLoggerLevel(String logger)
+ {
+ LogManager mgr = getLogManager();
+ Logger l = mgr.getLogger(logger);
+ if (l == null)
+ return null;
+ Level lev = l.getLevel();
+ if (lev == null)
+ return "";
+ return lev.getName();
+ }
+
+ public List getLoggerNames()
+ {
+ LogManager mgr = getLogManager();
+ // This is inefficient, but perhaps better for maintenance.
+ return Collections.list(mgr.getLoggerNames());
+ }
+
+ public String getParentLoggerName(String logger)
+ {
+ LogManager mgr = getLogManager();
+ Logger l = mgr.getLogger(logger);
+ if (l == null)
+ return null;
+ l = l.getParent();
+ if (l == null)
+ return "";
+ return l.getName();
+ }
+
+ public void setLoggerLevel(String logger, String level)
+ {
+ LogManager mgr = getLogManager();
+ Logger l = mgr.getLogger(logger);
+ if (l == null)
+ throw new IllegalArgumentException("no logger named " + logger);
+ Level newLevel;
+ if (level == null)
+ newLevel = null;
+ else
+ newLevel = Level.parse(level);
+ l.setLevel(newLevel);
+ }
+ };
+ }
+ return loggingBean;
+ }
+}