public class TextAreaPainter extends JComponent implements TabExpander
{
	//{{{ Layers
	/**
	 * The lowest possible layer.
	 * @see #addExtension(int,TextAreaExtension)
	 * @since jEdit 4.0pre4
	 */
	public static final int LOWEST_LAYER = Integer.MIN_VALUE;

	/**
	 * Below selection layer. The JDiff plugin will use this.
	 * @see #addExtension(int,TextAreaExtension)
	 * @since jEdit 4.0pre4
	 */
	public static final int BACKGROUND_LAYER = -60;

	/**
	 * The line highlight and collapsed fold highlight layer.
	 * @see #addExtension(int,TextAreaExtension)
	 * @since jEdit 4.0pre7
	 */
	public static final int LINE_BACKGROUND_LAYER = -50;

	/**
	 * Below selection layer.
	 * @see #addExtension(int,TextAreaExtension)
	 * @since jEdit 4.0pre4
	 */
	public static final int BELOW_SELECTION_LAYER = -40;

	/**
	 * Selection layer. Most extensions will be above this layer, but some
	 * (eg, JDiff) will want to be below the selection.
	 * @see #addExtension(int,TextAreaExtension)
	 * @since jEdit 4.0pre4
	 */
	public static final int SELECTION_LAYER = -30;

	/**
	 * Wrap guide layer. Most extensions will be above this layer.
	 * @since jEdit 4.0pre4
	 */
	public static final int WRAP_GUIDE_LAYER = -20;

	/**
	 * Below most extensions layer.
	 * @see #addExtension(int,TextAreaExtension)
	 * @since jEdit 4.0pre4
	 */
	public static final int BELOW_MOST_EXTENSIONS_LAYER = -10;

	/**
	 * Default extension layer. This is above the wrap guide but below the
	 * structure highlight.
	 * @since jEdit 4.0pre4
	 */
	public static final int DEFAULT_LAYER = 0;

	/**
	 * Block caret layer. Most extensions will be below this layer.
	 * @since jEdit 4.2pre1
	 */
	public static final int BLOCK_CARET_LAYER = 50;

	/**
	 * Bracket highlight layer. Most extensions will be below this layer.
	 * @since jEdit 4.0pre4
	 */
	public static final int BRACKET_HIGHLIGHT_LAYER = 100;

	/**
	 * Text layer. Most extensions will be below this layer.
	 * @since jEdit 4.2pre1
	 */
	public static final int TEXT_LAYER = 200;

	/**
	 * Caret layer. Most extensions will be below this layer.
	 * @since jEdit 4.2pre1
	 */
	public static final int CARET_LAYER = 300;

	/**
	 * Highest possible layer.
	 * @since jEdit 4.0pre4
	 */
	public static final int HIGHEST_LAYER = Integer.MAX_VALUE;
	//}}}

	//{{{ setBounds() method
	/**
	 * It is a bad idea to override this, but we need to get the component
	 * event before the first repaint.
	 */
	@Override
	public void setBounds(int x, int y, int width, int height)
	{
		if(x == getX() && y == getY() && width == getWidth()
			&& height == getHeight())
		{
			return;
		}

		super.setBounds(x,y,width,height);

		textArea.recalculateVisibleLines();
		if(!textArea.getBuffer().isLoading())
			textArea.recalculateLastPhysicalLine();
		textArea.propertiesChanged();
		textArea.updateMaxHorizontalScrollWidth();
		textArea.scrollBarsInitialized = true;
	} //}}}

	//{{{ getFocusTraversalKeysEnabled() method
	/**
	 * Makes the tab key work in Java 1.4.
	 * @since jEdit 3.2pre4
	 */
	@Override
	public boolean getFocusTraversalKeysEnabled()
	{
		return false;
	} //}}}

	//{{{ Getters and setters

	//{{{ getStyles() method
	/**
	 * Returns the syntax styles used to paint colorized text. Entry n
	 * will be used to paint tokens with id = n.
	 * @return an array of SyntaxStyles
	 * @see org.gjt.sp.jedit.syntax.Token
	 */
	public final SyntaxStyle[] getStyles()
	{
		return styles;
	} //}}}

	//{{{ setStyles() method
	/**
	 * Sets the syntax styles used to paint colorized text. Entry n
	 * will be used to paint tokens with id = n.
	 * @param styles The syntax styles
	 * @see org.gjt.sp.jedit.syntax.Token
	 */
	public final void setStyles(SyntaxStyle[] styles)
	{
		// assumed this is called after a font render context is set up.
		// changing font render context settings without a setStyles()
		// call will not reset cached monospaced font info.
		fonts.clear();

		this.styles = styles;
		styles[Token.NULL] = new SyntaxStyle(getForeground(),null,getFont());
		repaint();
	} //}}}

	//{{{ getCaretColor() method
	/**
	 * Returns the caret color.
	 */
	public final Color getCaretColor()
	{
		return caretColor;
	} //}}}

	//{{{ setCaretColor() method
	/**
	 * Sets the caret color.
	 * @param caretColor The caret color
	 */
	public final void setCaretColor(Color caretColor)
	{
		this.caretColor = caretColor;
		if(textArea.getBuffer() != null)
			textArea.invalidateLine(textArea.getCaretLine());
	} //}}}

	//{{{ getSelectionColor() method
	/**
	 * Returns the selection color.
	 */
	public final Color getSelectionColor()
	{
		return selectionColor;
	} //}}}

	//{{{ setSelectionColor() method
	/**
	 * Sets the selection color.
	 * @param selectionColor The selection color
	 */
	public final void setSelectionColor(Color selectionColor)
	{
		this.selectionColor = selectionColor;
		textArea.repaint();
	} //}}}

	//{{{ getMultipleSelectionColor() method
	/**
	 * Returns the multiple selection color.
	 * @since jEdit 4.2pre1
	 */
	public final Color getMultipleSelectionColor()
	{
		return multipleSelectionColor;
	} //}}}

	//{{{ setMultipleSelectionColor() method
	/**
	 * Sets the multiple selection color.
	 * @param multipleSelectionColor The multiple selection color
	 * @since jEdit 4.2pre1
	 */
	public final void setMultipleSelectionColor(Color multipleSelectionColor)
	{
		this.multipleSelectionColor = multipleSelectionColor;
		textArea.repaint();
	} //}}}

	//{{{ getLineHighlightColor() method
	/**
	 * Returns the line highlight color.
	 */
	public final Color getLineHighlightColor()
	{
		return lineHighlightColor;
	} //}}}

	//{{{ setLineHighlightColor() method
	/**
	 * Sets the line highlight color.
	 * @param lineHighlightColor The line highlight color
	 */
	public final void setLineHighlightColor(Color lineHighlightColor)
	{
		this.lineHighlightColor = lineHighlightColor;
		if(textArea.getBuffer() != null)
			textArea.invalidateLine(textArea.getCaretLine());
	} //}}}

	//{{{ isLineHighlightEnabled() method
	/**
	 * Returns true if line highlight is enabled, false otherwise.
	 */
	public final boolean isLineHighlightEnabled()
	{
		return lineHighlight;
	} //}}}

	//{{{ setLineHighlightEnabled() method
	/**
	 * Enables or disables current line highlighting.
	 * @param lineHighlight True if current line highlight should be enabled,
	 * false otherwise
	 */
	public final void setLineHighlightEnabled(boolean lineHighlight)
	{
		this.lineHighlight = lineHighlight;
		textArea.repaint();
	} //}}}

	//{{{ getStructureHighlightColor() method
	/**
	 * Returns the structure highlight color.
	 * @since jEdit 4.2pre3
	 */
	public final Color getStructureHighlightColor()
	{
		return structureHighlightColor;
	} //}}}

	//{{{ setStructureHighlightColor() method
	/**
	 * Sets the structure highlight color.
	 * @param structureHighlightColor The bracket highlight color
	 * @since jEdit 4.2pre3
	 */
	public final void setStructureHighlightColor(
		Color structureHighlightColor)
	{
		this.structureHighlightColor = structureHighlightColor;
		textArea.invalidateStructureMatch();
	} //}}}

	//{{{ isStructureHighlightEnabled() method
	/**
	 * Returns true if structure highlighting is enabled, false otherwise.
	 * @since jEdit 4.2pre3
	 */
	public final boolean isStructureHighlightEnabled()
	{
		return structureHighlight;
	} //}}}

	//{{{ setStructureHighlightEnabled() method
	/**
	 * Enables or disables structure highlighting.
	 * @param structureHighlight True if structure highlighting should be
	 * enabled, false otherwise
	 * @since jEdit 4.2pre3
	 */
	public final void setStructureHighlightEnabled(boolean structureHighlight)
	{
		this.structureHighlight = structureHighlight;
		textArea.invalidateStructureMatch();
	} //}}}

	//{{{ isBlockCaretEnabled() method
	/**
	 * Returns true if the caret should be drawn as a block, false otherwise.
	 */
	public final boolean isBlockCaretEnabled()
	{
		return blockCaret;
	} //}}}

	//{{{ setBlockCaretEnabled() method
	/**
	 * Sets if the caret should be drawn as a block, false otherwise.
	 * @param blockCaret True if the caret should be drawn as a block,
	 * false otherwise.
	 */
	public final void setBlockCaretEnabled(boolean blockCaret)
	{
		this.blockCaret = blockCaret;
		extensionMgr.removeExtension(caretExtension);
		if(blockCaret)
			addExtension(BLOCK_CARET_LAYER,caretExtension);
		else
			addExtension(CARET_LAYER,caretExtension);
		if(textArea.getBuffer() != null)
			textArea.invalidateLine(textArea.getCaretLine());
	} //}}}

	//{{{ isThickCaretEnabled() method
	/**
	 * Returns true if the caret should be drawn with a thick line, false otherwise.
	 * @since jEdit 4.3pre15
	 */
	public final boolean isThickCaretEnabled()
	{
		return thickCaret;
	} //}}}

	//{{{ setThickCaretEnabled() method
	/**
	 * Sets if the caret should be drawn with a thick line.
	 * @param thickCaret
	 *     True if the caret should be drawn as a block, false otherwise.
	 * @since jEdit 4.3pre15
	 */
	public final void setThickCaretEnabled(boolean thickCaret)
	{
		this.thickCaret = thickCaret;
		if(textArea.getBuffer() != null)
			textArea.invalidateLine(textArea.getCaretLine());
	} //}}}

	//{{{ getEOLMarkerColor() method
	/**
	 * Returns the EOL marker color.
	 */
	public final Color getEOLMarkerColor()
	{
		return eolMarkerColor;
	} //}}}

	//{{{ setEOLMarkerColor() method
	/**
	 * Sets the EOL marker color.
	 * @param eolMarkerColor The EOL marker color
	 */
	public final void setEOLMarkerColor(Color eolMarkerColor)
	{
		this.eolMarkerColor = eolMarkerColor;
		repaint();
	} //}}}

	//{{{ getEOLMarkersPainted() method
	/**
	 * Returns true if EOL markers are drawn, false otherwise.
	 */
	public final boolean getEOLMarkersPainted()
	{
		return eolMarkers;
	} //}}}

	//{{{ setEOLMarkersPainted() method
	/**
	 * Sets if EOL markers are to be drawn.
	 * @param eolMarkers True if EOL markers should be drawn, false otherwise
	 */
	public final void setEOLMarkersPainted(boolean eolMarkers)
	{
		this.eolMarkers = eolMarkers;
		repaint();
	} //}}}

	//{{{ getWrapGuideColor() method
	/**
	 * Returns the wrap guide color.
	 */
	public final Color getWrapGuideColor()
	{
		return wrapGuideColor;
	} //}}}

	//{{{ setWrapGuideColor() method
	/**
	 * Sets the wrap guide color.
	 * @param wrapGuideColor The wrap guide color
	 */
	public final void setWrapGuideColor(Color wrapGuideColor)
	{
		this.wrapGuideColor = wrapGuideColor;
		repaint();
	} //}}}

