Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.apache.cxf.javascript.types.SchemaJavascriptBuilder Maven / Gradle / Ivy
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cxf.javascript.types;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import org.apache.cxf.common.i18n.Message;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.common.util.StringUtils;
import org.apache.cxf.common.xmlschema.SchemaCollection;
import org.apache.cxf.common.xmlschema.XmlSchemaConstants;
import org.apache.cxf.common.xmlschema.XmlSchemaUtils;
import org.apache.cxf.javascript.AttributeInfo;
import org.apache.cxf.javascript.ItemInfo;
import org.apache.cxf.javascript.JavascriptUtils;
import org.apache.cxf.javascript.NameManager;
import org.apache.cxf.javascript.NamespacePrefixAccumulator;
import org.apache.cxf.javascript.ParticleInfo;
import org.apache.cxf.javascript.UnsupportedConstruct;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaAnnotated;
import org.apache.ws.commons.schema.XmlSchemaAny;
import org.apache.ws.commons.schema.XmlSchemaAttributeOrGroupRef;
import org.apache.ws.commons.schema.XmlSchemaCollection;
import org.apache.ws.commons.schema.XmlSchemaComplexType;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaObject;
import org.apache.ws.commons.schema.XmlSchemaSimpleType;
import org.apache.ws.commons.schema.XmlSchemaType;
/**
* Generate Javascript for a schema, and provide information needed for the service builder. As of this pass,
* there is no support for non-sequence types or for attribute mappings.
*/
public class SchemaJavascriptBuilder {
private static final Logger LOG = LogUtils.getL7dLogger(SchemaJavascriptBuilder.class);
private static int anyPrefixCounter;
private SchemaCollection xmlSchemaCollection;
private NameManager nameManager;
private NamespacePrefixAccumulator prefixAccumulator;
// private SchemaInfo schemaInfo;
// In general, I (bimargulies) hate fields that are temporary communications
// between members of a class. However, given the style restrictions on the
// number of parameters, it's the least of the evils.
private StringBuilder code;
private StringBuilder accessors;
private JavascriptUtils utils;
private XmlSchema xmlSchema;
public SchemaJavascriptBuilder(SchemaCollection schemaCollection,
NamespacePrefixAccumulator prefixAccumulator, NameManager nameManager) {
this.xmlSchemaCollection = schemaCollection;
this.nameManager = nameManager;
this.prefixAccumulator = prefixAccumulator;
}
public String generateCodeForSchemaCollection(XmlSchemaCollection collection) {
StringBuilder accumulatedCode = new StringBuilder();
for (XmlSchema schema : collection.getXmlSchemas()) {
if (!XmlSchemaConstants.XSD_NAMESPACE_URI.equals(schema.getTargetNamespace())) {
accumulatedCode.append(generateCodeForSchema(schema));
}
}
return accumulatedCode.toString();
}
public String generateCodeForSchema(XmlSchema schema) {
xmlSchema = schema;
code = new StringBuilder();
code.append("//\n");
code.append("// Definitions for schema: " + schema.getTargetNamespace());
if (schema.getSourceURI() != null) {
code.append("\n// " + schema.getSourceURI());
}
code.append("\n//\n");
Map schemaTypes = schema.getSchemaTypes();
for (Map.Entry e : schemaTypes.entrySet()) {
XmlSchemaType type = e.getValue();
if (type instanceof XmlSchemaComplexType) {
try {
XmlSchemaComplexType complexType = (XmlSchemaComplexType)type;
if (!JavascriptUtils.notVeryComplexType(complexType) && complexType.getName() != null) {
complexTypeConstructorAndAccessors(complexType.getQName(), complexType);
complexTypeSerializerFunction(complexType.getQName(), complexType);
domDeserializerFunction(complexType.getQName(), complexType);
}
} catch (UnsupportedConstruct usc) {
LOG.warning(usc.toString());
continue; // it could be empty, but the style checker
// would complain.
}
} else if (type instanceof XmlSchemaSimpleType) {
XmlSchemaSimpleType simpleType = (XmlSchemaSimpleType)type;
if (XmlSchemaUtils.isEumeration(simpleType)) {
List values = XmlSchemaUtils.enumeratorValues(simpleType);
code.append("//\n");
code.append("// Simple type (enumeration) " + simpleType.getQName() + "\n");
code.append("//\n");
for (String value : values) {
code.append("// - " + value + "\n");
}
}
}
}
for (Map.Entry e : schema.getElements().entrySet()) {
XmlSchemaElement element = e.getValue();
try {
if (element.getSchemaTypeName() == null && element.getSchemaType() == null) {
Message message = new Message("ELEMENT_MISSING_TYPE", LOG, element.getQName(),
element.getSchemaTypeName(), schema.getTargetNamespace());
LOG.warning(message.toString());
continue;
}
XmlSchemaType type;
if (element.getSchemaType() != null) {
type = element.getSchemaType();
} else {
type = schema.getTypeByName(element.getSchemaTypeName());
}
if (!(type instanceof XmlSchemaComplexType)) {
// we never make classes for simple type.
continue;
}
XmlSchemaComplexType complexType = (XmlSchemaComplexType)type;
// for named types we don't bother to generate for the
// element.
if (!JavascriptUtils.notVeryComplexType(complexType) && complexType.getName() == null) {
complexTypeConstructorAndAccessors(element.getQName(), complexType);
complexTypeSerializerFunction(element.getQName(), complexType);
domDeserializerFunction(element.getQName(), complexType);
}
} catch (UnsupportedConstruct usc) {
continue; // it could be empty, but the style checker
// would complain.
}
}
String returnValue = code.toString();
LOG.finer(returnValue);
return returnValue;
}
// In general, I (bimargulies) hate fields that are temporary communications
// between members of a class. However, given the style restrictions on the
// number
// of parameters, it's the least of the evils.
public void complexTypeConstructorAndAccessors(QName name, XmlSchemaComplexType type) {
accessors = new StringBuilder();
utils = new JavascriptUtils(code);
List items = XmlSchemaUtils.getContentElements(type, xmlSchemaCollection);
List attrs = XmlSchemaUtils.getContentAttributes(type, xmlSchemaCollection);
final String elementPrefix = "this._";
String typeObjectName = nameManager.getJavascriptName(name);
code.append("//\n");
code.append("// Constructor for XML Schema item " + name.toString() + "\n");
code.append("//\n");
code.append("function " + typeObjectName + " () {\n");
// to assist in debugging we put a type property into every object.
utils.appendLine("this.typeMarker = '" + typeObjectName + "';");
for (XmlSchemaObject thing : items) {
ParticleInfo itemInfo = ParticleInfo.forLocalItem(thing, xmlSchema, xmlSchemaCollection,
prefixAccumulator, type.getQName());
constructOneItem(type, elementPrefix, typeObjectName, itemInfo);
}
for (XmlSchemaAnnotated thing : attrs) {
AttributeInfo itemInfo = AttributeInfo.forLocalItem(thing, xmlSchema, xmlSchemaCollection,
prefixAccumulator, type.getQName());
constructOneItem(type, elementPrefix, typeObjectName, itemInfo);
}
code.append("}\n\n");
code.append(accessors.toString());
}
private void constructOneItem(XmlSchemaComplexType type, final String elementPrefix,
String typeObjectName, ItemInfo itemInfo) {
String accessorSuffix = StringUtils.capitalize(itemInfo.getJavascriptName());
String accessorName = typeObjectName + "_get" + accessorSuffix;
String getFunctionProperty = typeObjectName + ".prototype.get" + accessorSuffix;
String setFunctionProperty = typeObjectName + ".prototype.set" + accessorSuffix;
accessors.append("//\n");
accessors.append("// accessor is " + getFunctionProperty + "\n");
accessors.append("// element get for " + itemInfo.getJavascriptName() + "\n");
if (itemInfo.isAny()) {
accessors.append("// - xs:any\n");
} else {
if (itemInfo.getType() != null) {
accessors.append("// - element type is " + itemInfo.getType().getQName() + "\n");
}
}
if (itemInfo.isOptional()) {
accessors.append("// - optional element\n");
} else {
accessors.append("// - required element\n");
}
if (itemInfo.isArray()) {
accessors.append("// - array\n");
}
if (itemInfo.isNillable()) {
accessors.append("// - nillable\n");
}
accessors.append("//\n");
accessors.append("// element set for " + itemInfo.getJavascriptName() + "\n");
accessors.append("// setter function is is " + setFunctionProperty + "\n");
accessors.append("//\n");
accessors.append("function " + accessorName + "() { return this._" + itemInfo.getJavascriptName()
+ ";}\n\n");
accessors.append(getFunctionProperty + " = " + accessorName + ";\n\n");
accessorName = typeObjectName + "_set" + accessorSuffix;
accessors.append("function " + accessorName + "(value) { this._" + itemInfo.getJavascriptName()
+ " = value;}\n\n");
accessors.append(setFunctionProperty + " = " + accessorName + ";\n");
if (itemInfo.isOptional() || (itemInfo.isNillable() && !itemInfo.isArray())) {
utils.appendLine("this._" + itemInfo.getJavascriptName() + " = null;");
} else if (itemInfo.isArray()) {
utils.appendLine("this._" + itemInfo.getJavascriptName() + " = [];");
} else if (itemInfo.isAny() || itemInfo.getType() instanceof XmlSchemaComplexType) {
// even for required complex elements, we leave them null.
// otherwise, we could end up in a cycle or otherwise miserable. The
// application code is responsible for this.
utils.appendLine("this._" + itemInfo.getJavascriptName() + " = null;");
} else {
if (itemInfo.getDefaultValue() == null) {
itemInfo.setDefaultValue(utils.getDefaultValueForSimpleType(itemInfo.getType()));
}
utils.appendLine("this._" + itemInfo.getJavascriptName() + " = " + itemInfo.getDefaultValue()
+ ";");
}
}
/**
* Produce a serializer function for a type. These functions emit the surrounding element XML if the
* caller supplies an XML element name. It's not quite as simple as that, though. The element name may
* need namespace qualification, and this function will add more namespace prefixes as needed.
*
* @param type
* @return
*/
public void complexTypeSerializerFunction(QName name, XmlSchemaComplexType type) {
StringBuilder bodyCode = new StringBuilder();
JavascriptUtils bodyUtils = new JavascriptUtils(bodyCode);
bodyUtils.setXmlStringAccumulator("xml");
complexTypeSerializerBody(type, "this._", bodyUtils);
utils = new JavascriptUtils(code);
String functionName = nameManager.getJavascriptName(name) + "_" + "serialize";
code.append("//\n");
code.append("// Serialize " + name + "\n");
code.append("//\n");
code.append("function " + functionName + "(cxfjsutils, elementName, extraNamespaces) {\n");
utils.startXmlStringAccumulator("xml");
utils.startIf("elementName != null");
utils.appendString("<");
utils.appendExpression("elementName");
// now add any accumulated namespaces.
String moreNamespaces = prefixAccumulator.getAttributes();
if (moreNamespaces.length() > 0) {
utils.appendString(" ");
utils.appendString(moreNamespaces);
}
utils.startIf("extraNamespaces");
utils.appendExpression("' ' + extraNamespaces");
utils.endBlock();
// attributes
complexTypeSerializeAttributes(type, "this._");
utils.appendString(">");
utils.endBlock();
code.append(bodyCode);
utils.startIf("elementName != null");
utils.appendString("");
utils.endBlock();
utils.appendLine("return xml;");
code.append("}\n\n");
code.append(nameManager.getJavascriptName(name) + ".prototype.serialize = " + functionName + ";\n\n");
}
private void complexTypeSerializeAttributes(XmlSchemaComplexType type, String string) {
@SuppressWarnings("unused")
List attributes = type.getAttributes();
// work in progress.
}
/**
* Build the serialization code for a complex type. At the top level, this operates on single items, so it
* does not pay attention to minOccurs and maxOccurs. However, as it works through the sequence, it
* manages optional elements and arrays.
*
* @param type
* @param elementPrefix
* @param bodyNamespaceURIs
* @return
*/
protected void complexTypeSerializerBody(XmlSchemaComplexType type, String elementPrefix,
JavascriptUtils bodyUtils) {
List items = XmlSchemaUtils.getContentElements(type, xmlSchemaCollection);
for (XmlSchemaObject sequenceItem : items) {
ParticleInfo itemInfo = ParticleInfo.forLocalItem(sequenceItem, xmlSchema, xmlSchemaCollection,
prefixAccumulator, type.getQName());
// If the item is 'any', it could be ANY of our top-level elements.
if (itemInfo.isAny()) {
serializeAny(itemInfo, bodyUtils);
} else {
bodyUtils.generateCodeToSerializeElement(itemInfo, "this._", xmlSchemaCollection);
}
}
}
private void serializeAny(ParticleInfo itemInfo, JavascriptUtils bodyUtils) {
String prefix = "cxfjsany" + anyPrefixCounter;
anyPrefixCounter++;
bodyUtils.generateCodeToSerializeAny(itemInfo, prefix, xmlSchemaCollection);
}
/**
* Generate a JavaScript function that takes an element for a complex type and walks through its children
* using them to fill in the values for a JavaScript object.
*
* @param type schema type for the process
* @return the string contents of the JavaScript.
*/
public void domDeserializerFunction(QName name, XmlSchemaComplexType type) {
utils = new JavascriptUtils(code);
List contentElements = XmlSchemaUtils.getContentElements(type, xmlSchemaCollection);
String typeObjectName = nameManager.getJavascriptName(name);
code.append("function " + typeObjectName + "_deserialize (cxfjsutils, element) {\n");
// create the object we are deserializing into.
utils.appendLine("var newobject = new " + typeObjectName + "();");
utils.appendLine("cxfjsutils.trace('element: ' + cxfjsutils.traceElementName(element));");
utils.appendLine("var curElement = cxfjsutils.getFirstElementChild(element);");
utils.appendLine("var item;");
int nContentElements = contentElements.size();
for (int i = 0; i < contentElements.size(); i++) {
XmlSchemaObject contentElement = contentElements.get(i);
utils.appendLine("cxfjsutils.trace('curElement: ' + cxfjsutils.traceElementName(curElement));");
ParticleInfo itemInfo = ParticleInfo.forLocalItem(contentElement, xmlSchema, xmlSchemaCollection,
prefixAccumulator, type.getQName());
if (itemInfo.isAny()) {
ParticleInfo nextItem = null;
if (i != nContentElements - 1) {
XmlSchemaObject nextThing = contentElements.get(i + 1);
nextItem = ParticleInfo.forLocalItem(nextThing, xmlSchema, xmlSchemaCollection,
prefixAccumulator, type.getQName());
// theoretically, you could have two anys with different
// namespaces.
if (nextItem.isAny()) {
unsupportedConstruct("MULTIPLE_ANY", type.getQName());
}
}
deserializeAny(type, itemInfo, nextItem);
} else {
deserializeElement(type, contentElement);
}
}
utils.appendLine("return newobject;");
code.append("}\n\n");
}
private String buildNamespaceList(String anyNamespaceSpec) {
StringBuilder nslist = new StringBuilder();
String[] namespaces = anyNamespaceSpec.split("\\s");
nslist.append("[ ");
for (int x = 0; x < namespaces.length; x++) {
String ns = namespaces[x];
nslist.append("'");
if ("##targetNamespace".equals(ns)) {
nslist.append(xmlSchema.getTargetNamespace());
} else if ("##local".equals(ns)) {
// nothing, empty string
} else {
nslist.append(ns);
}
nslist.append("'");
if (x < namespaces.length - 1) {
nslist.append(",");
}
}
nslist.append("]");
return nslist.toString();
}
private void deserializeAny(XmlSchemaComplexType type, ParticleInfo itemInfo, ParticleInfo nextItem) {
XmlSchemaAny any = (XmlSchemaAny)itemInfo.getParticle();
boolean array = XmlSchemaUtils.isParticleArray(any);
boolean optional = XmlSchemaUtils.isParticleOptional(any);
if (array) {
utils.appendLine("var anyObject = [];");
} else {
utils.appendLine("var anyObject = null;");
}
String anyNamespaceSpec = any.getNamespace();
// we aren't dealing with any-after-any.
XmlSchemaElement nextElement = null;
if (nextItem != null) {
nextElement = (XmlSchemaElement)nextItem.getParticle();
}
String matchType;
String namespaceList = "[]";
if (anyNamespaceSpec == null || "##any".equals(anyNamespaceSpec) || "".equals(anyNamespaceSpec)) {
matchType = "org_apache_cxf_any_ns_matcher.ANY";
} else if ("##other".equals(anyNamespaceSpec)) {
matchType = "org_apache_cxf_any_ns_matcher.OTHER";
} else if ("##local".equals(anyNamespaceSpec)) {
matchType = "org_apache_cxf_any_ns_matcher.LOCAL";
} else {
matchType = "org_apache_cxf_any_ns_matcher.LISTED";
namespaceList = buildNamespaceList(anyNamespaceSpec);
}
String nextLocalPartConstant = "null";
if (nextElement != null) {
nextLocalPartConstant = "'" + nextElement.getQName().getLocalPart() + "'";
}
utils.appendLine("var matcher = new org_apache_cxf_any_ns_matcher(" + matchType + ", '"
+ xmlSchema.getTargetNamespace() + "'" + ", " + namespaceList + ", "
+ nextLocalPartConstant + ");");
if (array) {
utils.appendLine("var anyNeeded = " + any.getMinOccurs() + ";");
utils.appendLine("var anyAllowed = " + any.getMaxOccurs() + ";");
} else if (optional) {
utils.appendLine("var anyNeeded = 0;");
utils.appendLine("var anyAllowed = 1;");
} else {
utils.appendLine("var anyNeeded = 1;");
utils.appendLine("var anyAllowed = 1;");
}
utils.startWhile("anyNeeded > 0 || anyAllowed > 0");
utils.appendLine("var anyURI;");
utils.appendLine("var anyLocalPart;");
utils.appendLine("var anyMatched = false;");
utils.startIf("curElement");
utils.appendLine("anyURI = cxfjsutils.getElementNamespaceURI(curElement);");
utils.appendLine("anyLocalPart = cxfjsutils.getNodeLocalName(curElement);");
utils.appendLine("var anyQName = '{' + anyURI + '}' + anyLocalPart;");
utils.appendLine("cxfjsutils.trace('any match: ' + anyQName);");
utils.appendLine("anyMatched = matcher.match(anyURI, anyLocalPart)");
utils.appendLine("cxfjsutils.trace(' --> ' + anyMatched);");
utils.endBlock(); // curElement != null
utils.startIf("anyMatched"); // if match
utils.appendLine("anyDeserializer = "
+ "cxfjsutils.interfaceObject.globalElementDeserializers[anyQName];");
utils.appendLine("cxfjsutils.trace(' deserializer: ' + anyDeserializer);");
utils.startIf("anyDeserializer"); // if complex/serializer function
utils.appendLine("var anyValue = anyDeserializer(cxfjsutils, curElement);");
utils.appendElse(); // else complex/serializer function
// TODO: for simple types we really need a dictionary of the simple type qnames.
utils.appendLine("var anyValue = curElement.nodeValue;");
utils.endBlock(); // complex/serializer function
if (array) {
utils.appendLine("anyObject.push(anyValue);");
} else {
utils.appendLine("anyObject = anyValue;");
}
utils.appendLine("anyNeeded--;");
utils.appendLine("anyAllowed--;");
// if we consumed the element, we advance.
utils.appendLine("curElement = cxfjsutils.getNextElementSibling(curElement);");
utils.appendElse(); // match
// non-matching case
utils.startIf("anyNeeded > 0");
utils.appendLine("throw 'not enough ws:any elements';");
utils.endBlock(); // non-match+required
utils.endBlock(); // match/non-match.
utils.endBlock(); // while
utils.appendLine("var anyHolder = new org_apache_cxf_any_holder(anyURI, anyLocalPart, anyValue);");
utils.appendLine("newobject.setAny(anyHolder);");
}
private void deserializeElement(XmlSchemaComplexType type, XmlSchemaObject thing) {
ParticleInfo itemInfo = ParticleInfo.forLocalItem(thing, xmlSchema, xmlSchemaCollection,
prefixAccumulator, type.getQName());
XmlSchemaType itemType = itemInfo.getType();
boolean simple = itemType instanceof XmlSchemaSimpleType
|| JavascriptUtils.notVeryComplexType(itemType);
boolean mtomCandidate = JavascriptUtils.mtomCandidateType(itemType);
String accessorName = "set" + StringUtils.capitalize(itemInfo.getJavascriptName());
utils.appendLine("cxfjsutils.trace('processing " + itemInfo.getJavascriptName() + "');");
XmlSchemaElement element = (XmlSchemaElement)itemInfo.getParticle();
QName elementQName = XmlSchemaUtils.getElementQualifiedName(element, xmlSchema);
String elementNamespaceURI = elementQName.getNamespaceURI();
boolean elementNoNamespace = "".equals(elementNamespaceURI);
XmlSchema elementSchema = null;
if (!elementNoNamespace) {
elementSchema = xmlSchemaCollection.getSchemaByTargetNamespace(elementNamespaceURI);
}
boolean qualified = !elementNoNamespace
&& XmlSchemaUtils.isElementQualified(element, itemInfo.isGlobal(), xmlSchema,
elementSchema);
if (!qualified) {
elementNamespaceURI = "";
}
String localName = elementQName.getLocalPart();
String valueTarget = "item";
if (itemInfo.isOptional() || itemInfo.isArray()) {
utils.startIf("curElement != null && cxfjsutils.isNodeNamedNS(curElement, '"
+ elementNamespaceURI + "', '" + localName + "')");
if (itemInfo.isArray()) {
utils.appendLine("item = [];");
utils.startDo();
valueTarget = "arrayItem";
utils.appendLine("var arrayItem;");
}
}
utils.appendLine("var value = null;");
utils.startIf("!cxfjsutils.isElementNil(curElement)");
if (itemInfo.isAnyType()) {
// use our utility
utils.appendLine(valueTarget + " = org_apache_cxf_deserialize_anyType(cxfjsutils, curElement);");
} else if (simple) {
if (mtomCandidate) {
utils.appendLine(valueTarget + " = cxfjsutils.deserializeBase64orMom(curElement);");
} else {
utils.appendLine("value = cxfjsutils.getNodeText(curElement);");
utils.appendLine(valueTarget + " = " + utils.javascriptParseExpression(itemType, "value")
+ ";");
}
} else {
XmlSchemaComplexType complexType = (XmlSchemaComplexType)itemType;
QName baseQName = complexType.getQName();
if (baseQName == null) {
baseQName = element.getQName();
}
String elTypeJsName = nameManager.getJavascriptName(baseQName);
utils.appendLine(valueTarget + " = " + elTypeJsName + "_deserialize(cxfjsutils, curElement);");
}
utils.endBlock(); // the if for the nil.
if (itemInfo.isArray()) {
utils.appendLine("item.push(arrayItem);");
utils.appendLine("curElement = cxfjsutils.getNextElementSibling(curElement);");
utils.endBlock();
utils.appendLine(" while(curElement != null && cxfjsutils.isNodeNamedNS(curElement, '"
+ elementNamespaceURI + "', '" + localName + "'));");
}
utils.appendLine("newobject." + accessorName + "(item);");
utils.appendLine("var item = null;");
if (!itemInfo.isArray()) {
utils.startIf("curElement != null");
utils.appendLine("curElement = cxfjsutils.getNextElementSibling(curElement);");
utils.endBlock();
}
if (itemInfo.isOptional() || itemInfo.isArray()) {
utils.endBlock();
}
}
private void unsupportedConstruct(String messageKey, Object... args) {
Message message = new Message(messageKey, LOG, args);
throw new UnsupportedConstruct(message);
}
}