Branch: Tag:

2001-03-13

2001-03-13 20:21:46 by Martin Stjernholm <mast@lysator.liu.se>

Introduced two new types RXML.t_string and RXML.t_plain to replace
RXML.t_text. RXML.t_text is now equivalent to RXML.t_plain, which
means that it no longer is a supertype for all text and string types.
Also allow a result type to be a subtype of the surrounding content
type. Fixed a bogus debug check in rxml_index.

Rev: server/etc/modules/RXML.pmod/module.pmod:1.138

2:   //!   //! Created 1999-07-30 by Martin Stjernholm.   //! - //! $Id: module.pmod,v 1.137 2001/03/13 17:08:29 mast Exp $ + //! $Id: module.pmod,v 1.138 2001/03/13 20:21:46 mast Exp $      //! Kludge: Must use "RXML.refs" somewhere for the whole module to be   //! loaded correctly.
36:   //! o The type system will be developed further, and the API in the   //! Type class might change as advanced types gets implemented.   //! Don't make assumptions about undocumented behavior. Declare - //! data properly with the types RXML.TXml, RXML.THtml and - //! RXML.TText to let the parser handle the necessary conversions + //! data properly with the types RXML.t_xml, RXML.t_html and + //! RXML.t_plain to let the parser handle the necessary conversions   //! instead of doing it yourself. Try to avoid implementing types.   //!   //! o Various utilities have FIXME's in their documentation. Needless
109:    //! type specifies a parser, it'll be used on the argument value.    //! Note that the order in which arguments are parsed is arbitrary.    -  Type def_arg_type = t_text (PEnt); +  Type def_arg_type = t_plain (PEnt);    //! The type used for arguments that isn't present in neither    //! req_arg_types nor opt_arg_types. This default is a parser that    //! only parses XML-style entities.
117:    Type content_type = t_same (PXml);    //! The handled type of the content, if the tag gets any.    //! -  //! This default is the special type t_same, which means the type is -  //! taken from the effective type of the result. The PXml argument -  //! causes the standard XML parser to be used to read it, which -  //! means that the content is preparsed with XML syntax. Use no -  //! parser to get the raw text. +  //! This default is the special type @[RXML.t_same], which means the +  //! type is taken from the effective type of the result. The @[PXml] +  //! argument causes the standard XML parser to be used to read it, +  //! which means that the content is preparsed with XML syntax. Use +  //! no parser to get the raw text.    //! -  //! Note: You probably want to change this to t_text (without -  //! parser) if the tag is a processing instruction (see -  //! FLAG_PROC_INSTR). +  //! Note: You probably want to change this to @[RXML.t_plain] +  //! (without parser) if the tag is a processing instruction (see +  //! @[FLAG_PROC_INSTR]).    -  array(Type) result_types = ({t_xml, t_html, t_text}); +  array(Type) result_types = ({t_xml, t_html, t_plain});    //! The possible types of the result, in order of precedence. If a    //! result type has a parser, it'll be used to parse any strings in -  //! the exec array returned from Frame.do_enter() and similar +  //! the exec array returned from @[Frame.do_enter] and similar    //! callbacks.    //! -  //! When the tag is used in content of some type, it suffices that -  //! the content type is a subtype of any type in result_types. The -  //! tag must therefore be prepared to produce result of more -  //! specific types than those declared here. I.e. the extreme case, -  //! t_any, means that this tag takes the responsibility to produce -  //! result of any type that's asked for, not that it has the liberty -  //! to produce results of any type it chooses. +  //! When the tag is used in content of some type, the content type +  //! may be a supertype of any type in @[result_types], but it may +  //! also be a subtype of any of them. The tag must therefore be +  //! prepared to produce result of more specific types than those +  //! declared here. I.e. the extreme case, @[RXML.t_any], means that +  //! this tag takes the responsibility to produce result of any type +  //! that's asked for, not that it has the liberty to produce results +  //! of any type it chooses.       //!program Frame;    //!object(Frame) Frame();
256: Inside #if defined(MODULE_DEBUG)
  #ifdef MODULE_DEBUG    if (mixed err = catch {   #endif -  splice_args = t_text (PEnt)->eval (splice_args, ctx, 0, parser, 1); +  splice_args = t_plain (PEnt)->eval (splice_args, ctx, 0, parser, 1);   #ifdef MODULE_DEBUG    }) {    if (objectp (err) && ([object] err)->thrown_at_unwind)
