/* AbstractTextual.java


Purpose: 
Description: 
History:
C2001/10/22 20:48:47, reate, Tom M. Yeh

Copyright (C) 2001 Potix Corporation. All Rights Reserved.

{{IS_RIGHT
	This program is distributed under LGPL Version 2.1 in the hope that
	it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.idom.impl;

import java.util.List;
import org.w3c.dom.CharacterData;

import org.zkoss.idom.DOMException;
import org.zkoss.idom.Element;
import org.zkoss.idom.Item;
import org.zkoss.idom.Textual;
import org.zkoss.idom.Verifier;
import org.zkoss.lang.Objects;
import org.zkoss.lang.Classes;
import org.zkoss.lang.SystemException;

/**
 * Represents a textual item.
 *
 * <p>Note: any deriving class's getText shall not return null.
 *
 * <p>Element.getText uses this class's isPartOfParentText to know
 * whether a child's text shall be concatenated.
 *
 * @author tomyeh
 * @see Element
 */
public abstract class AbstractTextual extends AbstractItem
implements Textual, CharacterData {
	/** The text. */
	protected String _text;

	/** Constructor.
	 */
	protected AbstractTextual(String text) {
		setText(text);
	}
	/** Constructor.
	 */
	protected AbstractTextual() {
		_text = "";
	}

	//-- Textual --//
	/**
	 * Returns true if this text object is part of the parent's text.
	 * Default: true.
	 */
	public boolean isPartOfParentText() {
		return true;
	}
	/**
	 * Returns true if this textual object is allowed to be coalesced with
	 * its siblings with the same type (class).
	 * It is used by Group.coalesce.
	 * <p>Default: false. Right now only Text override it to true.
	 */
	public boolean isCoalesceable() {
		return false;
	}
	public Textual split(int offset) {
		String s = getText();
		if (offset < s.length())
			return null;

		Textual vtx;
		try {
			vtx = (Textual)Classes.newInstance(getClass(),
				new Class[] {String.class},
				new Object[] {s.substring(offset)});
		} catch (Exception ex) {
			throw SystemException.Aide.wrap(ex,
					getClass() + " must have a String constructor");
		}
		setText(s.substring(0, offset));

		if (getParent() != null) {
			List<Item> list = getParent().getChildren();
			list.add(list.indexOf(this) + 1, (Item)vtx);
		}
		return vtx;
	}

	//-- utilities --//
	/**
	 * Checks whether the text is valid.
	 * It is usually overridden by the deriving classes to check
	 * more specifically.
	 */
	protected void checkText(String text) {
		Verifier.checkCharacterData(text, getLocator());
	}

	//-- Item --//
	public String getText() {
		return _text;
	}
	public void setText(String text) {
		if (text == null)
			text = "";

		if (!Objects.equals(_text, text)) {
			checkText(text);
			_text = text;
		}
	}

	//-- CharacterData --//
	public final int getLength() {
		return getText().length(); //not use _text since it might be overred
	}
	public final String getData() {
		return getText();
	}
	public final void setData(String data) {
		setText(data);
	}
	public final String substringData(int offset, int count) {
		return getText().substring(offset, offset + count);
	}
	public final void appendData(String newData) {
		if (newData != null && newData.length() != 0)
			setText(getText() + newData);
	}
	public final void insertData(int offset, String arg) {
		if (arg != null && arg.length() != 0)
			setText(new StringBuffer(getText())
				.insert(offset, arg).toString());
	}
	public final void deleteData(int offset, int count) {
		if (offset < getLength())
			setText(new StringBuffer(getText())
				.delete(offset, offset + count).toString());
	}
	public final void replaceData(int offset, int count, String arg) {
		setText(new StringBuffer(getText())
			.replace(offset, offset + count, arg).toString());
	}

	//-- Text if implemented by derived class --//
	public final org.w3c.dom.Text splitText(int offset) {
		return (org.w3c.dom.Text)split(offset);
	}

	public boolean isElementContentWhitespace() {
		throw new UnsupportedOperationException("DOM Level 3");
	}
	public String getWholeText() {
		throw new UnsupportedOperationException("DOM Level 3");
	}
	public org.w3c.dom.Text replaceWholeText(String content)
	throws DOMException {
		throw new UnsupportedOperationException("DOM Level 3");
	}

	//Node//
	public String getTextContent() {
		return getText();
	}

	//-- Object --//
	/**
	 * Gets the textual representation for debug.
	 */
	public String toString() {
		String clsName = getClass().getName();
		int j = clsName.lastIndexOf('.');
		if (j >= 0)
			clsName = clsName.substring(j + 1);
		return '[' + clsName + ": \"" + getText() + "\"]";
	}
}
