/* ProcessingInstruction.java
Purpose:
Description:
History:
2001/10/22 20:53:28, Create, 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;
import java.util.Map;
import java.util.LinkedHashMap;
import java.util.Iterator;
import java.util.Collections;
import org.zkoss.mesg.MCommon;
import org.zkoss.lang.Objects;
import org.zkoss.lang.Strings;
import static org.zkoss.lang.Generics.cast;
import org.zkoss.lang.SystemException;
import org.zkoss.util.Maps;
import org.zkoss.idom.impl.*;
/**
* The iDOM processing instruction.
*
* @author tomyeh
*/
public class ProcessingInstruction extends AbstractItem
implements org.w3c.dom.ProcessingInstruction {
/** The target. */
protected String _target;
/** The raw data. */
protected String _rawData;
/** Constructor.
*/
public ProcessingInstruction(String target, String data) {
setTarget(target);
setData(data);
}
/** Constructor.
*/
public ProcessingInstruction(String target, Map<String, String> data) {
setTarget(target);
setData(data);
}
/** Constructor.
*/
protected ProcessingInstruction() {
}
//-- ProcessingInstruction extras --//
public final String getTarget() {
return _target;
}
public final void setTarget(String target) {
if (!Objects.equals(_target, target)) {
Verifier.checkPITarget(target, getLocator());
_target = target;
}
}
public final String getData() {
return _rawData;
}
public final void setData(String data) {
if (data == null)
data = "";
_rawData = data;
}
/** Returns the parsed data in the form of Map (never null).
*/
public final Map<String, String> parseData() {
return parseToMap(new LinkedHashMap<String, String>(), getData());
}
/**
* Sets the raw data with a data map.
* Each entry in the data map is a (name, value) pair.
*
* @exception org.zkoss.util.IllegalSyntaxException if name contains
* an invalid character: '=', ' ', '\'', '"'
*/
public final void setData(Map<String, String> data) {
final String notAllowed = "= '\"";
for (final Iterator it = data.keySet().iterator(); it.hasNext();) {
final String key = (String)it.next();
final int j = Strings.anyOf(key, notAllowed, 0);
if (j < key.length()) { //found
final char cc = key.charAt(j);
throw new SystemException(MCommon.ILLEGAL_CHAR,
cc + " (0x" + Integer.toHexString(cc) + ')');
}
}
setData(Maps.toString(data, '"', ' '));
}
/**
* Parses the raw data into a map.
* Each entry in the data map is a (name, value) pair.
* This method will convert a value to a number, either Integer
* or Double, if appropriate.
*
* <p>Most of characters are considered as ordinary (like 'a'),
* exception '"', '='
*
* <p>For example, the string will cause ("a12", Intger(12)),
* ("b+3", null), ("345", null), ("c6", "abc=125&3?5"):<br>
* a12 =12 b+3 345 c6=\t'abc=125&3?5'
*
* @return the map (never null)
* @exception org.zkoss.util.IllegalSyntaxException if syntax erros
*/
public static final Map<String, String> parseToMap(Map<String, String> map, String rawData) {
if (rawData == null || rawData.trim().length() == 0) {
if (map != null)
return map;
return Collections.emptyMap();
}
map = cast(Maps.parse(map, rawData, ' ', (char)1)); //both ' and "
//" and other are not processed by SAXHandler,
//so we have to handle them here
for (Map.Entry<String, String> me: map.entrySet()) {
final String val = me.getValue();
if (val == null)
continue;
StringBuffer sb = null;
for (int i = 0, j = 0, len = val.length();;) {
int k = val.indexOf('&', j);
if (k < 0) {
if (sb != null)
me.setValue(sb.append(val.substring(i)).toString());
break;
}
int l = val.indexOf(';', k);
if (l >= 0) {
final char cc;
final String code = val.substring(k + 1, l);
if ("quot".equals(code)) {
cc = '"';
} else if ("amp".equals(code)) {
cc = '&';
} else if ("lt".equals(code)) {
cc = '<';
} else if ("gt".equals(code)) {
cc = '>';
} else {
//TODO: handle nnn; and more
j = l + 1;
continue; //ignore it
}
if (sb == null) sb = new StringBuffer(len);
sb.append(val.substring(i, k)).append(cc);
i = j = l + 1;
} else {
j = k + 1;
}
}
}
return map;
}
//-- Item --//
public final String getName() {
return getTarget();
}
public final void setName(String name) {
setTarget(name);
}
public final String getText() {
return getData();
}
public final void setText(String text) {
setData(text);
}
//-- Node --//
public final short getNodeType() {
return PROCESSING_INSTRUCTION_NODE;
}
//-- org.w3c.dom.ProcessingInstruction --//
//-- Object --//
public String toString() {
StringBuffer sb = new StringBuffer(64)
.append("[PI: ").append(_target);
if (_rawData.length() > 0)
sb.append(' ').append(_rawData);
return sb.append(']').toString();
}
}