804:    //! and @[scope_name] are set to where this @[Value] object was    //! found. Note that @[scope_name] can be on the form    //! @tt{scope.index1.index2...@} when this object was encountered -  //! through subindexing. Either @[nil] or the undefined value may be -  //! returned if the variable doesn't have a value. +  //! through subindexing. Either @[RXML.nil] or the undefined value +  //! may be returned if the variable doesn't have a value.    //!    //! If the @[type] argument is given, it's the type the returned    //! value should have. If the value can't be converted to that type,
870:    void|string scope_name, void|Type type)    //! Called to get the value of a variable in the scope. @[var] is    //! the name of it, @[ctx] and @[scope_name] are set to where this -  //! @[Scope] object was found. Either @[nil] or the undefined value -  //! may be returned if the variable doesn't exist in the scope. +  //! @[Scope] object was found. Either @[RXML.nil] or the undefined +  //! value may be returned if the variable doesn't exist in the +  //! scope.    //!    //! If the @[type] argument is given, it's the type the returned    //! value should have, unless it's an object which implements
894:    //!    //! An RXML error may be thrown if the value is not acceptable for    //! the variable. It's undefined what happens if a variable is set -  //! to @[nil]; it should be avoided. +  //! to @[RXML.nil]; it should be avoided.    {parse_error ("Cannot set variable" + _in_the_scope (scope_name) + ".\n");}       array(string) _indices (void|Context ctx, void|string scope_name)
903:    //! was found.    //!    //! There's no guarantee that the returned variable names produce a -  //! value (i.e. neither @[nil] nor the undefined value) when +  //! value (i.e. neither @[RXML.nil] nor the undefined value) when    //! indexed.    {parse_error ("Cannot list variables" + _in_the_scope (scope_name) + ".\n");}   
1706:   constant FLAG_DONT_REPORT_ERRORS = FLAG_DONT_RECOVER; // For compatibility.      constant FLAG_RAW_ARGS = 0x00004000; - //! Special flag to @[t_xml.format_tag]; only defined here as a - //! placeholder. When this is given to @[t_xml.format_tag], it only - //! encodes the argument quote character with the "Roxen encoding" - //! when writing argument values, instead of encoding with entity - //! references. It's intended for reformatting a tag which has been - //! parsed by Parser.HTML() (or parse_html()) but hasn't been + //! Special flag to @[RXML.t_xml.format_tag]; only defined here as a + //! placeholder. When this is given to @[RXML.t_xml.format_tag], it + //! only encodes the argument quote character with the "Roxen + //! encoding" when writing argument values, instead of encoding with + //! entity references. It's intended for reformatting a tag which has + //! been parsed by Parser.HTML() (or parse_html()) but hasn't been   //! processed further.      //! The following flags specifies whether certain conditions must be
2466:    this_object());   #endif    Type ptype = parser->type; +  find_result_type: { +  // First check if any of the types is a subtype of the +  // wanted type. If so, we can use it directly.    foreach (tag->result_types, Type rtype) -  if (ptype == rtype) { +  if (rtype->subtype_of (ptype)) {    result_type = rtype; -  break; +  break find_result_type;    } -  else if (ptype->subtype_of (rtype)) { +  // Then check if any of the types is a supertype of the +  // wanted type. If so, set the result type to the wanted +  // type, since the tag has the responsibility to produce a +  // value of that type. +  foreach (tag->result_types, Type rtype) +  if (ptype->subtype_of (rtype)) {    result_type = ptype (rtype->_parser_prog); -  break; +  break find_result_type;    } -  if (!result_type) +     parse_error (    "Tag returns " +    String.implode_nicely ([array(string)] tag->result_types->name, "or") +    " but " + [string] parser->type->name + " is expected.\n"); -  +  }    THIS_TAG_DEBUG (sprintf ("Resolved result_type to %O from surrounding %O\n",    result_type, ptype));    }
3089: Inside #if defined(MODULE_DEBUG)
   val = nil;   #ifdef MODULE_DEBUG    else if (mixed err = scope_got_type && want_type && +  !(objectp (val) && ([object] val)->rxml_var_eval) &&    catch (want_type->type_check (val)))    if (([object] err)->is_RXML_Backtrace)    error ("%O->`[] didn't return a value of the correct type:\n%s",
