/* * Copyright 1995, 2003 Perforce Software. All rights reserved. */ /* * Spec.h - spec manipulation * * Description: * * A 'spec' is one of those dumb ascii forms that user edits * to specify various information -- clients, labels, etc. * * The Spec class is a spec definition, indicating what fields * there are and their format. For formatting and parsing, * Spec::Format() and Spec::Parse() expect a subclassed * SpecData object, with SpecData::Get() and SpecData::Put() * defined for moving data between the actual object and the spec. * * The Spec object contains SpecElems to describe each element * of a spec. * * A Spec can be encoded into a simple string for passing between * processes. Spec::Encode() and Spec::Decode() do this. They * are built for interoperability. Namely, Decode() ignores fields * it doesn't understand. * * Finally, a Spec is also be represented as a 'jobspec': this is * actually a form (formatted by Spec) that describes another Spec * (in jobspec's case, a job's Spec). The SpecData that does this * is part of the jobspec code, but that uses Spec::Add(), Get() * and SpecElem::Fmt*() and Set*() for low level construction/ * examination of the spec. * * Spec definition strings: * * A Spec is described by a definition string, parsed by * Spec::Encode() and regenerated (if needed) by Spec::Decode(). * Each item on the form is describe by a substring separated by * other items by ";;". That substring contains a number of codes * separated by ";". Each code is one of the following: * * type:X field type * * type:word single line with N words each * type:wlist list of lines with N words each * type:select single line with a word selected from a list * type:line single line of arbitrary data * type:llist list of lines of arbitrary data * type:date single line with a date on it * type:text block of text spanning any number of lines * type:bulk text that doesn't get indexed (for jobs) * * opt:X how optional the field is * * opt:optional not required * opt:default required, and has a default * opt:required required, but no default * opt:once read-only; set automatically before user gets it * opt:always read-only; field set automatically after user * * pre:X automatic settings (value or condition/value,...) * * pre:X default/once/always value * pre:Y/X on user-defined condition Y set to X * * code:X a unique numeric code identifying the field * len:X advisory length for displaying field * ro old name for opt:always * rq old name for opt:required * seq:X advisory sequence for display field * val:X /-separated list of values for type:select * words:X the number of words for a word/wlist field * * fmt:X advisory format for displaying field * * fmt:none normal (full width) * fmt:L left half only * fmt:R right half only; if follows L goes on same line * fmt:I indented * fmt:C appended comment (not used for p4win) * * Class Defined: * * Spec - the definition of a spec, for parsing and formatting * SpecElem - the definition of a single item type in a spec * SpecData - a spec-specific formatter/parser helper * SpecWords -- array of words in a spec value, allowing surrounding "'s * SpecDataTable -- a SpecData interface to a StrDict * * Virtual methods, to be defined by caller: * * SpecData::Get() - get a data value for stuffing into a spec * SpecData::Set() - set a data value parsed from a spec * * Public methods: * * Spec::Add() -- add a single SpecElem manually, with default values * Spec::Decode() -- decode a spec definition from a string * Spec::Encode() -- encode a spec definition in a transmittable string * Spec::Find() -- find a SpecElem in the spec * Spec::Get() -- find n'th SpecElem in the spec * Spec::GetComment() -- return the spec's comment string * Spec::Format() -- turn SpecData into a spec string * Spec::Parse() -- parse a spec string into SpecData * Spec::ParseNoValid() -- parse without validating 'select' items * Spec::SetComment() -- set the spec's comment string * * SpecElem::FmtOpt() - format the SpecOpt for jobspec * SpecElem::FmtType() - format the SpecType for jobspec * SpecElem::FmtFmt() - format the SpecFmt for jobspec * SpecElem::Is*() - ask various questions about the SpecType * SpecElem::SetOpt() - parse the SpecOpt from a jobspec * SpecElem::SetType() - format the SpecOpt for a jobspec * SpecElem::Compare() - compare SpecElems from different specs */ #ifndef _spec_h_ #define _spec_h_ class VarArray; class SpecData; class SpecElem; class StrBufDict; const int SpecWordsMax = 10; // for SDT_WORD, WLIST, SELECT enum SpecType { SDT_WORD, // single line, N words SDT_WLIST, // multiple lines, N words SDT_SELECT, // SDT_WORD from a list of words SDT_LINE, // single line of text (arbitrary words) SDT_LLIST, // multiple lines of text (arbitrary words) SDT_DATE, // SDT_LINE that is a date SDT_TEXT, // block of text, SDT_BULK // SDT_TEXT not indexed } ; enum SpecOpt { SDO_OPTIONAL, // not required, user updatable, no default SDO_DEFAULT, // not required, user updatable, default provided SDO_REQUIRED, // required, user updatable, default provided SDO_ONCE, // required, not updatable, set once after creation SDO_ALWAYS, // required, not updatable, set after every update SDO_KEY // required, not updatable, set once before creation } ; enum SpecFmt { SDF_NORMAL, // no hint given SDF_LEFT, // left half only SDF_RIGHT, // right half only; if follows LEFT goes on same line SDF_INDENT, // indented SDF_COMMENT // append comment to list } ; enum SpecOpen { SDO_NOTOPEN, // default, field does not isolate to a client at all SDO_ISOLATE, // field is isolated when a spec is opened SDO_PROPAGATE // as ISOLATE, plus field propagates on integrate } ; class Spec { public: Spec(); Spec( const char *encoded, const char *cmt, Error *e ); ~Spec(); // Using the Spec -- formatting and parsing forms StrBuf * Format( SpecData *data ) { StrBuf *s = new StrBuf; Format( data, s ); return s; } void Format( SpecData *data, StrBuf *result ); void Format( SpecData *data, StrDict *result ); void Parse( const char *buf, SpecData *data, Error *e, int valid ); void Parse( const char *buf, SpecData *data, Error *e ) { Parse( buf, data, e, 1 ); } void ParseNoValid( const char *buf, SpecData *data, Error *e ) { Parse( buf, data, e, 0 ); } // Manipulating the Spec itself -- building and examining it SpecElem * Add( const StrPtr &tag ); SpecElem * Get( int i ); SpecElem * Find( const StrPtr &tag, Error *e = 0 ); SpecElem * Find( int code, Error *e = 0 ); int Count(); void Decode( StrPtr *encoded, Error *e ); void Encode( StrBuf *encoded ); const StrPtr * GetComment() { return &comment; } void SetComment( const StrPtr &c ) { comment = c; } SpecElem * Add( char *t ) { return Add( StrRef( t ) ); } private: StrRef comment; VarArray *elems; StrBuf decoderBuffer; } ; class SpecElem { public: // Type access int IsDate() { return type == SDT_DATE; } int IsSelect() { return type == SDT_SELECT; } int IsText() { return type == SDT_TEXT || type == SDT_BULK; } int IsList() { return type == SDT_WLIST || type == SDT_LLIST; } int IsWords() { return type == SDT_WORD || type == SDT_WLIST || type == SDT_SELECT; } int IsSingle() { return type == SDT_WORD || type == SDT_SELECT || type == SDT_LINE || type == SDT_DATE; } int CheckValue( StrBuf &value ); // Opt access int IsRequired() { return opt == SDO_REQUIRED || opt == SDO_KEY; } int IsReadOnly() { return opt == SDO_ONCE || opt == SDO_ALWAYS || opt == SDO_KEY; } int NeedsDefault() { return opt == SDO_DEFAULT || opt == SDO_ALWAYS || opt == SDO_ONCE || opt == SDO_KEY; } // Preset access const StrPtr GetPreset( const char *name = 0 ); int HasPreset() { return GetPreset().Length(); } void SetPresets( const char *x ) { presets = x; } StrPtr & GetPresets() { return presets; } int HasPresets() { return presets.Length(); } // Fmt access SpecFmt GetFmt() { return fmt; } int GetSeq() { return seq; } // Open access int IsOpenable() { return open != SDO_NOTOPEN; } int IsPropagating() { return open == SDO_PROPAGATE; } // Type building -- so jobspec can create a spec const char * FmtOpt(); const char * FmtType(); const char * FmtFmt(); const char * FmtOpen(); void SetSeq( int s ) { seq = s; } void SetOpt( const char *optName, Error *e ); void SetFmt( const char *fmtName, Error *e ); void SetType( const char *s, Error *e ); void SetOpen( const char *openName, Error *e ); int Compare( const SpecElem &other ); public: // only to SpecData's subclasses SpecType type; // how it is formatted StrBuf tag; // name of the field StrBuf presets; // (pre)set codes StrBuf values; // what values can be had int code; // what it's use it StrBuf subCode; // user's code char nWords; // how many words on the line short maxLength; // advisory SpecOpt opt; // how field is updated SpecOpen open; // how field is opened char maxWords; // max words on the line. Streams private: friend class Spec; void Decode( StrRef *s, Error *e ); void Encode( StrBuf *s, int code ); // gui hints SpecFmt fmt; // format code int seq; // display sequence number // reference back to Get(index) int index; // Tmp for presets StrBuf preset; // tmp for GetPreset() } ; // Cautionary note to caller that SpecWords (ie. tVal.wv[0]) are not // cleared between tag invocations in a spec form. class SpecWords : public StrBuf { public: int Split(); void Join( int wc ); const char *wv[ SpecWordsMax + 1 ]; } ; class SpecData { public: virtual ~SpecData() {} // Extract data from or build data into user's data structure. // Spec::Format() calls Get(); Spec::Parse() calls Set(). // One of the two sets of Get/Set must be replaced in the subclass. // This interface assumes whole lines. Its default implementation // calls the word-oriented Get/Set and Joins/Splits them into // whole lines. virtual StrPtr *GetLine( SpecElem *sd, int x, const char **cmt ); virtual void SetLine( SpecElem *sd, int x, const StrPtr *val, Error *e ); virtual void SetComment( SpecElem *sd, int x, const StrPtr *val, int nl, Error *e ); // This interface has words-oriented lines split apart. // The const version casts and calls the non-const version, // for compatibility. // The non-const one has a bogus default implementation. virtual int Get( SpecElem *sd, int x, const char **wv, const char **cmt ); virtual void Set( SpecElem *sd, int x, const char **wv, Error *e ); virtual void Comment( SpecElem *sd, int x, const char **wv, int nl, Error *e ); virtual int Get( SpecElem *sd, int x, char **wv, char **cmt ); virtual void Set( SpecElem *sd, int x, char **wv, Error *e ); virtual void Comment( SpecElem *sd, int x, char **wv, int nl, Error *e ); protected: SpecWords tVal; } ; class SpecDataTable : public SpecData { public: SpecDataTable( StrDict *dict = 0 ); virtual ~SpecDataTable(); virtual StrPtr *GetLine( SpecElem *sd, int x, const char **cmt ); virtual void SetLine( SpecElem *sd, int x, const StrPtr *val, Error *e ); virtual void SetComment( SpecElem *sd, int x, const StrPtr *val, int nl, Error *e ); StrDict * Dict() { return table; } private: int privateTable; StrDict *table; StrBuf empty; } ; #endif /* _spec_h_ */