	//{{{ isWrapGuidePainted() method
	/**
	 * Returns true if the wrap guide is drawn, false otherwise.
	 * @since jEdit 4.0pre4
	 */
	public final boolean isWrapGuidePainted()
	{
		return wrapGuide;
	} //}}}

	//{{{ setWrapGuidePainted() method
	/**
	 * Sets if the wrap guide is to be drawn.
	 * @param wrapGuide True if the wrap guide should be drawn, false otherwise
	 */
	public final void setWrapGuidePainted(boolean wrapGuide)
	{
		this.wrapGuide = wrapGuide;
		repaint();
	} //}}}

	//{{{ getFoldLineStyle() method
	/**
	 * Returns the fold line style. The first element is the style for
	 * lines with a fold level greater than 3. The remaining elements
	 * are for fold levels 1 to 3.
	 */
	public final SyntaxStyle[] getFoldLineStyle()
	{
		return foldLineStyle;
	} //}}}

	//{{{ setFoldLineStyle() method
	/**
	 * Sets the fold line style. The first element is the style for
	 * lines with a fold level greater than 3. The remaining elements
	 * are for fold levels 1 to 3.
	 * @param foldLineStyle The fold line style
	 */
	public final void setFoldLineStyle(SyntaxStyle[] foldLineStyle)
	{
		this.foldLineStyle = foldLineStyle;
		repaint();
	} //}}}

	//{{{ setAntiAliasEnabled() method
	/**
	 * @deprecated use setAntiAlias(AntiAlias newMode)
	 */
	@Deprecated
	public void setAntiAliasEnabled(boolean isEnabled)
	{
		setAntiAlias(new AntiAlias(isEnabled));
	}
	/**
	 * As of jEdit 4.3pre4, a new JDK 1.6 subpixel antialias mode is supported.
	 *
	 * @since jEdit 4.2pre4
	 */
	public void setAntiAlias(AntiAlias newValue)
	{
		antiAlias = newValue;
		updateRenderingHints();
	} //}}}

	/**
	 * @return the AntiAlias value that is currently used for TextAreas.
	 * @since jedit 4.3pre4
	 */
	public AntiAlias getAntiAlias()
	{
		return antiAlias;
	}

	//{{{ isAntiAliasEnabled() method
	/**
	 * Returns if anti-aliasing is enabled.
	 * @since jEdit 3.2pre6
	 * @deprecated - use @ref getAntiAlias()
	 */
	@Deprecated
	public boolean isAntiAliasEnabled()
	{
		return antiAlias.val() > 0;
	} //}}}

	//{{{ setFractionalFontMetricsEnabled() method
	/**
	 * Sets if fractional font metrics should be enabled. Has no effect when
	 * running on Java 1.1.
	 * @since jEdit 3.2pre6
	 */
	public void setFractionalFontMetricsEnabled(boolean fracFontMetrics)
	{
		this.fracFontMetrics = fracFontMetrics;
		updateRenderingHints();
	} //}}}

	//{{{ isFractionalFontMetricsEnabled() method
	/**
	 * Returns if fractional font metrics are enabled.
	 * @since jEdit 3.2pre6
	 */
	public boolean isFractionalFontMetricsEnabled()
	{
		return fracFontMetrics;
	} //}}}

	//{{{ getFontRenderContext() method
	/**
	 * Returns the font render context.
	 * @since jEdit 4.0pre4
	 */
	public FontRenderContext getFontRenderContext()
	{
		return fontRenderContext;
	} //}}}

	//}}}

	//{{{ addExtension() method
	/**
	 * Adds a text area extension, which can perform custom painting and
	 * tool tip handling.
	 * @param extension The extension
	 * @since jEdit 4.0pre4
	 */
	public void addExtension(TextAreaExtension extension)
	{
		extensionMgr.addExtension(DEFAULT_LAYER,extension);
		repaint();
	} //}}}

	//{{{ addExtension() method
	/**
	 * Adds a text area extension, which can perform custom painting and
	 * tool tip handling.
	 * @param layer The layer to add the extension to. Note that more than
	 * extension can share the same layer.
	 * @param extension The extension
	 * @since jEdit 4.0pre4
	 */
	public void addExtension(int layer, TextAreaExtension extension)
	{
		extensionMgr.addExtension(layer,extension);
		repaint();
	} //}}}

	//{{{ removeExtension() method
	/**
	 * Removes a text area extension. It will no longer be asked to
	 * perform custom painting and tool tip handling.
	 * @param extension The extension
	 * @since jEdit 4.0pre4
	 */
	public void removeExtension(TextAreaExtension extension)
	{
		extensionMgr.removeExtension(extension);
		repaint();
	} //}}}

	//{{{ getExtensions() method
	/**
	 * Returns an array of registered text area extensions. Useful for
	 * debugging purposes.
	 * @since jEdit 4.1pre5
	 */
	public TextAreaExtension[] getExtensions()
	{
		return extensionMgr.getExtensions();
	} //}}}

	//{{{ getToolTipText() method
	/**
	 * Returns the tool tip to display at the specified location.
	 * @param evt The mouse event
	 */
	@Override
	public String getToolTipText(MouseEvent evt)
	{
		if(textArea.getBuffer().isLoading())
			return null;

		return extensionMgr.getToolTipText(evt.getX(),evt.getY());
	} //}}}

	//{{{ getFontMetrics() method
	/**
	 * Returns the font metrics used by this component.
	 */
	public FontMetrics getFontMetrics()
	{
		return fm;
	} //}}}

	//{{{ setFont() method
	/**
	 * Sets the font for this component. This is overridden to update the
	 * cached font metrics and to recalculate which lines are visible.
	 * @param font The font
	 */
	@Override
	public void setFont(Font font)
	{
		super.setFont(font);
		fm = getFontMetrics(font);
		textArea.recalculateVisibleLines();
		if(textArea.getBuffer() != null
			&& !textArea.getBuffer().isLoading())
			textArea.recalculateLastPhysicalLine();
		//textArea.propertiesChanged();
	} //}}}

	//{{{ getStringWidth() method
	/**
	 * Returns the width of the given string, in pixels, using the text
	 * area's current font.
	 *
	 * @since jEdit 4.2final
	 */
	public float getStringWidth(String str)
	{
		if(textArea.charWidth != 0)
			return textArea.charWidth * str.length();
		else
		{
			return (float)getFont().getStringBounds(
				str,getFontRenderContext()).getWidth();
		}
	} //}}}

	//{{{ update() method
	/**
	 * Repaints the text.
	 * @param _gfx The graphics context
	 */
	@Override
	public void update(Graphics _gfx)
	{
		paint(_gfx);
	} //}}}

	//{{{ paint() method
	/**
	 * Repaints the text.
	 * @param _gfx The graphics context
	 */
	@Override
	public void paint(Graphics _gfx)
	{
		assert _gfx instanceof Graphics2D;
		Graphics2D gfx = (Graphics2D)_gfx;
		gfx.setRenderingHints(renderingHints);
		fontRenderContext = gfx.getFontRenderContext();

		Rectangle clipRect = gfx.getClipBounds();
		int lineHeight = fm.getHeight();
		if(lineHeight == 0 || textArea.getBuffer().isLoading())
		{
			gfx.setColor(getBackground());
			gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height);
		}
		else
		{
			long prepareTime = System.nanoTime();
			// Because the clipRect's height is usually an even multiple
			// of the font height, we subtract 1 from it, otherwise one
			// too many lines will always be painted.
			int firstLine = clipRect.y / lineHeight;
			int lastLine = (clipRect.y + clipRect.height - 1) / lineHeight;
			gfx.setColor(getBackground());
			gfx.setFont(getFont());
			prepareTime = System.nanoTime() - prepareTime;

			long linesTime = System.nanoTime();
			int numLines = lastLine - firstLine + 1;
			int y = firstLine * lineHeight;
			gfx.fillRect(0,y,getWidth(),numLines * lineHeight);
			extensionMgr.paintScreenLineRange(textArea,gfx,
				firstLine,lastLine,y,lineHeight);
			linesTime = System.nanoTime() - linesTime;

			if(Debug.PAINT_TIMER && numLines >= 1)
				Log.log(Log.DEBUG,this,"repainting " + numLines + " lines took " + prepareTime + "/" + linesTime + " ns");
		}