3603:   //! properties of the type. It may also contain a Parser program that   //! will be used to read text and evaluate values of this type. Note   //! that the parser is not relevant for type checking. + //! + //! @note The doc for this class is rather lengthy, but most of it + //! applies only to type implementors. It's very much simpler to use a + //! type than to implement one; typical users only need to choose one + //! and use @[encode], @[subtype_of], @[`()] or @[eval] in it.   {    constant is_RXML_Type = 1;    -  //! Interface. +  // Services    -  //! @decl constant string name; -  //! -  //! Unique type identifier. Required and considered constant. -  //! -  //! If it contains a "/", it's treated as a MIME type and should -  //! then follow the rules for a MIME type with subtype (RFC 2045, -  //! section 5.1). Among other things, that means that the valid -  //! characters are, besides the "/", US-ASCII values 33-126 except -  //! "(", ")", "<", ">", "@@", ",", ";", ":", "\", """, "/", "[", -  //! "]", "?" and "=". -  //! -  //! If it doesn't contain a "/", it's treated as a type outside the -  //! MIME system, e.g. "int" for an integer. Any type that can be -  //! mapped to a MIME type should be so. -  //! -  //! Type hierarchies are currently implemented with glob patterns, -  //! e.g. "image/png" is a subtype of "image/*". However, this syntax -  //! will be developed further. -  //! -  //! A type in the type hierarchy should be able to express any value -  //! that any of its subtypes can express without (or with at most -  //! negligible) loss of information, but not necessarily on the same -  //! form. This is different from the type trees described by -  //! @[conversion_type], although it's always preferable if a -  //! supertype also can be used as @[conversion_type] in its -  //! subtypes. -  -  //!int sequential; -  //! Nonzero if data of this type is sequential, defined as: -  //! o One or more data items can be concatenated with `+. -  //! o (Sane) parsers are homomorphic on the type, i.e. -  //! eval ("da") + eval ("ta") == eval ("da" + "ta") -  //! and -  //! eval ("data") + eval ("") == eval ("data") -  //! provided the data is only split between (sensibly defined) -  //! atomic elements. -  -  //!mixed empty_value; -  //! The empty value, i.e. what eval ("") would produce. -  -  Type conversion_type; -  //! The type to use as middlestep in indirect conversions. Required -  //! and considered constant. It should be zero (not @[t_any]) if -  //! there is no sensible conversion type. The @[conversion_type] -  //! references must never produce a cycle between types. -  //! -  //! It's values of the conversion type that @[decode] tries to -  //! return, and also that @[encode] must handle without resorting to -  //! indirect conversions. It's used as a fallback between types -  //! which doesn't have explicit conversion functions for each other; -  //! see @[indirect_convert]. -  //! -  //! @note The trees described by the conversion types aren't proper -  //! type hierarchies in the sense of value set sizes, as opposed to -  //! the relations expressed by the glob patterns in @[name]. The -  //! conversion type is chosen purely on pragmatic grounds for doing -  //! indirect conversions. It's better if the conversion type is a -  //! supertype (i.e. has a larger value set), but in lack of proper -  //! supertypes it may also be a subtype, to make it possible to use -  //! indirect conversion for at least a subset of the values. See the -  //! example in @[decode]. -  -  //!int free_text; -  //! Nonzero if the type keeps the free text between parsed tokens, -  //! e.g. the plain text between tags in XML. The type must be -  //! sequential and use strings. -  -  //! @decl optional constant mixed entity_syntax; -  //! -  //! Nonzero constant for all types with string values that use -  //! entity syntax, like XML or HTML. -  -  void type_check (mixed val, void|string msg, mixed... args); -  //! Checks whether the given value is a valid one of this type. -  //! Errors are thrown as RXML parse errors, and in that case @[msg], -  //! if given, is prepended to the error message with ": " between -  //! them. If there are any more arguments on the line, the prepended -  //! message is formatted with @tt{sprintf (@[msg], @@@[args])@}. -  //! There's a @[type_check_error] helper that can be used to handle -  //! the message formatting and error throwing. -  -  mixed encode (mixed val, void|Type from); -  //! Converts the given value to this type. -  //! -  //! If the @[from] type isn't given, the function should try to -  //! convert it to the required internal form for this type, using a -  //! cast as a last resort if the type of @[val] isn't recognized. It -  //! should then encode it, if necessary, as though it were a literal -  //! (typically only applicable for types using strings with -  //! encodings, like the @[t_xml] type). Any conversion error, -  //! including in the cast, should be thrown as an RXML parse error. -  //! -  //! If the @[from] type is given, it's the type of the value. If -  //! it's @[t_any], the function should (superficially) check the -  //! value and return it without conversion. Otherwise, if the encode -  //! function doesn't have routines to explicitly handle a conversion -  //! from that type, then indirect conversion using -  //! @[conversion_type] should be done. The @[indirect_convert] -  //! function implements that. The encode function should at least be -  //! able to convert values of @[conversion_type] to this type, or -  //! else throw an RXML parse error if it isn't possible. -  //! -  //! @note Remember to override @[convertible] if this function can -  //! convert directly from any type besides the conversion type. -  //! Don't count on that the conversion type tree is constant so that -  //! the default implementation would return true anyway. -  -  optional mixed decode (mixed val); -  //! Converts the value, which is of this type, to a value of type -  //! @[conversion_type]. If this function isn't defined, then any -  //! value of this type works directly in the conversion type. -  //! -  //! If the type can't be converted, an RXML parse error should be -  //! thrown. That might happen if the value contains markup or -  //! similar that can't be represented in the conversion type. -  //! -  //! E.g. in a type for XML markup which have @[t_text] as the -  //! conversion type, this function should return a literal string -  //! only if the text doesn't contain tags, otherwise it should throw -  //! an error. It should never both decode e.g. "&lt;" to "<" and -  //! just leave literal "<" in the string. It should also not parse -  //! the value with some evaluating parser (see @[get_parser]) since -  //! the value should only change representation. (This example shows -  //! that a more fitting conversion type for XML markup would be a -  //! DOM type that can represent XML node trees, since values -  //! containing tags could be decoded then.) -  -  Type clone() -  //! Returns a copy of the type. Exists only for overriding purposes; -  //! it's normally not useful to call this since type objects are -  //! shared. -  { -  Type newtype = object_program ((object(this_program)) this_object())(); -  newtype->_parser_prog = _parser_prog; -  newtype->_parser_args = _parser_args; -  newtype->_t_obj_cache = _t_obj_cache; -  return newtype; -  } -  -  string format_tag (string|Tag tag, void|mapping(string:string) args, -  void|string content, void|int flags) -  //! Returns a formatted tag according to the type. tag is either a -  //! tag object or the name of the tag. Throws an error if this type -  //! cannot format tags. -  { -  parse_error ("Cannot format tags with type %s.\n", this_object()->name); -  } -  -  string format_entity (string entity) -  //! Returns a formatted entity according to the type. Throws an -  //! error if this type cannot format entities. -  { -  parse_error ("Cannot format entities with type %s.\n", this_object()->name); -  } -  -  //! Services. -  +     int `== (mixed other)    //! Returns nonzero iff this type is the same as @[other], i.e. has    //! the same name. If @[other] is known to be a type, it's somewhat
3973:    return res;    }    +  // Interface +  +  //! @decl constant string name; +  //! +  //! Unique type identifier. Required and considered constant. +  //! +  //! If it contains a "/", it's treated as a MIME type and should +  //! then follow the rules for a MIME type with subtype (RFC 2045, +  //! section 5.1). Among other things, that means that the valid +  //! characters are, besides the "/", US-ASCII values 33-126 except +  //! "(", ")", "<", ">", "@@", ",", ";", ":", "\", """, "/", "[", +  //! "]", "?" and "=". +  //! +  //! If it doesn't contain a "/", it's treated as a type outside the +  //! MIME system, e.g. "int" for an integer. Any type that can be +  //! mapped to a MIME type should be so. +  //! +  //! Type hierarchies are currently implemented with glob patterns, +  //! e.g. "image/png" is a subtype of "image/*". However, this syntax +  //! will be developed further. +  //! +  //! A type in the type hierarchy should be able to express any value +  //! that any of its subtypes can express without (or with at most +  //! negligible) loss of information, but not necessarily on the same +  //! form. This is different from the type trees described by +  //! @[conversion_type], although it's always preferable if a +  //! supertype also can be used as @[conversion_type] in its +  //! subtypes. +  //! +  //! There are however exceptions to the rule above about information +  //! preservation, since it's impossible to satisfy it for +  //! sufficiently generic types. E.g. the type @[RXML.t_any] cannot +  //! express hardly any value (except @[RXML.nil]) without loss of +  //! information. +  +  constant sequential = 0; +  //! Nonzero if data of this type is sequential, defined as: +  //! o One or more data items can be concatenated with `+. +  //! o (Sane) parsers are homomorphic on the type, i.e. +  //! eval ("da") + eval ("ta") == eval ("da" + "ta") +  //! and +  //! eval ("data") + eval ("") == eval ("data") +  //! provided the data is only split between (sensibly defined) +  //! atomic elements. +  +  //!mixed empty_value; +  //! The empty value, i.e. what eval ("") would produce. +  +  Type conversion_type; +  //! The type to use as middlestep in indirect conversions. Required +  //! and considered constant. It should be zero (not @[RXML.t_any]) +  //! if there is no sensible conversion type. The @[conversion_type] +  //! references must never produce a cycle between types. +  //! +  //! It's values of the conversion type that @[decode] tries to +  //! return, and also that @[encode] must handle without resorting to +  //! indirect conversions. It's used as a fallback between types +  //! which doesn't have explicit conversion functions for each other; +  //! see @[indirect_convert]. +  //! +  //! @note The trees described by the conversion types aren't proper +  //! type hierarchies in the sense of value set sizes, as opposed to +  //! the relations expressed by the glob patterns in @[name]. The +  //! conversion type is chosen purely on pragmatic grounds for doing +  //! indirect conversions. It's better if the conversion type is a +  //! supertype (i.e. has a larger value set), but in lack of proper +  //! supertypes it may also be a subtype, to make it possible to use +  //! indirect conversion for at least a subset of the values. See the +  //! example in @[decode]. +  +  //!int free_text; +  //! Nonzero if the type keeps the free text between parsed tokens, +  //! e.g. the plain text between tags in XML. The type must be +  //! sequential and use strings. +  +  //! @decl optional constant int entity_syntax; +  //! +  //! Nonzero constant for all types with string values that use +  //! entity syntax, like XML or HTML. +  +  void type_check (mixed val, void|string msg, mixed... args); +  //! Checks whether the given value is a valid one of this type. +  //! Errors are thrown as RXML parse errors, and in that case @[msg], +  //! if given, is prepended to the error message with ": " between +  //! them. If there are any more arguments on the line, the prepended +  //! message is formatted with @tt{sprintf (@[msg], @@@[args])@}. +  //! There's a @[type_check_error] helper that can be used to handle +  //! the message formatting and error throwing. +  +  mixed encode (mixed val, void|Type from); +  //! Converts the given value to this type. +  //! +  //! If the @[from] type isn't given, the function should try to +  //! convert it to the required internal form for this type, using a +  //! cast as a last resort if the type of @[val] isn't recognized. It +  //! should then encode it, if necessary, as though it were a literal +  //! (typically only applicable for types using strings with +  //! encodings, like the @[RXML.t_xml] type). Any conversion error, +  //! including in the cast, should be thrown as an RXML parse error. +  //! +  //! If the @[from] type is given, it's the type of the value. If +  //! it's @[RXML.t_any], the function should (superficially) check +  //! the value and return it without conversion. Otherwise, if the +  //! encode function doesn't have routines to explicitly handle a +  //! conversion from that type, then indirect conversion using +  //! @[conversion_type] should be done. The @[indirect_convert] +  //! function implements that. The encode function should at least be +  //! able to convert values of @[conversion_type] to this type, or +  //! else throw an RXML parse error if it isn't possible. +  //! +  //! @note Remember to override @[convertible] if this function can +  //! convert directly from any type besides the conversion type. +  //! Don't count on that the conversion type tree is constant so that +  //! the default implementation would return true anyway. +  +  optional mixed decode (mixed val); +  //! Converts the value, which is of this type, to a value of type +  //! @[conversion_type]. If this function isn't defined, then any +  //! value of this type works directly in the conversion type. +  //! +  //! If the type can't be converted, an RXML parse error should be +  //! thrown. That might happen if the value contains markup or +  //! similar that can't be represented in the conversion type. +  //! +  //! E.g. in a type for XML markup which have @[RXML.t_text] as the +  //! conversion type, this function should return a literal string +  //! only if the text doesn't contain tags, otherwise it should throw +  //! an error. It should never both decode e.g. "&lt;" to "<" and +  //! just leave literal "<" in the string. It should also not parse +  //! the value with some evaluating parser (see @[get_parser]) since +  //! the value should only change representation. (This example shows +  //! that a more fitting conversion type for XML markup would be a +  //! DOM type that can represent XML node trees, since values +  //! containing tags could be decoded then.) +  +  Type clone() +  //! Returns a copy of the type. Exists only for overriding purposes; +  //! it's normally not useful to call this since type objects are +  //! shared. +  { +  Type newtype = object_program ((object(this_program)) this_object())(); +  newtype->_parser_prog = _parser_prog; +  newtype->_parser_args = _parser_args; +  newtype->_t_obj_cache = _t_obj_cache; +  return newtype; +  } +  +  string format_tag (string|Tag tag, void|mapping(string:string) args, +  void|string content, void|int flags) +  //! Returns a formatted tag according to the type. tag is either a +  //! tag object or the name of the tag. Throws an error if this type +  //! cannot format tags. +  { +  parse_error ("Cannot format tags with type %s.\n", this_object()->name); +  } +  +  string format_entity (string entity) +  //! Returns a formatted entity according to the type. Throws an +  //! error if this type cannot format entities. +  { +  parse_error ("Cannot format entities with type %s.\n", this_object()->name); +  } +     static final void type_check_error (string msg1, array args1,    string msg2, mixed... args2)    //! Helper intended to format and throw an RXML parse error in
4040:    parse_error ("Cannot convert type %s to %s.\n", from->name, this_object()->name);    }    -  // Internals. +  // Internals       program/*(Parser)HMM*/ _parser_prog = PNone;    // The parser to use. Should never be changed in a type object.
4073:      TAny t_any = TAny();   //! A completely unspecified nonsequential type. Every type is a - //! subtype of this one. This type is also special in that any value - //! can be converted to this type without changing it (i.e. with no - //! intermediate @[Type.decode] calls). + //! subtype of this one. + //! + //! This type is also special in that any value can be converted to + //! and from this type without the value getting changed in any way, + //! which means that the meaning of a value might change when this + //! type is used as a middle step. + //! + //! E.g if @tt{"<foo>"@} of type @[RXML.t_plain] is converted directly + //! to @[RXML.t_xml], it's quoted to @tt{"&lt;foo&gt;"@}, since + //! @[RXML.t_plain] always is literal text. However if it's first + //! converted to @[RXML.t_any] and then to @[RXML.t_xml], it still + //! remains @tt{"<foo>"@}, which then carries a totally different + //! meaning.      static class TAny   {
4111:       Nil encode (mixed val, void|Type from)    { -  if (from) -  if (from->name == TAny.name) -  type_check (val); -  else { +  if (from && from != local::name)    val = indirect_convert (val, from);   #ifdef MODULE_DEBUG    type_check (val);   #endif -  } +     return nil;    }   
4145:      TScalar t_scalar = TScalar();   //! Any type of scalar, i.e. text or number. It's not sequential, as - //! opposed to the subtype t_text. + //! opposed to the subtype @[RXML.t_text].   //!   //! FIXME: This is currently not labeled as a supertype for t_text and   //! t_num, so it's only marginally useful. It's name will probably
4299:      // Text types.    - TText t_text = TText(); - //! The type for generic document text. Labelled "text/*" and thus - //! acts as a supertype for all text types. + TString t_string = TString(); + //! Any type of string; acts as a supertype for all text types. + //! Conversion to and from this type and other text types is similar + //! to @[RXML.t_any] in that the value doesn't change, which means + //! that its meaning might change (for an example see the doc for + //! @[RXML.t_any]).    - static class TText + static class TString   {    inherit Type;    constant name = "text/*";
4324:    switch (from->name) {    case TAny.name: type_check (val); // Fall through.    case local::name: return [string] val; -  default: return [string] indirect_convert (val, from); +  default: +  if (from->subtype_of (this_object())) { + #ifdef MODULE_DEBUG +  type_check (val); + #endif +  return [string] val; +  } +  return [string] indirect_convert (val, from);    case TScalar.name:    }    mixed err = catch {return (string) val;};
4343:    string _sprintf() {return "RXML.t_text" + OBJ_COUNT;}   }    + TPlain t_plain = TPlain(); + //! The type for plain text. +  + static class TPlain + { +  inherit TString; +  constant name = "text/plain"; +  +  string encode (mixed val, void|Type from) +  { +  if (from) +  switch (from->name) { +  case TAny.name: type_check (val); // Fall through. +  case TString.name: case local::name: return [string] val; +  default: return [string] indirect_convert (val, from); +  case TScalar.name: +  } +  mixed err = catch {return (string) val;}; +  parse_error ("Cannot convert value to %s: %s", name, describe_error (err)); +  } + } +  + TPlain t_text = t_plain; // For compatibility. +    THtml t_xml = TXml();   //! The type for XML and similar markup.      static class TXml   { -  inherit TText; +  inherit TString;    constant name = "text/xml"; -  Type conversion_type = t_text; +  Type conversion_type = t_plain;    constant entity_syntax = 1;    constant encoding_type = "xml"; // For compatibility.    -  // FIXME: type_check is not really strict. +  // Note: type_check is not strict.       string encode (mixed val, void|Type from)    {    if (from)    switch (from->name) {    case TAny.name: type_check (val); // Fall through. -  case local::name: return [string] val; +  case TString.name: case local::name: return [string] val;    default: return [string] indirect_convert (val, from); -  case TText.name: +  case TPlain.name:    }    else if (mixed err = catch {val = (string) val;})    parse_error ("Cannot convert value to %s: %s", name, describe_error (err));
4455:    string encode (mixed val, void|Type from)    {    if (from && from->name == local::name) -  return ::encode (val, t_xml); +  return [string] val;    else    return ::encode (val, from);    }    -  string decode (mixed val) -  { -  return [string] val; -  } +  constant decode = 0; // Cover it; not needed here.       string _sprintf() {return "RXML.t_html" + OBJ_COUNT;}   }