		textArea.updateMaxHorizontalScrollWidth();
	} //}}}

	//{{{ nextTabStop() method
	/**
	 * Implementation of TabExpander interface. Returns next tab stop after
	 * a specified point.
	 * @param x The x co-ordinate
	 * @param tabOffset Ignored
	 * @return The next tab stop after x
	 */
	public float nextTabStop(float x, int tabOffset)
	{
		int ntabs = (int)(x / textArea.tabSize);
		return (ntabs + 1) * textArea.tabSize;
	} //}}}

	//{{{ getPreferredSize() method
	/**
	 * Returns the painter's preferred size.
	 */
	@Override
	public Dimension getPreferredSize()
	{
		Dimension dim = new Dimension();

		char[] foo = new char[80];
		for(int i = 0; i < foo.length; i++)
			foo[i] = ' ';
		dim.width = (int)getStringWidth(new String(foo));
		dim.height = fm.getHeight() * 25;
		return dim;
	} //}}}

	//{{{ getMinimumSize() method
	/**
	 * Returns the painter's minimum size.
	 */
	@Override
	public Dimension getMinimumSize()
	{
		return getPreferredSize();
	} //}}}

	//{{{ Package-private members

	//{{{ Instance variables
	/* package-private since they are accessed by inner classes and we
	 * want this to be fast */
	TextArea textArea;

	SyntaxStyle[] styles;
	Color caretColor;
	Color selectionColor;
	Color multipleSelectionColor;
	Color lineHighlightColor;
	Color structureHighlightColor;
	Color eolMarkerColor;
	Color wrapGuideColor;

	SyntaxStyle[] foldLineStyle;

	boolean blockCaret;
	boolean thickCaret;
	boolean lineHighlight;
	boolean structureHighlight;
	boolean eolMarkers;
	boolean wrapGuide;
	AntiAlias antiAlias;
	boolean fracFontMetrics;
	RenderingHints renderingHints;
	// should try to use this as little as possible.
	FontMetrics fm;
	//}}}

	//{{{ TextAreaPainter constructor
	/**
	 * Creates a new painter. Do not create instances of this class
	 * directly.
	 */
	TextAreaPainter(TextArea textArea)
	{
		enableEvents(AWTEvent.FOCUS_EVENT_MASK
			| AWTEvent.KEY_EVENT_MASK
			| AWTEvent.MOUSE_EVENT_MASK);

		this.textArea = textArea;
		antiAlias = new AntiAlias(0);
		fonts = new HashMap();
		extensionMgr = new ExtensionManager();

		setAutoscrolls(true);
		setOpaque(true);
		setRequestFocusEnabled(false);
		setDoubleBuffered(false);

		setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));

		fontRenderContext = new FontRenderContext(null,false,false);

		addExtension(LINE_BACKGROUND_LAYER,new PaintLineBackground());
		addExtension(SELECTION_LAYER,new PaintSelection());
		addExtension(WRAP_GUIDE_LAYER,new PaintWrapGuide());
		addExtension(BRACKET_HIGHLIGHT_LAYER,new StructureMatcher
			.Highlight(textArea));
		addExtension(TEXT_LAYER,new PaintText());
		caretExtension = new PaintCaret();
	} //}}}

	//}}}

	//{{{ Private members

	//{{{ Instance variables
	private final ExtensionManager extensionMgr;
	private final PaintCaret caretExtension;
	private FontRenderContext fontRenderContext;
	private final Map fonts;
	//}}}

	private static Object sm_hrgbRender;
	private static Constructor<FontRenderContext> sm_frcConstructor;

	static
	{
		try
		{
			Field f = RenderingHints.class.getField("VALUE_TEXT_ANTIALIAS_LCD_HRGB");
			sm_hrgbRender = f.get(null);
			Class[] fracFontMetricsTypeList = {AffineTransform.class, Object.class, Object.class};
			sm_frcConstructor = FontRenderContext.class.getConstructor(fracFontMetricsTypeList);
		}
		catch (NullPointerException npe) {}
		catch (SecurityException se) {}
		catch (NoSuchFieldException nsfe) {}
		catch (IllegalArgumentException iae) {}
		catch (IllegalAccessException iae) {}
		catch (NoSuchMethodException nsme) {}
	}
	//{{{ updateRenderingHints() method
	private void updateRenderingHints()
	{
		Map<RenderingHints.Key,Object> hints = new HashMap<RenderingHints.Key,Object>();

		hints.put(RenderingHints.KEY_FRACTIONALMETRICS,
			fracFontMetrics ? RenderingHints.VALUE_FRACTIONALMETRICS_ON
				: RenderingHints.VALUE_FRACTIONALMETRICS_OFF);

		if (antiAlias.val() == 0)
		{
			hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
			hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
			fontRenderContext = new FontRenderContext(null, antiAlias.val() > 0, fracFontMetrics);
		}
		/** LCD HRGB mode - works with JRE 1.6 only, which is why we use reflection */
		else if (antiAlias.val() == 2 && sm_hrgbRender != null )
		{
			hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, sm_hrgbRender);
			Object fontRenderHint = fracFontMetrics ?
				RenderingHints.VALUE_FRACTIONALMETRICS_ON :
				RenderingHints.VALUE_FRACTIONALMETRICS_OFF;
			Object[] paramList = {null, sm_hrgbRender, fontRenderHint};
			try
			{
				fontRenderContext = sm_frcConstructor.newInstance(paramList);
			}
			catch (Exception e)
			{
				fontRenderContext = new FontRenderContext(null, antiAlias.val() > 0, fracFontMetrics);
			}
		}
		else /** Standard Antialias Version */
		{
			hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
			hints.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
			hints.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
			fontRenderContext = new FontRenderContext(null, antiAlias.val() > 0, fracFontMetrics);
		}

		renderingHints = new RenderingHints(hints);

	} //}}}

	//}}}

	//{{{ Inner classes

	//{{{ PaintLineBackground class
	private class PaintLineBackground extends TextAreaExtension
	{
		//{{{ shouldPaintLineHighlight() method
		private boolean shouldPaintLineHighlight(int caret, int start, int end)
		{
			if(!isLineHighlightEnabled()
				|| caret < start || caret >= end)
			{
				return false;
			}

			int count = textArea.getSelectionCount();
			if(count == 1)
			{
				Selection s = textArea.getSelection(0);
				return s.getStartLine() == s.getEndLine();
			}
			else
				return count == 0;
		} //}}}

		//{{{ paintValidLine() method
		@Override
		public void paintValidLine(Graphics2D gfx, int screenLine,
			int physicalLine, int start, int end, int y)
		{
			// minimise access$ methods
			TextArea textArea = TextAreaPainter.this.textArea;
			JEditBuffer buffer = textArea.getBuffer();

			//{{{ Paint line highlight and collapsed fold highlight
			boolean collapsedFold =
				physicalLine < buffer.getLineCount() - 1
				&& buffer.isFoldStart(physicalLine)
				&& !textArea.displayManager
				.isLineVisible(physicalLine + 1);

			SyntaxStyle foldLineStyle = null;
			if(collapsedFold)
			{
				int level = buffer.getFoldLevel(physicalLine + 1);
				if(buffer.getFoldHandler() instanceof IndentFoldHandler)
					level = Math.max(1,level / buffer.getIndentSize());
				if(level > 3)
					level = 0;
				foldLineStyle = TextAreaPainter.this.foldLineStyle[level];
			}

			int caret = textArea.getCaretPosition();
			boolean paintLineHighlight = shouldPaintLineHighlight(
				caret,start,end);

			Color bgColor;
			if(paintLineHighlight)
				bgColor = lineHighlightColor;
			else if(collapsedFold)
			{
				bgColor = foldLineStyle.getBackgroundColor();
				if(bgColor == null)
					bgColor = getBackground();
			}
			else
				bgColor = getBackground();

			if(paintLineHighlight || collapsedFold)
			{
				gfx.setColor(bgColor);
				gfx.fillRect(0,y,getWidth(),fm.getHeight());
			} //}}}

			//{{{ Paint token backgrounds
			ChunkCache.LineInfo lineInfo = textArea.chunkCache
				.getLineInfo(screenLine);

			if(lineInfo.chunks != null)
			{
				float baseLine = y + fm.getHeight()
					- (fm.getLeading()+1) - fm.getDescent();
				Chunk.paintChunkBackgrounds(
					lineInfo.chunks,gfx,
					textArea.getHorizontalOffset(),
					baseLine);
			} //}}}
		} //}}}
	} //}}}

	//{{{ PaintSelection class
	private class PaintSelection extends TextAreaExtension
	{
		//{{{ paintValidLine() method
		@Override
		public void paintValidLine(Graphics2D gfx, int screenLine,
			int physicalLine, int start, int end, int y)
		{
			if(textArea.getSelectionCount() == 0)
				return;

			gfx.setColor(textArea.isMultipleSelectionEnabled()
				? getMultipleSelectionColor()
				: getSelectionColor());

			Iterator<Selection> iter = textArea.getSelectionIterator();
			while(iter.hasNext())
			{
				Selection s = iter.next();
				paintSelection(gfx,screenLine,physicalLine,y,s);
			}
		} //}}}

		//{{{ paintSelection() method
		private void paintSelection(Graphics2D gfx, int screenLine,
			int physicalLine, int y, Selection s)
		{
			int[] selectionStartAndEnd
				= textArea.selectionManager
				.getSelectionStartAndEnd(
				screenLine,physicalLine,s);
			if(selectionStartAndEnd == null)
				return;

			int x1 = selectionStartAndEnd[0];
			int x2 = selectionStartAndEnd[1];

			gfx.fillRect(x1,y,x2 - x1,fm.getHeight());
		} //}}}
	} //}}}

	//{{{ PaintWrapGuide class
	private class PaintWrapGuide extends TextAreaExtension
	{
		@Override
		public void paintScreenLineRange(Graphics2D gfx, int firstLine,
			int lastLine, int[] physicalLines, int[] start,
			int[] end, int y, int lineHeight)
		{
			if(textArea.wrapMargin != 0
				&& !textArea.wrapToWidth
				&& isWrapGuidePainted())
			{
				gfx.setColor(getWrapGuideColor());
				int x = textArea.getHorizontalOffset()
					+ textArea.wrapMargin;
				gfx.drawLine(x,y,x,y + (lastLine - firstLine
					+ 1) * lineHeight);
			}
		}

		@Override
		public String getToolTipText(int x, int y)
		{
			if(textArea.wrapMargin != 0
				&& !textArea.wrapToWidth
				&& isWrapGuidePainted())
			{
				int wrapGuidePos = textArea.wrapMargin
					+ textArea.getHorizontalOffset();
				if(Math.abs(x - wrapGuidePos) < 5)
				{
					return String.valueOf(textArea.getBuffer()
						.getProperty("maxLineLen"));
				}
			}

			return null;
		}
	} //}}}

	//{{{ PaintText class
	private class PaintText extends TextAreaExtension
	{
		@Override
		public void paintValidLine(Graphics2D gfx, int screenLine,
			int physicalLine, int start, int end, int y)
		{
			ChunkCache.LineInfo lineInfo = textArea.chunkCache
				.getLineInfo(screenLine);

			Font defaultFont = getFont();
			Color defaultColor = getForeground();

			gfx.setFont(defaultFont);
			gfx.setColor(defaultColor);

			int x = textArea.getHorizontalOffset();
			int originalX = x;

			float baseLine = y + fm.getHeight()
				- (fm.getLeading()+1) - fm.getDescent();

			if(lineInfo.chunks != null)
			{
				x += Chunk.paintChunkList(lineInfo.chunks,
					gfx,textArea.getHorizontalOffset(),
					baseLine,!Debug.DISABLE_GLYPH_VECTOR);
			}

			JEditBuffer buffer = textArea.getBuffer();

			if(!lineInfo.lastSubregion)
			{
				gfx.setFont(defaultFont);
				gfx.setColor(eolMarkerColor);
				gfx.drawString(":",Math.max(x,
					textArea.getHorizontalOffset()
					+ textArea.wrapMargin + textArea.charWidth),
					baseLine);
				x += textArea.charWidth;
			}
			else if(physicalLine < buffer.getLineCount() - 1
				&& buffer.isFoldStart(physicalLine)
				&& !textArea.displayManager
				.isLineVisible(physicalLine + 1))
			{
				int level = buffer.getFoldLevel(physicalLine + 1);
				if(buffer.getFoldHandler() instanceof IndentFoldHandler)
					level = Math.max(1,level / buffer.getIndentSize());
				if(level > 3)
					level = 0;
				SyntaxStyle foldLineStyle = TextAreaPainter.this.foldLineStyle[level];

				Font font = foldLineStyle.getFont();
				gfx.setFont(font);
				gfx.setColor(foldLineStyle.getForegroundColor());

				int nextLine;
				int nextScreenLine = screenLine + 1;
				if(nextScreenLine < textArea.getVisibleLines())
				{
					nextLine = textArea.chunkCache.getLineInfo(nextScreenLine)
						.physicalLine;
				}
				else
				{
					nextLine = textArea.displayManager
						.getNextVisibleLine(physicalLine);
				}

				if(nextLine == -1)
					nextLine = textArea.getLineCount();

				int count = nextLine - physicalLine - 1;
				String str = " [" + count + " lines]";

				float width = getStringWidth(str);

				gfx.drawString(str,x,baseLine);
				x += width;
			}
			else if(eolMarkers)
			{
				gfx.setFont(defaultFont);
				gfx.setColor(eolMarkerColor);
				gfx.drawString(".",x,baseLine);
				x += textArea.charWidth;
			}

			lineInfo.width = x - originalX;
		}
	} //}}}

	//{{{ PaintCaret class
	private class PaintCaret extends TextAreaExtension
	{
		@Override
		public void paintValidLine(Graphics2D gfx, int screenLine,
			int physicalLine, int start, int end, int y)
		{
			if(!textArea.isCaretVisible())
				return;

			int caret = textArea.getCaretPosition();
			if(caret < start || caret >= end)
				return;

			int offset = caret - textArea.getLineStartOffset(physicalLine);
			textArea.offsetToXY(physicalLine,
					    offset, textArea.offsetXY);
			int caretX = textArea.offsetXY.x;
			int lineHeight = fm.getHeight();

			gfx.setColor(caretColor);

			if(textArea.isOverwriteEnabled())
			{
				gfx.drawLine(caretX,y + lineHeight - 1,
					     caretX + textArea.charWidth,
					     y + lineHeight - 1);
			}
			else if(blockCaret)
				gfx.drawRect(caretX,y,textArea.charWidth - 1,
					     lineHeight - 1);
			else
			{
				if (thickCaret)
					gfx.drawRect(caretX, y,
						1, lineHeight - 1);
				else
					gfx.drawLine(caretX,y,
						caretX,y + lineHeight - 1);

FUNC_DECL IF IF PARAM PARAM PARAM PARAM ASSIGN CALL CALL CALL CALL BINARY_OP LITERAL_ANCHOR CALL CALL RETURN BINARY_OP BINARY_OP CALL BINARY_OP BINARY_OP CALL BINARY_OP BINARY_OP CALL CALL

FUNC_DECL PARAM CALL ASSIGN ASSIGN CALL CALL CALL LITERAL_ANCHOR CALL

FUNC_DECL IF PARAM BINARY_OP ASSIGN CALL LITERAL_ANCHOR CALL CALL

FUNC_DECL IF IF PARAM BINARY_OP CALL ASSIGN CALL LITERAL_ANCHOR CALL CALL CALL CALL

FUNC_DECL RETURN IF PARAM CALL RETURN CALL CALL CALL LITERAL_ANCHOR CALL

FUNC_DECL IF PARAM BINARY_OP CALL ASSIGN CALL CALL BINARY_OP CALL CALL LITERAL_ANCHOR CALL

FUNC_DECL IF PARAM RETURN BINARY_OP RETURN BINARY_OP LITERAL_ANCHOR CALL CALL CALL CALL CALL

FUNC_DECL PARAM ASSIGN CALL CALL CALL CALL CALL ASSIGN CALL CALL CALL CALL CALL ASSIGN ASSIGN ASSIGN ASSIGN CALL CALL CALL CALL CALL CALL CALL CALL CALL LITERAL_ANCHOR LITERAL_ANCHOR LITERAL_ANCHOR LITERAL_ANCHOR CALL CALL CALL BINARY_OP LITERAL_ANCHOR LITERAL_ANCHOR LITERAL_ANCHOR LITERAL_ANCHOR BINARY_OP


public abstract class AbstractTranslet implements Translet {

    public String  _version = "1.0";
    public String  _method = null;
    public String  _encoding = "UTF-8";
    public boolean _omitHeader = false;
    public String  _standalone = null;
    public String  _doctypePublic = null;
    public String  _doctypeSystem = null;
    public boolean _indent = false;
    public String  _mediaType = null;
    public Hashtable _cdata = null;

    protected String[] namesArray;
    protected String[] namespaceArray;

    protected StringValueHandler stringValueHandler = new StringValueHandler();

    private final static String EMPTYSTRING = "";

    /************************************************************************
     * Debugging
     ************************************************************************/
    public void printInternalState() {
	System.out.println("-------------------------------------");
	System.out.println("AbstractTranslet this = " + this);
	System.out.println("pbase = " + pbase);
	System.out.println("vframe = " + pframe);
	System.out.println("paramsStack.size() = " + paramsStack.size());
	System.out.println("namesArray.size = " + namesArray.length);
	System.out.println("namespaceArray.size = " + namespaceArray.length);
	System.out.println("");
	System.out.println("Total memory = " + Runtime.getRuntime().totalMemory());
    }

    /**
     * Wrap the initial input DOM in a dom adapter. This adapter is wrapped in
     * a DOM multiplexer if the document() function is used (handled by compiled
     * code in the translet - see compiler/Stylesheet.compileTransform()).
     */
    public final DOMAdapter makeDOMAdapter(DOM dom)
	throws TransletException {
	if (dom instanceof DOMImpl)
	    return new DOMAdapter((DOMImpl)dom, namesArray, namespaceArray);
	BasisLibrary.runTimeError(BasisLibrary.DOM_ADAPTER_INIT_ERR);
	return null;
    }

    /************************************************************************
     * Parameter handling
     ************************************************************************/

    protected int pbase = 0, pframe = 0;
    protected ArrayList paramsStack = new ArrayList();

    /**
     * Push a new parameter frame.
     */
    public final void pushParamFrame() {
	paramsStack.add(pframe, new Integer(pbase));
	pbase = ++pframe;
    }

    /**
     * Pop the topmost parameter frame.
     */
    public final void popParamFrame() {
	if (pbase > 0) {
	    final int oldpbase = ((Integer)paramsStack.get(--pbase)).intValue();
	    for (int i = pframe - 1; i >= pbase; i--) {
		paramsStack.remove(i);
	    }
	    pframe = pbase; pbase = oldpbase;
	}
    }

    /**
     * Add a new global parameter if not already in the current frame.
     */
    public final Object addParameter(String name, Object value) {
	name = BasisLibrary.replace(name, ".-",
				    new String[] { "$dot$", "$dash$" });
	return addParameter(name, value, false);
    }

    /**
     * Add a new global or local parameter if not already in the current frame.
     * The 'isDefault' parameter is set to true if the value passed is the
     * default value from the <xsl:parameter> element's select attribute or
     * element body.
     */
    public final Object addParameter(String name, Object value,
	boolean isDefault)
    {
	for (int i = pframe - 1; i >= pbase; i--) {
	    final Parameter param = (Parameter) paramsStack.get(i);

	    if (param._name.equals(name)) {
		if (param._isDefault || !isDefault) {
		    param._value = value;
		    param._isDefault = isDefault;
		    return value;
		}
		return param._value;
	    }
	}

	paramsStack.add(pframe++, new Parameter(name, value, isDefault));
	return value;
    }

    /**
     * Clears the parameter stack.
     */
    public void clearParameters() {
	pbase = pframe = 0;
	paramsStack.clear();
    }

    /**
     * Get the value of a parameter from the current frame or
     * <tt>null</tt> if undefined.
     */
    public final Object getParameter(String name) {
	for (int i = pframe - 1; i >= pbase; i--) {
	    final Parameter param = (Parameter)paramsStack.get(i);
	    if (param._name.equals(name)) return param._value;
	}
	return null;
    }

    /************************************************************************
     * Message handling - implementation of <xsl:message>
     ************************************************************************/

    private MessageHandler _msgHandler = null;

    /**
     * Set the translet's message handler - must implement MessageHandler
     */
    public final void setMessageHandler(MessageHandler handler) {
	_msgHandler = handler;
    }

    /**
     * Pass a message to the message handler - used by Message class.
     */
    public final void displayMessage(String msg) {
	if (_msgHandler == null) {
            System.err.println(msg);
	}
	else {
	    _msgHandler.displayMessage(msg);
	}
    }

    /************************************************************************
     * Decimal number format symbol handling
     ************************************************************************/

    public Hashtable _formatSymbols = null;

    /**
     * Adds a DecimalFormat object to the _formatSymbols hashtable.
     * The entry is created with the input DecimalFormatSymbols.
     */
    public void addDecimalFormat(String name, DecimalFormatSymbols symbols) {
	if (_formatSymbols == null) _formatSymbols = new Hashtable();

	if (name == null) name = EMPTYSTRING;

	final DecimalFormat df = new DecimalFormat();
	if (symbols != null) {
	    df.setDecimalFormatSymbols(symbols);
	}
	_formatSymbols.put(name, df);
    }

    /**
     * Retrieves a named DecimalFormat object from _formatSymbols hashtable.
     */
    public final DecimalFormat getDecimalFormat(String name) {

	if (_formatSymbols != null) {
	    if (name == null) name = EMPTYSTRING;

	    DecimalFormat df = (DecimalFormat)_formatSymbols.get(name);
	    if (df == null) df = (DecimalFormat)_formatSymbols.get(EMPTYSTRING);
	    return df;
	}
	return(null);
    }

    /************************************************************************
     * Unparsed entity URI handling - implements unparsed-entity-uri()
     ************************************************************************/

    public Hashtable _unparsedEntities = null;

    /**
     * Get the value of an unparsed entity URI.
     * This method is used by the compiler/UnparsedEntityUriCall class.
     */
    public final String getUnparsedEntity(String name) {
	final String uri = (String)_unparsedEntities.get(name);
	return uri == null ? EMPTYSTRING : uri;
    }

    /**
     * Add an unparsed entity URI. The URI/value pairs are passed from the
     * DOM builder to the translet.
     */
    public final void addUnparsedEntity(String name, String uri) {
	if (_unparsedEntities == null)
	    _unparsedEntities = new Hashtable();
	if (_unparsedEntities.containsKey(name) == false)
	    _unparsedEntities.put(name, uri);
    }

    /**
     * Add an unparsed entity URI. The URI/value pairs are passed from the
     * DOM builder to the translet.
     */
    public final void setUnparsedEntityURIs(Hashtable table) {
	if (_unparsedEntities == null)
	    _unparsedEntities = table;
	else {
	    Enumeration keys = table.keys();
	    while (keys.hasMoreElements()) {
		String name = (String)keys.nextElement();
		_unparsedEntities.put(name,table.get(name));
	    }
	}
    }

    /**
     * The DTD monitor used by the DOM builder scans the input document DTD
     * for unparsed entity URIs. These are passed to the translet using
     * this method.
     */
    public final void setDTDMonitor(DTDMonitor monitor) {
	setUnparsedEntityURIs(monitor.getUnparsedEntityURIs());
    }

    /************************************************************************
     * Index(es) for <xsl:key> / key() / id()
     ************************************************************************/

    private Hashtable _keyIndexes = null;
    private KeyIndex  _emptyKeyIndex = new KeyIndex(1);
    private int       _indexSize = 0;

    /**
     * This method is used to pass the largest DOM size to the translet.
     * Needed to make sure that the translet can index the whole DOM.
     */
    public void setIndexSize(int size) {
	if (size > _indexSize) _indexSize = size;
    }

    /**
     * Creates a KeyIndex object of the desired size - don't want to resize!!!
     */
    public KeyIndex createKeyIndex() {
	return(new KeyIndex(_indexSize));
    }

    /**
     * Adds a value to a key/id index
     *   @name is the name of the index (the key or ##id)
     *   @node is the node id of the node to insert
     *   @value is the value that will look up the node in the given index
     */
    public void buildKeyIndex(String name, int node, Object value) {
	if (_keyIndexes == null) _keyIndexes = new Hashtable();

	KeyIndex index = (KeyIndex)_keyIndexes.get(name);
	if (index == null) {
	    _keyIndexes.put(name, index = new KeyIndex(_indexSize));
	}
	index.add(value, node);
    }

    /**
     * Returns the index for a given key (or id).
     * The index implements our internal iterator interface
     */
    public KeyIndex getKeyIndex(String name) {
	if (_keyIndexes == null) return(_emptyKeyIndex);

	final KeyIndex index = (KeyIndex)_keyIndexes.get(name);

	if (index == null) return(_emptyKeyIndex);

	return(index);
    }

    /**
     * This method builds key indexes - it is overridden in the compiled
     * translet in cases where the <xsl:key> element is used
     */
    public void buildKeys(DOM document, NodeIterator iterator,
			  TransletOutputHandler handler,
			  int root) throws TransletException {
    }

    /************************************************************************
     * DOM cache handling
     ************************************************************************/

    private DOMCache _domCache = null;

    /**
     * Sets the DOM cache used for additional documents loaded using the
     * document() function.
     */
    public void setDOMCache(DOMCache cache) {
	_domCache = cache;
    }

    /**
     * Returns the DOM cache used for this translet. Used by the LoadDocument
     * class (if present) when the document() function is used.
     */
    public DOMCache getDOMCache() {
	return(_domCache);
    }

    /************************************************************************
     * Multiple output document extension.
     * See compiler/TransletOutput for actual implementation.
     ************************************************************************/

    public TransletOutputHandler openOutputHandler(String filename)
	throws TransletException
    {
	try {
	    final TransletOutputHandlerFactory factory
		= TransletOutputHandlerFactory.newInstance();

	    factory.setEncoding(_encoding);
	    factory.setOutputMethod(_method);
	    factory.setWriter(new FileWriter(filename));
	    factory.setOutputType(TransletOutputHandlerFactory.STREAM);

	    final TransletOutputHandler handler
		= factory.getTransletOutputHandler();

	    transferOutputSettings(handler);
	    handler.startDocument();
	    return handler;
	}
	catch (Exception e) {
	    throw new TransletException(e);
	}
    }

    public void closeOutputHandler(TransletOutputHandler handler) {
	try {
	    handler.endDocument();
	    handler.close();
	}
	catch (Exception e) {
	}
    }

    /************************************************************************
     * Native API transformation methods - _NOT_ JAXP/TrAX
     ************************************************************************/

    /**
     * Main transform() method - this is overridden by the compiled translet
     */
    public abstract void transform(DOM document, NodeIterator iterator,
				   TransletOutputHandler handler)
	throws TransletException;

    /**
     * Calls transform() with a given output handler
     */
    public final void transform(DOM document, TransletOutputHandler handler)
	throws TransletException {
	transform(document, document.getIterator(), handler);
    }

    /**
     * Used by some compiled code as a shortcut for passing strings to the
     * output handler
     */
    public final void characters(final String string,
				 TransletOutputHandler handler)
	throws TransletException {
	final int length = string.length();
	handler.characters(string.toCharArray(), 0, length);
    }

    /**
     * Add's a name of an element whose text contents should be output as CDATA
     */
    public void addCdataElement(String name) {
	if (_cdata == null) _cdata = new Hashtable();
	_cdata.put(name, name);
    }

    /**
     * Transfer the output settings to the output post-processor
     */
    protected void transferOutputSettings(TransletOutputHandler handler) {
	if (_method != null) {
	    if (_method.equals("xml")) {
	        if (_standalone != null) {
		    handler.setStandalone(_standalone);
		}
		if (_omitHeader) {
		    handler.omitHeader(true);
		}
		handler.setCdataElements(_cdata);
		if (_version != null) {
		    handler.setVersion(_version);
		}
		handler.setIndent(_indent);
		if (_doctypeSystem != null) {
		    handler.setDoctype(_doctypeSystem, _doctypePublic);
		}
	    }
	    else if (_method.equals("html")) {
		handler.setIndent(_indent);
		handler.setDoctype(_doctypeSystem, _doctypePublic);
		if (_mediaType != null) {
		    handler.setMediaType(_mediaType);
		}
	    }
	}
	else {
	    handler.setCdataElements(_cdata);
	    if (_version != null) {
		handler.setVersion(_version);
	    }
	    if (_standalone != null) {
		handler.setStandalone(_standalone);
	    }
	    if (_omitHeader) {
		handler.omitHeader(true);
	    }
	    handler.setIndent(_indent);
	    handler.setDoctype(_doctypeSystem, _doctypePublic);
	}
    }

    private Hashtable _auxClasses = null;

    public void addAuxiliaryClass(Class auxClass) {
	if (_auxClasses == null) _auxClasses = new Hashtable();
	_auxClasses.put(auxClass.getName(), auxClass);
    }

    public Class getAuxiliaryClass(String className) {
	if (_auxClasses == null) return null;
	return((Class)_auxClasses.get(className));
    }

    public String[] getNamesArray() {
	return namesArray;
    }
    public String[] getNamespaceArray() {
	return namespaceArray;

FUNC_DECL RETURN IF PARAM LITERAL_ANCHOR CALL RETURN BINARY_OP CALL

FUNC_DECL RETURN LOOP PARAM PARAM PARAM CALL BINARY_OP CALL IF VAR_DECL CALL BINARY_OP RETURN IF LITERAL_ANCHOR BINARY_OP CALL RETURN ASSIGN ASSIGN

FUNC_DECL IF VAR_DECL IF PARAM PARAM PARAM CALL BINARY_OP BINARY_OP LITERAL_ANCHOR ASSIGN LITERAL_ANCHOR CALL CALL CALL ASSIGN CALL

FUNC_DECL TRY PARAM CATCH RETURN VAR_DECL VAR_DECL THROW_RAISE CALL CALL CALL CALL CALL CALL CALL CALL CALL CALL

FUNC_DECL IF PARAM CALL BINARY_OP CALL ASSIGN LITERAL_ANCHOR CALL


public class JmsConfiguration implements Cloneable {
    protected static final String TRANSACTED = "TRANSACTED";
    protected static final String CLIENT_ACKNOWLEDGE = "CLIENT_ACKNOWLEDGE";
    protected static final String AUTO_ACKNOWLEDGE = "AUTO_ACKNOWLEDGE";
    protected static final String DUPS_OK_ACKNOWLEDGE = "DUPS_OK_ACKNOWLEDGE";

    private JmsOperations jmsOperations;
    private DestinationResolver destinationResolver;
    private ConnectionFactory connectionFactory;
    private ConnectionFactory templateConnectionFactory;
    private ConnectionFactory listenerConnectionFactory;
    private int acknowledgementMode = -1;
    private String acknowledgementModeName = null;
    private ExceptionListener exceptionListener;
    private ConsumerType consumerType = ConsumerType.Default;
    private boolean autoStartup = true;
    private boolean acceptMessagesWhileStopping;
    private String clientId;
    private String durableSubscriptionName;
    private boolean subscriptionDurable;
    private boolean exposeListenerSession = true;
    private TaskExecutor taskExecutor;
    private boolean pubSubNoLocal;
    private int concurrentConsumers = 1;
    private int maxMessagesPerTask = 1;
    private ServerSessionFactory serverSessionFactory;
    private int cacheLevel = -1;
    private String cacheLevelName = "CACHE_CONNECTION";
    private long recoveryInterval = -1;
    private long receiveTimeout = -1;
    private int idleTaskExecutionLimit = 1;
    private int maxConcurrentConsumers = 1;
    private boolean useVersion102;
    private boolean explicitQosEnabled;
    private boolean deliveryPersistent = true;
    private long timeToLive = -1;
    private MessageConverter messageConverter;
    private boolean messageIdEnabled = true;
    private boolean messageTimestampEnabled = true;
    private int priority = -1;
    private boolean transacted;
    private PlatformTransactionManager transactionManager;
    private String transactionName;
    private int transactionTimeout = -1;
    private boolean preserveMessageQos;

    public JmsConfiguration() {
    }

    public JmsConfiguration(ConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
    }

    /**
     * Returns a copy of this configuration
     */
    public JmsConfiguration copy() {
        try {
            return (JmsConfiguration)clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeCamelException(e);
        }
    }

    public JmsOperations createJmsOperations(boolean pubSubDomain, String destination) {

        if ( jmsOperations !=null ) {
            return jmsOperations;
        }

        ConnectionFactory factory = getTemplateConnectionFactory();

        JmsTemplate template = useVersion102 ? new JmsTemplate102(factory, pubSubDomain) {
            /**
             * Override so we can support preserving the Qos settings that have
             * been set on the message.
             */
            @Override
            protected void doSend(MessageProducer producer, Message message) throws JMSException {
                if (preserveMessageQos) {
                    long ttl = message.getJMSExpiration();
                    if (ttl != 0) {
                        ttl = ttl - System.currentTimeMillis();
                        if (ttl <= 0) {
                            ttl = 1;
                        }
                    }
                    if (isPubSubDomain()) {
                        ((TopicPublisher)producer).publish(message, message.getJMSDeliveryMode(), message.getJMSPriority(), ttl);
                    } else {
                        ((QueueSender)producer).send(message, message.getJMSDeliveryMode(), message.getJMSPriority(), ttl);
                    }
                } else {
                    super.doSend(producer, message);
                }
            }
        } : new JmsTemplate(factory) {
            /**
             * Override so we can support preserving the Qos settings that have
             * been set on the message.
             */
            @Override
            protected void doSend(MessageProducer producer, Message message) throws JMSException {
                if (preserveMessageQos) {
                    long ttl = message.getJMSExpiration();
                    if (ttl != 0) {
                        ttl = ttl - System.currentTimeMillis();
                        if (ttl <= 0) {
                            ttl = 1;
                        }
                    }
                    producer.send(message, message.getJMSDeliveryMode(), message.getJMSPriority(), ttl);
                } else {
                    super.doSend(producer, message);
                }
            }
        };

        template.setPubSubDomain(pubSubDomain);
        if( destinationResolver!=null ) {
            template.setDestinationResolver(destinationResolver);
        }
        template.setDefaultDestinationName(destination);

        template.setExplicitQosEnabled(explicitQosEnabled);
        template.setDeliveryPersistent(deliveryPersistent);
        if (messageConverter != null) {
            template.setMessageConverter(messageConverter);
        }
        template.setMessageIdEnabled(messageIdEnabled);
        template.setMessageTimestampEnabled(messageTimestampEnabled);
        if (priority >= 0) {
            template.setPriority(priority);
        }
        template.setPubSubNoLocal(pubSubNoLocal);
        if (receiveTimeout >= 0) {
            template.setReceiveTimeout(receiveTimeout);
        }
        if (timeToLive >= 0) {
            template.setTimeToLive(timeToLive);
        }

        template.setSessionTransacted(transacted);
        if (transacted) {
            template.setSessionAcknowledgeMode(Session.SESSION_TRANSACTED);
        }
        else {
            if (acknowledgementMode >= 0) {
                template.setSessionAcknowledgeMode(acknowledgementMode);
            } else if (acknowledgementModeName != null) {
                template.setSessionAcknowledgeModeName(acknowledgementModeName);
            }
        }
        return template;
    }

    public AbstractMessageListenerContainer createMessageListenerContainer() {
        AbstractMessageListenerContainer container = chooseMessageListenerContainerImplementation();
        configureMessageListenerContainer(container);
        return container;
    }

    protected void configureMessageListenerContainer(AbstractMessageListenerContainer container) {
        container.setConnectionFactory(getListenerConnectionFactory());
        if( destinationResolver!=null ) {
            container.setDestinationResolver(destinationResolver);
        }
        if (autoStartup) {
            container.setAutoStartup(true);
        }
        if (clientId != null) {
            container.setClientId(clientId);
        }
        container.setSubscriptionDurable(subscriptionDurable);
        if (durableSubscriptionName != null) {
            container.setDurableSubscriptionName(durableSubscriptionName);
        }

        if (durableSubscriptionName != null && clientId != null) {
            container.setSubscriptionDurable(true);
        }

        if (exceptionListener != null) {
            container.setExceptionListener(exceptionListener);
        }

        container.setAcceptMessagesWhileStopping(acceptMessagesWhileStopping);
        container.setExposeListenerSession(exposeListenerSession);
        container.setSessionTransacted(transacted);
        if (transacted) {
            container.setSessionAcknowledgeMode(Session.SESSION_TRANSACTED);
        }
        else {
            if (acknowledgementMode >= 0) {
                container.setSessionAcknowledgeMode(acknowledgementMode);
            } else if (acknowledgementModeName != null) {
                container.setSessionAcknowledgeModeName(acknowledgementModeName);
            }
        }

        if (container instanceof DefaultMessageListenerContainer) {
            DefaultMessageListenerContainer listenerContainer = (DefaultMessageListenerContainer)container;
            if (concurrentConsumers >= 0) {
                listenerContainer.setConcurrentConsumers(concurrentConsumers);
            }

            if (cacheLevel >= 0) {
                listenerContainer.setCacheLevel(cacheLevel);
            } else if (cacheLevelName != null) {
                listenerContainer.setCacheLevelName(cacheLevelName);
            } else {
                listenerContainer.setCacheLevel(DefaultMessageListenerContainer.CACHE_CONSUMER);
            }

            if (idleTaskExecutionLimit >= 0) {
                listenerContainer.setIdleTaskExecutionLimit(idleTaskExecutionLimit);
            }
            if (maxConcurrentConsumers >= 0) {
                listenerContainer.setMaxConcurrentConsumers(maxConcurrentConsumers);
            }
            if (maxMessagesPerTask >= 0) {
                listenerContainer.setMaxMessagesPerTask(maxMessagesPerTask);
            }
            listenerContainer.setPubSubNoLocal(pubSubNoLocal);
            if (receiveTimeout >= 0) {
                listenerContainer.setReceiveTimeout(receiveTimeout);
            }
            if (recoveryInterval >= 0) {
                listenerContainer.setRecoveryInterval(recoveryInterval);
            }
            if (taskExecutor != null) {
                listenerContainer.setTaskExecutor(taskExecutor);
            }
            if (transactionManager != null) {
                listenerContainer.setTransactionManager(transactionManager);
            }
            else if (transacted) {
                throw new IllegalArgumentException("Property transacted is enabled but a transactionManager was not injected!");
            }
            if (transactionName != null) {
                listenerContainer.setTransactionName(transactionName);
            }
            if (transactionTimeout >= 0) {
                listenerContainer.setTransactionTimeout(transactionTimeout);
            }
        } else if (container instanceof ServerSessionMessageListenerContainer) {
            ServerSessionMessageListenerContainer listenerContainer = (ServerSessionMessageListenerContainer)container;
            if (maxMessagesPerTask >= 0) {
                listenerContainer.setMaxMessagesPerTask(maxMessagesPerTask);
            }
            if (serverSessionFactory != null) {
                listenerContainer.setServerSessionFactory(serverSessionFactory);
            }
        } else if (container instanceof SimpleMessageListenerContainer) {
            SimpleMessageListenerContainer listenerContainer = (SimpleMessageListenerContainer)container;
            if (concurrentConsumers >= 0) {
                listenerContainer.setConcurrentConsumers(concurrentConsumers);
            }
            listenerContainer.setPubSubNoLocal(pubSubNoLocal);
            if (taskExecutor != null) {
                listenerContainer.setTaskExecutor(taskExecutor);
            }
        }
    }

    public ConnectionFactory getConnectionFactory() {
        if (connectionFactory == null) {
            connectionFactory = createConnectionFactory();
        }
        return connectionFactory;
    }

    /**
     * Sets the default connection factory to be used if a connection factory is
     * not specified for either
     * {@link #setTemplateConnectionFactory(ConnectionFactory)} or
     * {@link #setListenerConnectionFactory(ConnectionFactory)}
     *
     * @param connectionFactory the default connection factory to use
     */
    public void setConnectionFactory(ConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
    }

    public ConnectionFactory getListenerConnectionFactory() {
        if (listenerConnectionFactory == null) {
            listenerConnectionFactory = createListenerConnectionFactory();
        }
        return listenerConnectionFactory;
    }

    /**
     * Sets the connection factory to be used for consuming messages via the
     * {@link #createMessageListenerContainer()}
     *
     * @param listenerConnectionFactory the connection factory to use for
     *                consuming messages
     */
    public void setListenerConnectionFactory(ConnectionFactory listenerConnectionFactory) {
        this.listenerConnectionFactory = listenerConnectionFactory;
    }

    public ConnectionFactory getTemplateConnectionFactory() {
        if (templateConnectionFactory == null) {
            templateConnectionFactory = createTemplateConnectionFactory();
        }
        return templateConnectionFactory;
    }

    /**
     * Sets the connection factory to be used for sending messages via the
     * {@link JmsTemplate} via {@link #createJmsOperations(boolean, String)}
     *
     * @param templateConnectionFactory the connection factory for sending
     *                messages
     */
    public void setTemplateConnectionFactory(ConnectionFactory templateConnectionFactory) {
        this.templateConnectionFactory = templateConnectionFactory;
    }

    public boolean isUseVersion102() {
        return useVersion102;
    }

    public void setUseVersion102(boolean useVersion102) {
        this.useVersion102 = useVersion102;
    }

    public boolean isAutoStartup() {
        return autoStartup;
    }

    public void setAutoStartup(boolean autoStartup) {
        this.autoStartup = autoStartup;
    }

    public boolean isAcceptMessagesWhileStopping() {
        return acceptMessagesWhileStopping;
    }

    public void setAcceptMessagesWhileStopping(boolean acceptMessagesWhileStopping) {
        this.acceptMessagesWhileStopping = acceptMessagesWhileStopping;
    }

    public String getClientId() {
        return clientId;
    }

    public void setClientId(String consumerClientId) {
        this.clientId = consumerClientId;
    }

    public String getDurableSubscriptionName() {
        return durableSubscriptionName;
    }

    public void setDurableSubscriptionName(String durableSubscriptionName) {
        this.durableSubscriptionName = durableSubscriptionName;
    }

    public ExceptionListener getExceptionListener() {
        return exceptionListener;
    }

    public void setExceptionListener(ExceptionListener exceptionListener) {
        this.exceptionListener = exceptionListener;
    }

    public boolean isSubscriptionDurable() {
        return subscriptionDurable;
    }

    public void setSubscriptionDurable(boolean subscriptionDurable) {
        this.subscriptionDurable = subscriptionDurable;
    }

    public String getAcknowledgementModeName() {
        return acknowledgementModeName;
    }

    public void setAcknowledgementModeName(String consumerAcknowledgementMode) {
        this.acknowledgementModeName = consumerAcknowledgementMode;
        this.acknowledgementMode = -1;
    }

    public boolean isExposeListenerSession() {
        return exposeListenerSession;
    }

    public void setExposeListenerSession(boolean exposeListenerSession) {
        this.exposeListenerSession = exposeListenerSession;
    }

    public TaskExecutor getTaskExecutor() {
        return taskExecutor;
    }

    public void setTaskExecutor(TaskExecutor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }

    public boolean isPubSubNoLocal() {
        return pubSubNoLocal;
    }

    public void setPubSubNoLocal(boolean pubSubNoLocal) {
        this.pubSubNoLocal = pubSubNoLocal;
    }

    public int getConcurrentConsumers() {
        return concurrentConsumers;
    }

    public void setConcurrentConsumers(int concurrentConsumers) {
        this.concurrentConsumers = concurrentConsumers;
    }

    public int getMaxMessagesPerTask() {
        return maxMessagesPerTask;
    }

    public void setMaxMessagesPerTask(int maxMessagesPerTask) {
        this.maxMessagesPerTask = maxMessagesPerTask;
    }

    public ServerSessionFactory getServerSessionFactory() {
        return serverSessionFactory;
    }

    public void setServerSessionFactory(ServerSessionFactory serverSessionFactory) {
        this.serverSessionFactory = serverSessionFactory;
    }

    public int getCacheLevel() {
        return cacheLevel;
    }

    public void setCacheLevel(int cacheLevel) {
        this.cacheLevel = cacheLevel;
    }

    public String getCacheLevelName() {
        return cacheLevelName;
    }

    public void setCacheLevelName(String cacheName) {
        this.cacheLevelName = cacheName;
    }

    public long getRecoveryInterval() {
        return recoveryInterval;
    }

    public void setRecoveryInterval(long recoveryInterval) {
        this.recoveryInterval = recoveryInterval;
    }

    public long getReceiveTimeout() {
        return receiveTimeout;
    }

    public void setReceiveTimeout(long receiveTimeout) {
        this.receiveTimeout = receiveTimeout;
    }

    public PlatformTransactionManager getTransactionManager() {
        return transactionManager;
    }

    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    public String getTransactionName() {
        return transactionName;
    }

    public void setTransactionName(String transactionName) {
        this.transactionName = transactionName;
    }

    public int getTransactionTimeout() {
        return transactionTimeout;
    }

    public void setTransactionTimeout(int transactionTimeout) {
        this.transactionTimeout = transactionTimeout;
    }

    public int getIdleTaskExecutionLimit() {
        return idleTaskExecutionLimit;
    }

    public void setIdleTaskExecutionLimit(int idleTaskExecutionLimit) {
        this.idleTaskExecutionLimit = idleTaskExecutionLimit;
    }

    public int getMaxConcurrentConsumers() {
        return maxConcurrentConsumers;
    }

    public void setMaxConcurrentConsumers(int maxConcurrentConsumers) {
        this.maxConcurrentConsumers = maxConcurrentConsumers;
    }

    public boolean isExplicitQosEnabled() {
        return explicitQosEnabled;
    }

    public void setExplicitQosEnabled(boolean explicitQosEnabled) {
        this.explicitQosEnabled = explicitQosEnabled;
    }

    public boolean isDeliveryPersistent() {
        return deliveryPersistent;
    }

    public void setDeliveryPersistent(boolean deliveryPersistent) {
        this.deliveryPersistent = deliveryPersistent;
    }

    public long getTimeToLive() {
        return timeToLive;
    }

    public void setTimeToLive(long timeToLive) {
        this.timeToLive = timeToLive;
    }

    public MessageConverter getMessageConverter() {
        return messageConverter;
    }

    public void setMessageConverter(MessageConverter messageConverter) {
        this.messageConverter = messageConverter;
    }

    public boolean isMessageIdEnabled() {
        return messageIdEnabled;
    }

    public void setMessageIdEnabled(boolean messageIdEnabled) {
        this.messageIdEnabled = messageIdEnabled;
    }

    public boolean isMessageTimestampEnabled() {
        return messageTimestampEnabled;
    }

    public void setMessageTimestampEnabled(boolean messageTimestampEnabled) {
        this.messageTimestampEnabled = messageTimestampEnabled;
    }

    public int getPriority() {
        return priority;
    }

    public void setPriority(int priority) {
        this.priority = priority;
    }

    public ConsumerType getConsumerType() {
        return consumerType;
    }

    public void setConsumerType(ConsumerType consumerType) {
        this.consumerType = consumerType;
    }

    public int getAcknowledgementMode() {
        return acknowledgementMode;
    }

    public void setAcknowledgementMode(int consumerAcknowledgementMode) {
        this.acknowledgementMode = consumerAcknowledgementMode;
        this.acknowledgementModeName = null;
    }

    public boolean isTransacted() {
        return transacted;
    }

    public void setTransacted(boolean consumerTransacted) {
        this.transacted = consumerTransacted;
    }

    protected AbstractMessageListenerContainer chooseMessageListenerContainerImplementation() {
        switch (consumerType) {
        case Simple:
            return isUseVersion102() ? new SimpleMessageListenerContainer102() : new SimpleMessageListenerContainer();
        case ServerSessionPool:
            return isUseVersion102() ? new ServerSessionMessageListenerContainer102() : new ServerSessionMessageListenerContainer();
        case Default:
            return isUseVersion102() ? new DefaultMessageListenerContainer102() : new DefaultMessageListenerContainer();
        default:
            throw new IllegalArgumentException("Unknown consumer type: " + consumerType);
        }
    }

    /**
     * Factory method which allows derived classes to customize the lazy
     * creation
     */
    protected ConnectionFactory createConnectionFactory() {
        ObjectHelper.notNull(connectionFactory, "connectionFactory");
        return null;
    }

    /**
     * Factory method which allows derived classes to customize the lazy
     * creation
     */
    protected ConnectionFactory createListenerConnectionFactory() {
        return getConnectionFactory();
    }

    /**
     * Factory method which allows derived classes to customize the lazy
     * creation
     */
    protected ConnectionFactory createTemplateConnectionFactory() {
        return getConnectionFactory();
    }

    public boolean isPreserveMessageQos() {
        return preserveMessageQos;
    }

    /**
     * Set to true if you want to send message using the QoS settings specified
     * on the message.  Normally the QoS settings used are the one configured
     * on this Object.
     *
     * @param preserveMessageQos
     */
    public void setPreserveMessageQos(boolean preserveMessageQos) {
        this.preserveMessageQos = preserveMessageQos;
    }

    public JmsOperations getJmsOperations() {
        return jmsOperations;
    }

    public void setJmsOperations(JmsOperations jmsOperations) {
        this.jmsOperations = jmsOperations;
    }

    public DestinationResolver getDestinationResolver() {
        return destinationResolver;
    }

    public void setDestinationResolver(DestinationResolver destinationResolver) {
        this.destinationResolver = destinationResolver;

FUNC_DECL TRY CATCH RETURN THROW_RAISE CALL CALL

    public boolean isUseVersion102() {
        return useVersion102;

    public boolean isAutoStartup() {
        return autoStartup;

    public String getClientId() {
        return clientId;

    public boolean isSubscriptionDurable() {
        return subscriptionDurable;

    public TaskExecutor getTaskExecutor() {
        return taskExecutor;

    public boolean isPubSubNoLocal() {
        return pubSubNoLocal;

    public int getConcurrentConsumers() {
        return concurrentConsumers;

    public int getMaxMessagesPerTask() {
        return maxMessagesPerTask;

    public int getCacheLevel() {
        return cacheLevel;

    public String getCacheLevelName() {
        return cacheLevelName;

    public long getRecoveryInterval() {
        return recoveryInterval;

    public long getReceiveTimeout() {
        return receiveTimeout;

    public String getTransactionName() {
        return transactionName;

    public int getTransactionTimeout() {
        return transactionTimeout;

    public boolean isExplicitQosEnabled() {
        return explicitQosEnabled;

    public boolean isDeliveryPersistent() {
        return deliveryPersistent;

    public long getTimeToLive() {
        return timeToLive;

    public boolean isMessageIdEnabled() {
        return messageIdEnabled;

    public int getPriority() {
        return priority;

    public void setPriority(int priority) {
        this.priority = priority;

    public ConsumerType getConsumerType() {
        return consumerType;

    public int getAcknowledgementMode() {
        return acknowledgementMode;

    public boolean isTransacted() {
        return transacted;

FUNC_DECL SWITCH THROW_RAISE RETURN RETURN RETURN CALL COND_EXPR COND_EXPR COND_EXPR BINARY_OP CALL CALL CALL CALL CALL CALL CALL CALL CALL LITERAL_ANCHOR

    public boolean isPreserveMessageQos() {
        return preserveMessageQos;

    public JmsOperations getJmsOperations() {
        return jmsOperations;


org.apache.xalan.lib.ExsltMath

public class ExsltMath
{
  /**
   * The math:max function returns the maximum value of the nodes passed as the argument.
   * The maximum value is defined as follows. The node set passed as an argument is sorted
   * in descending order as it would be by xsl:sort with a data type of number. The maximum
   * is the result of converting the string value of the first node in this sorted list to
   * a number using the number function.
   *
   * If the node set is empty, or if the result of converting the string values of any of the
   * nodes to a number is NaN, then NaN is returned.
   *
   * @param expCon is passed in by the Xalan extension processor
   * @param ni The NodeIterator for the node-set to be evaluated.
   *
   * @return String representation of the maximum value found, NaN if any node cannot be
   * converted to a number.
   *
   */
  public static String max (ExpressionContext expCon, NodeIterator ni)
  {
    NodeSet ns = new NodeSet(ni);
    Node maxNode = null;
    double m = Double.MIN_VALUE;
    for (int i = 0; i < ns.getLength(); i++)
    {
      Node n = ns.elementAt(i);
      double d = expCon.toNumber(n);
      if (Double.isNaN(d))
        return "NaN";
      else if (d > m)
      {
        m = d;
        maxNode = n;
      }
    }
    return expCon.toString(maxNode);
  }

  /**
   * The math:min function returns the minimum value of the nodes passed as the argument.
   * The minimum value is defined as follows. The node set passed as an argument is sorted
   * in ascending order as it would be by xsl:sort with a data type of number. The minimum
   * is the result of converting the string value of the first node in this sorted list to
   * a number using the number function.
   *
   * If the node set is empty, or if the result of converting the string values of any of
   * the nodes to a number is NaN, then NaN is returned.
   *
   * @param expCon is passed in by the Xalan extension processor
   * @param ni The NodeIterator for the node-set to be evaluated.
   *
   * @return String representation of the minimum value found, NaN if any node cannot be
   * converted to a number.
   *
   */
  public static String min (ExpressionContext expCon, NodeIterator ni)
  {
    NodeSet ns = new NodeSet(ni);
    Node minNode = null;
    double m = Double.MAX_VALUE;
    for (int i = 0; i < ns.getLength(); i++)
    {
      Node n = ns.elementAt(i);
      double d = expCon.toNumber(n);
      if (Double.isNaN(d))
        return "NaN";
      else if (d < m)
      {
        m = d;
        minNode = n;
      }
    }
    return expCon.toString(minNode);
  }

  /**
   * The math:highest function returns the nodes in the node set whose value is the maximum
   * value for the node set. The maximum value for the node set is the same as the value as
   * calculated by math:max. A node has this maximum value if the result of converting its
   * string value to a number as if by the number function is equal to the maximum value,
   * where the equality comparison is defined as a numerical comparison using the = operator.
   *
   * If any of the nodes in the node set has a non-numeric value, the math:max function will
   * return NaN. The definition numeric comparisons entails that NaN != NaN. Therefore if any
   * of the nodes in the node set has a non-numeric value, math:highest will return an empty
   * node set.
   *
   * @param expCon is passed in by the Xalan extension processor
   * @param ni The NodeIterator for the node-set to be evaluated.
   *
   * @return node-set with nodes containing the maximum value found, an empty node-set
   * if any node cannot be converted to a number.
   */
  public static NodeSet highest (ExpressionContext expCon, NodeIterator ni)
    throws java.lang.CloneNotSupportedException
  {
    NodeSet ns = new NodeSet(ni);
    NodeIterator niClone = ns.cloneWithReset();
    double high = new Double(max(expCon, niClone)).doubleValue();
    NodeSet highNodes = new NodeSet();
    highNodes.setShouldCacheNodes(true);

    if (Double.isNaN(high))

     for (int i = 0; i < ns.getLength(); i++)
    {
      Node n = ns.elementAt(i);
      double d = expCon.toNumber(n);
      if (d == high)
      highNodes.addElement(n);
    }
    return highNodes;
  }

  /**
   * The math:lowest function returns the nodes in the node set whose value is the minimum value
   * for the node set. The minimum value for the node set is the same as the value as calculated
   * by math:min. A node has this minimum value if the result of converting its string value to
   * a number as if by the number function is equal to the minimum value, where the equality
   * comparison is defined as a numerical comparison using the = operator.
   *
   * If any of the nodes in the node set has a non-numeric value, the math:min function will return
   * NaN. The definition numeric comparisons entails that NaN != NaN. Therefore if any of the nodes
   * in the node set has a non-numeric value, math:lowest will return an empty node set.
   *
   * @param expCon is passed in by the Xalan extension processor
   * @param ni The NodeIterator for the node-set to be evaluated.
   *
   * @return node-set with nodes containing the minimum value found, an empty node-set
   * if any node cannot be converted to a number.
   *
   */
  public static NodeSet lowest (ExpressionContext expCon, NodeIterator ni)
    throws java.lang.CloneNotSupportedException
  {
    NodeSet ns = new NodeSet(ni);
    NodeIterator niClone = ns.cloneWithReset();
    double low = new Double(min(expCon, niClone)).doubleValue();

    NodeSet lowNodes = new NodeSet();
    lowNodes.setShouldCacheNodes(true);

    if (Double.isNaN(low))

     for (int i = 0; i < ns.getLength(); i++)
    {
      Node n = ns.elementAt(i);
      double d = expCon.toNumber(n);
      if (d == low)
      lowNodes.addElement(n);
    }
    return lowNodes;


public class RetrieveOptions extends LogOptions {
    /**
     * The names of configurations to retrieve. If the array consists only of '*', then all
     * configurations of the module will be retrieved.
     */
    private String[] confs = new String[] {"*"};

    /**
     * The pattern to which ivy files should be retrieved. If destIvyPattern is null no ivy files
     * will be copied.
     */
    private String destIvyPattern = null;

    /**
     * The filter to apply before retrieving artifacts.
     */
    private Filter artifactFilter = FilterHelper.NO_FILTER;

    /**
     * True if a synchronisation of the destination directory should be done, false if a simple copy
     * is enough. Synchronisation means that after the retrieve only files which have been retrieved
     * will be present in the destination directory, which means that some files may be deleted.
     */
    private boolean sync = false;

    /**
     * True if the original files should be used insteaad of their cache copy.
     */
    private boolean useOrigin = false;

    /**
     * True if symbolic links should be created instead of plain copy. Works only on OS supporting
     * symbolic links.
     */
    private boolean makeSymlinks = false;

    /**
     * The id used to store the resolve information.
     */
    private String resolveId;

    public Filter getArtifactFilter() {
        return artifactFilter;
    }

    public RetrieveOptions setArtifactFilter(Filter artifactFilter) {
        this.artifactFilter = artifactFilter;
        return this;
    }

    public String[] getConfs() {
        return confs;
    }

    public RetrieveOptions setConfs(String[] confs) {
        this.confs = confs;
        return this;
    }

    public String getDestIvyPattern() {
        return destIvyPattern;
    }

    public RetrieveOptions setDestIvyPattern(String destIvyPattern) {
        this.destIvyPattern = destIvyPattern;
        return this;
    }

    public boolean isMakeSymlinks() {
        return makeSymlinks;
    }

    public RetrieveOptions setMakeSymlinks(boolean makeSymlinks) {
        this.makeSymlinks = makeSymlinks;
        return this;
    }

    public boolean isSync() {
        return sync;
    }

    public RetrieveOptions setSync(boolean sync) {
        this.sync = sync;
        return this;
    }

    public boolean isUseOrigin() {
        return useOrigin;
    }

    public RetrieveOptions setUseOrigin(boolean useOrigin) {
        this.useOrigin = useOrigin;
        return this;
    }

    public String getResolveId() {
        return resolveId;
    }

    public RetrieveOptions setResolveId(String resolveId) {
        this.resolveId = resolveId;
        return this;

    public boolean isSync() {
        return sync;


org.apache.poi.ddf.EscherBlipRecord

public class EscherBlipRecord
        extends EscherRecord
{
    public static final short  RECORD_ID_START    = (short) 0xF018;
    public static final short  RECORD_ID_END      = (short) 0xF117;
    public static final String RECORD_DESCRIPTION = "msofbtBlip";
    private static final int   HEADER_SIZE               = 8;

    private              byte[] field_1_secondaryUID;
    private              int    field_2_cacheOfSize;
    private              int    field_3_boundaryTop;
    private              int    field_4_boundaryLeft;
    private              int    field_5_boundaryWidth;
    private              int    field_6_boundaryHeight;
    private              int    field_7_width;
    private              int    field_8_height;
    private              int    field_9_cacheOfSavedSize;
    private              byte   field_10_compressionFlag;
    private              byte   field_11_filter;
    private              byte[] field_12_data;

    /**
     * This method deserializes the record from a byte array.
     *
     * @param data          The byte array containing the escher record information
     * @param offset        The starting offset into data.
     * @param recordFactory May be null since this is not a container record.
     * @return The number of bytes read from the byte array.
     */
    public int fillFields( byte[] data, int offset,
                              EscherRecordFactory recordFactory
                              )
    {
        int bytesAfterHeader = readHeader( data, offset );
        int pos              = offset + HEADER_SIZE;

        int size = 0;
        field_1_secondaryUID = new byte[16];
        System.arraycopy( data, pos + size, field_1_secondaryUID, 0, 16 ); size += 16;
        field_2_cacheOfSize      =  LittleEndian.getInt( data, pos + size );size+=4;
        field_3_boundaryTop      =  LittleEndian.getInt( data, pos + size );size+=4;
        field_4_boundaryLeft     =  LittleEndian.getInt( data, pos + size );size+=4;
        field_5_boundaryWidth    =  LittleEndian.getInt( data, pos + size );size+=4;
        field_6_boundaryHeight   =  LittleEndian.getInt( data, pos + size );size+=4;
        field_7_width            =  LittleEndian.getInt( data, pos + size );size+=4;
        field_8_height           =  LittleEndian.getInt( data, pos + size );size+=4;
        field_9_cacheOfSavedSize =  LittleEndian.getInt( data, pos + size );size+=4;
        field_10_compressionFlag = data[pos + size]; size++;
        field_11_filter          = data[pos + size]; size++;

        int bytesRemaining = bytesAfterHeader - size;
        field_12_data = new byte[bytesRemaining];
        System.arraycopy(data, pos + size, field_12_data, 0, bytesRemaining);

        return bytesRemaining + HEADER_SIZE + bytesAfterHeader;
    }

    /**
     * This method serializes this escher record into a byte array.
     *
     * @param offset   The offset into data to start writing the record data to.
     * @param data     The byte array to serialize to.
     * @param listener A listener to retrieve start and end callbacks.  Use a NullEscherSerailizationListener to ignore these events.
     * @return The number of bytes written.
     *
     * @see NullEscherSerializationListener
     */
    public int serialize( int offset, byte[] data, EscherSerializationListener listener )
    {
        listener.beforeRecordSerialize(offset, getRecordId(), this);

        LittleEndian.putShort( data, offset, getOptions() );
        LittleEndian.putShort( data, offset + 2, getRecordId() );
        int remainingBytes = field_12_data.length + 36;
        LittleEndian.putInt( data, offset + 4, remainingBytes );

        int pos = offset + HEADER_SIZE;
        System.arraycopy(field_1_secondaryUID, 0, data, pos, 16 ); pos += 16;
        LittleEndian.putInt( data, pos, field_2_cacheOfSize); pos += 4;
        LittleEndian.putInt( data, pos, field_3_boundaryTop); pos += 4;
        LittleEndian.putInt( data, pos, field_4_boundaryLeft); pos += 4;
        LittleEndian.putInt( data, pos, field_5_boundaryWidth); pos += 4;
        LittleEndian.putInt( data, pos, field_6_boundaryHeight); pos += 4;
        LittleEndian.putInt( data, pos, field_7_width); pos += 4;
        LittleEndian.putInt( data, pos, field_8_height); pos += 4;
        LittleEndian.putInt( data, pos, field_9_cacheOfSavedSize); pos += 4;
        data[pos++] = field_10_compressionFlag;
        data[pos++] = field_11_filter;
        System.arraycopy(field_12_data, 0, data, pos, field_12_data.length); pos += field_12_data.length;

        listener.afterRecordSerialize(pos, getRecordId(), pos - offset, this);
        return pos - offset;
    }

    /**
     * Returns the number of bytes that are required to serialize this record.
     *
     * @return Number of bytes
     */
    public int getRecordSize()
    {
        return 58 + field_12_data.length;
    }

    /**
     * The short name for this record
     */
    public String getRecordName()
    {
        return "Blip";
    }

    /**
     * Retrieve the secondary UID
     */
    public byte[] getSecondaryUID()
    {
        return field_1_secondaryUID;
    }

    /**
     * Set the secondary UID
     */
    public void setSecondaryUID( byte[] field_1_secondaryUID )
    {
        this.field_1_secondaryUID = field_1_secondaryUID;
    }

    /**
     * Retrieve the cache of the metafile size
     */
    public int getCacheOfSize()
    {
        return field_2_cacheOfSize;
    }

    /**
     * Set the cache of the metafile size
     */
    public void setCacheOfSize( int field_2_cacheOfSize )
    {
        this.field_2_cacheOfSize = field_2_cacheOfSize;
    }

    /**
     * Retrieve the top boundary of the metafile drawing commands
     */
    public int getBoundaryTop()
    {
        return field_3_boundaryTop;
    }

    /**
     * Set the top boundary of the metafile drawing commands
     */
    public void setBoundaryTop( int field_3_boundaryTop )
    {
        this.field_3_boundaryTop = field_3_boundaryTop;
    }

    /**
     * Retrieve the left boundary of the metafile drawing commands
     */
    public int getBoundaryLeft()
    {
        return field_4_boundaryLeft;
    }

    /**
     * Set the left boundary of the metafile drawing commands
     */
    public void setBoundaryLeft( int field_4_boundaryLeft )
    {
        this.field_4_boundaryLeft = field_4_boundaryLeft;
    }

    /**
     * Retrieve the boundary width of the metafile drawing commands
     */
    public int getBoundaryWidth()
    {
        return field_5_boundaryWidth;
    }

    /**
     * Set the boundary width of the metafile drawing commands
     */
    public void setBoundaryWidth( int field_5_boundaryWidth )
    {
        this.field_5_boundaryWidth = field_5_boundaryWidth;
    }

    /**
     * Retrieve the boundary height of the metafile drawing commands
     */
    public int getBoundaryHeight()
    {
        return field_6_boundaryHeight;
    }

    /**
     * Set the boundary height of the metafile drawing commands
     */
    public void setBoundaryHeight( int field_6_boundaryHeight )
    {
        this.field_6_boundaryHeight = field_6_boundaryHeight;
    }

    /**
     * Retrieve the width of the metafile in EMU's (English Metric Units).
     */
    public int getWidth()
    {
        return field_7_width;
    }

    /**
     * Set the width of the metafile in EMU's (English Metric Units).
     */
    public void setWidth( int width )
    {
        this.field_7_width = width;
    }

    /**
     * Retrieve the height of the metafile in EMU's (English Metric Units).
     */
    public int getHeight()
    {
        return field_8_height;
    }

    /**
     * Set the height of the metafile in EMU's (English Metric Units).
     */
    public void setHeight( int height )
    {
        this.field_8_height = height;
    }

    /**
     * Retrieve the cache of the saved size
     */
    public int getCacheOfSavedSize()
    {
        return field_9_cacheOfSavedSize;
    }

    /**
     * Set the cache of the saved size
     */
    public void setCacheOfSavedSize( int field_9_cacheOfSavedSize )
    {
        this.field_9_cacheOfSavedSize = field_9_cacheOfSavedSize;
    }

    /**
     * Is the contents of the blip compressed?
     */
    public byte getCompressionFlag()
    {
        return field_10_compressionFlag;
    }

    /**
     * Set whether the contents of the blip is compressed
     */
    public void setCompressionFlag( byte field_10_compressionFlag )
    {
        this.field_10_compressionFlag = field_10_compressionFlag;
    }

    /**
     * Filter should always be 0
     */
    public byte getFilter()
    {
        return field_11_filter;
    }

    /**
     * Filter should always be 0
     */
    public void setFilter( byte field_11_filter )
    {
        this.field_11_filter = field_11_filter;
    }

    /**
     * The BLIP data
     */
    public byte[] getData()
    {
        return field_12_data;
    }

    /**
     * The BLIP data
     */
    public void setData( byte[] field_12_data )
    {
        this.field_12_data = field_12_data;
    }

    /**
     * The string representation of this record.
     *
     * @return A string
     */
    public String toString()
    {
        String nl = System.getProperty( "line.separator" );

        String extraData;
        ByteArrayOutputStream b = new ByteArrayOutputStream();
        try
        {
            HexDump.dump( this.field_12_data, 0, b, 0 );
            extraData = b.toString();
        }
        catch ( Exception e )
        {
            extraData = e.toString();
        }
        return getClass().getName() + ":" + nl +
                "  RecordId: 0x" + HexDump.toHex( getRecordId() ) + nl +
                "  Options: 0x" + HexDump.toHex( getOptions() ) + nl +
                "  Secondary UID: " + HexDump.toHex( field_1_secondaryUID ) + nl +
                "  CacheOfSize: " + field_2_cacheOfSize + nl +
                "  BoundaryTop: " + field_3_boundaryTop + nl +
                "  BoundaryLeft: " + field_4_boundaryLeft + nl +
                "  BoundaryWidth: " + field_5_boundaryWidth + nl +
                "  BoundaryHeight: " + field_6_boundaryHeight + nl +
                "  X: " + field_7_width + nl +
                "  Y: " + field_8_height + nl +
                "  CacheOfSavedSize: " + field_9_cacheOfSavedSize + nl +
                "  CompressionFlag: " + field_10_compressionFlag + nl +
                "  Filter: " + field_11_filter + nl +
                "  Data:" + nl + extraData;
    }

    /**
     * Compress the contents of the provided array
     *
     * @param data An uncompressed byte array
     * @see DeflaterOutputStream#write(int b)
     */
    public static byte[] compress( byte[] data )
    {
        ByteArrayOutputStream out                  = new ByteArrayOutputStream();
        DeflaterOutputStream  deflaterOutputStream = new DeflaterOutputStream( out );
        try
        {
            for ( int i = 0; i < data.length; i++ )
                deflaterOutputStream.write( data[i] );
        }
        catch ( IOException e )
        {
            throw new RecordFormatException( e.toString() );
        }

        return out.toByteArray();
    }

    /**
     * Decompresses a byte array.
     *
     * @param data   The compressed byte array
     * @param pos    The starting position into the byte array
     * @param length The number of compressed bytes to decompress
     * @return An uncompressed byte array
     * @see InflaterInputStream#read
     */
    public static byte[] decompress( byte[] data, int pos, int length )
    {
        byte[] compressedData = new byte[length];
        System.arraycopy( data, pos + 50, compressedData, 0, length );
        InputStream           compressedInputStream = new ByteArrayInputStream( compressedData );
        InflaterInputStream   inflaterInputStream   = new InflaterInputStream( compressedInputStream );
        ByteArrayOutputStream out                   = new ByteArrayOutputStream();
        int                   c;
        try
        {
            while ( ( c = inflaterInputStream.read() ) != -1 )
                out.write( c );
        }
        catch ( IOException e )
        {
            throw new RecordFormatException( e.toString() );
        }
        return out.toByteArray();


bsh.SimpleNode

class SimpleNode implements Node
{
	public static SimpleNode JAVACODE =
		new SimpleNode( -1 ) {
			public String getSourceFile() {
				return "<Called from Java Code>";
			}

			public int getLineNumber() {
				return -1;
			}

			public String getText()  {
				return "<Compiled Java Code>";
			}
		};

	protected Node parent;
	protected Node[] children;
	protected int id;
	Token firstToken, lastToken;

	/** the source of the text from which this was parsed */
	String sourceFile;

	public SimpleNode(int i) {
		id = i;
	}

	public void jjtOpen() { }
	public void jjtClose() { }

	public void jjtSetParent(Node n) { parent = n; }
	public Node jjtGetParent() { return parent; }
	//public SimpleNode getParent() { return (SimpleNode)parent; }

	public void jjtAddChild(Node n, int i)
	{
		if (children == null)
			children = new Node[i + 1];
		else
			if (i >= children.length)
			{
				Node c[] = new Node[i + 1];
				System.arraycopy(children, 0, c, 0, children.length);
				children = c;
			}

		children[i] = n;
	}

	public Node jjtGetChild(int i) {
		return children[i];
	}
	public SimpleNode getChild( int i ) {
		return (SimpleNode)jjtGetChild(i);
	}

	public int jjtGetNumChildren() {
		return (children == null) ? 0 : children.length;
	}

	/*
		You can override these two methods in subclasses of SimpleNode to
		customize the way the node appears when the tree is dumped.  If
		your output uses more than one line you should override
		toString(String), otherwise overriding toString() is probably all
		you need to do.
	*/
	public String toString() { return ParserTreeConstants.jjtNodeName[id]; }
	public String toString(String prefix) { return prefix + toString(); }

	/*
		Override this method if you want to customize how the node dumps
		out its children.
	*/
	public void dump(String prefix)
	{
		System.out.println(toString(prefix));
		if(children != null)
		{
			for(int i = 0; i < children.length; ++i)
			{
				SimpleNode n = (SimpleNode)children[i];
				if (n != null)
				{
					n.dump(prefix + " ");
				}
			}
		}
	}

	//  ---- BeanShell specific stuff hereafter ----  //

	/**
		Detach this node from its parent.
		This is primarily useful in node serialization.
		(see BSHMethodDeclaration)
	*/
	public void prune() {
		jjtSetParent( null );
	}

	/**
		This is the general signature for evaluation of a node.
	*/
	public Object eval( CallStack callstack, Interpreter interpreter )
		throws EvalError
	{
		throw new InterpreterError(
			"Unimplemented or inappropriate for " + getClass().getName() );
	}

	/**
		Set the name of the source file (or more generally source) of
		the text from which this node was parsed.
	*/
	public void setSourceFile( String sourceFile ) {
		this.sourceFile = sourceFile;
	}

	/**
		Get the name of the source file (or more generally source) of
		the text from which this node was parsed.
		This will recursively search up the chain of parent nodes until
		a source is found or return a string indicating that the source
		is unknown.
	*/
	public String getSourceFile() {
		if ( sourceFile == null )
			if ( parent != null )
				return ((SimpleNode)parent).getSourceFile();
			else
				return "<unknown file>";
		else
			return sourceFile;
	}

	/**
		Get the line number of the starting token
	*/
	public int getLineNumber() {
		return firstToken.beginLine;
	}

	/**
		Get the ending line number of the starting token
	public int getEndLineNumber() {
		return lastToken.endLine;
	}
	*/

	/**
		Get the text of the tokens comprising this node.
	*/
	public String getText()
	{
		StringBuffer text = new StringBuffer();
		Token t = firstToken;
		while ( t!=null ) {
			text.append(t.image);
			if ( !t.image.equals(".") )
				text.append(" ");
			if ( t==lastToken ||
				t.image.equals("{") || t.image.equals(";") )
				break;
			t=t.next;
		}

		return text.toString();