Suppose there is such a code:

int caseSwitch = 1;
 
switch (caseSwitch)
{
  case 1:
  Console.WriteLine("Case 1");
  //a lot of code
  break;
 
  case 2:
  case 8-9:
  Console.WriteLine("Case 2 or 8 or 9");
  //a lot of code
  break;
 
  case 3:
  Console.WriteLine("Case 3");
  //a lot of code
  break;
 
  /*...
  a lot conditions
  ...*/
 
  default:
  Console.WriteLine("Default case");
  //a lot of code
  break;
}

In this case

  1. $select;if/elseif/else is not effective because for last conditions will have to iterate through all the previous ones
  2. $case is redundant: you will have to specify the same code multiple times for different values

For this particular case, I suggest using a transition table, for example:

ClassMethod Test(caseSwitch 3) [ ProcedureBlock = 0 ]
{
  s:'$d(^||addr) ^||addr(1)=1,
                (^(2),^(8),^(9))=289,
                 ^(3)=3
  
  @("case"_$g(^||addr(caseSwitch),"def"))
  q  

case1
  "Case 1"
  q
case289
  "Case 2 or 8 or 9"
  q
case3
  "Case 3"
  q
casedef
  "Default case"
  q
}

Of course, you can modify the example to call procedures, functions, class methods, etc.

And if so?

Class dc.EmbedObj Extends %SerialObject
{

Property pOID As %ObjectIdentity Private ];

Property As %String;

Property As %String CalculatedSqlComputeCode = {{*}=##class(dc.EmbedObj).Calcb({pOID})}, SqlComputed ];

ClassMethod Calcb(pOIDAs %String
{
  r=""
  q:pOID="" r

  s $lb(id,cls)=$lfs(pOID,"@")

  p=$system.OBJ.OpenId(cls,id)
  q:'$IsObject(pr

  cls="dc.ContainerObj" {
    r="b"_p.Foobar
  }elseif cls="dc.blablablaContainerObj" {
    r="b"_p.qwe
  }
  r
}
}

Class dc.ContainerObj Extends %Persistent
{

Property Foobar As %String;

Property InnerObj As dc.EmbedObj;

Trigger NewTrigger1 [ Event = INSERT, Foreach = row/object, Time = AFTER ]
{
  oid
  oid={%%ID}_"@"_{%%CLASSNAMEQ}
  &sql(update dc.ContainerObj set InnerObj_pOID=:oid where %ID=:{id})
}

/// d ##class(dc.ContainerObj).Test()
ClassMethod Test()
{
  ..%KillExtent()
  
  t=..%New()
  t.Foobar="foobar1"
  t.InnerObj.a="a1"
  t.%Save()

  &sql(insert into dc.ContainerObj(Foobar,InnerObj_avalues('foobar2','a2'))
  
  ##class(%SQL.Statement).%ExecDirect(,"select * from dc.ContainerObj").%Display()
}
}

You can change the XSLFO Stylesheet dynamically or statically. See XSLFOSTYLESHEET

So, there is a class-report, for example MyApp.ReportDemo.

Series of steps:

  1. remove option XSLFOSTYLESHEET, if any, and recompile class
  2. generate XSLFO Stylesheet:
    SAMPLES>d $system.OBJ.DisplayError(##class(MyApp.ReportDemo).GenerateToFile($system.CSP.GetFileName($system.CSP.GetDefaultApp($zu(5))_"/MyApp.ReportDemo.xsl"),4))
  3. make changes to MyApp.ReportDemo.xsl
  4. add the parameter XSLFOSTYLESHEET and recompile the class again
    /// If defined, this provides a reference to the external
    /// stylesheet to use in generating the XSL-FO (PDF) report.
    /// If it is not provided, a stylesheet will be generated 
    /// from the ReportDisplay XData block.
    Parameter XSLFOSTYLESHEET As String = "MyApp.ReportDemo.xsl";

Now open the report in your browser/command line/etc. Profit!

Important: if you change something in ReportDisplay, you need to repeat the steps again.

Page numbering in Roman Numerals

Need add the attribute format to the element <fo:page-sequence>, i.e.

<fo:page-sequence master-reference='blablabla' format ='I' .. >

Possible value format:

  • format="1" results in 1 2 3 . .
  • format="01" results in 01 02 03
  • format="a" results in a b c . .
  • format="A" results in A B C. .
  • format="i" results in i ii iii iv . .
  • format="I" results in I II III IV . .
  • more..

Example:

  1. Do all the steps according to Zen Report Tutorial
  2. Insert the page number into <pagefooter>
    <!-- Optional Pagefooter element. Does not apply in HTML output. -->
    <pagefooter>
    <item special="page-number"/>
    </pagefooter>
  3. Insert the following code into the class:
    ClassMethod ReplaceStream(
      ByRef stream As %Stream.TmpBinary,
      format) [ Private ]
    {
      tmp=##class(%Stream.TmpBinary).%New()
      while 'stream.AtEnd {
        tmp.Write($replace(stream.Read($$$MaxLocalLength),"<fo:page-sequence ",$$$FormatText("<fo:page-sequence format=%1 ",$$$quote(format))))
      }
      stream.CopyFrom(tmp)
    }
    
    /// d ##class(MyApp.ReportDemo).Test()
    ClassMethod Test(format "1")
    {
      /*
        •0 = XML
        •1 = HTML
        •2 = PDF
        •3 = ToHTML Stylesheet
        •4 = ToXSLFO Stylesheet
        •5 = XSD Schema
        •6 = PrintPS
        •7 = Excel
        •8 = XSLFO
        •9 = ToEXCEL
        •10=xlsx
        •11=TIFF
        •12=pdfprint
        •13=displayxlsx
        •14=fo2pdf
        •15=foandpdf
      */
      
      xslfo=##class(%Stream.TmpBinary).%New()
    
      t=..%New()
      t.GenerateReportToStream(.xslfo,4)
      ..ReplaceStream(.xslfo,format)
      t.toxslfostream=xslfo
      d $system.OBJ.DisplayError(t.GenerateReport("c:\temp\test.pdf",2))
    }
  4. SAMPLES>##class(MyApp.ReportDemo).Test("I")
    or
    SAMPLES>##class(MyApp.ReportDemo).Test("01")
    or
    SAMPLES>##class(MyApp.ReportDemo).Test("A")
  5. open test.pdf. Profit!

Choose to your taste:

Class dc.test Abstract ]
{

ClassMethod Public() [ InternalPrivateProcedureBlock = 0 ]
{
  q
Choice0()
  "Sunday"
Choice1()
  "Monday"
Choice2()
  "Tuesday"
Choice3()
  "Wednesday"
Choice4()
  "Thursday"
Choice5()
  "Friday"
Choice6()
  "Saturday"
Choice()
  set a="entry " _"error"
  return a
}

/// d ##class(dc.test).Test()
ClassMethod Test()
{
  daynum=$zd($h,10)

  "1) ",$case(daynum,
                0:$$Choice0,
                1:$$Choice1,
                2:$$Choice2,
                3:$$Choice3,
                4:$$Choice4,
                5:$$Choice5,
                6:$$Choice6,
                :$$Choice),!

  "2) ",@("$$Choice"_$case(daynum,
                              0:0,
                              1:1,
                              2:2,
                              3:3,
                              4:4,
                              5:5,
                              6:6,
                              :"")),!

  daynum="-"
  
  "3) ",$case(daynum,
                0:"Sunday",
                1:"Monday",
                2:"Tuesday",
                3:"Wednesday",
                4:"Thursday",
                5:"Friday",
                6:"Saturday",
                :$xecute("()"_
                          " set a=""entry "" _""error"""_
                          " return a")),!
}

}

Here is my solution, which has a number of advantages:

  1. there are no restrictions to the order of ID
  2. you can search in queries for the entire timestamp or for parts of it
  3. standard, reliable and proven Caché features are used
  4. this works very, very fast.

See my article for details: Indexing of non-atomic attributes

Class dc.TSOrder Extends (%Persistent%Populate)
{

Index Extent [ ExtentType = bitmap ];

Index iSmartTS On (TS(KEYS), TS(ELEMENTS));

Index iTS On TS;

Property TS As %TimeStamp(MAXVAL "2016-07-31 23:59:59.999999"MINVAL "2016-07-01 00:00:00.000000");

Property Data As %String(MAXLEN 200MINLEN 100);

ClassMethod TSBuildValueArray(
  value,
  ByRef arrayAs %Status
{
  value="" {
    array(0)=value
  }else{
    date=$p(value," ",1),
    time=$p(value," ",2),
    array("date")=date,
    array("time")=time,
    array("ddhh")=$p(date,"-",3)_"-"_$p(time,":",1)
  }
  q $$$OK
}

/// d ##class(dc.TSOrder).Fill()
ClassMethod Fill(= {30e6})
{
  ..%KillExtent(), ..Populate(N), $system.SQL.TuneTable($classname(),$$$YES), $system.OBJ.Compile($classname(),"cu-d")
}

}

Results from SMP:

select distinct null from dc.TSOrder where TS between {ts '2016-07-01 00:00:00.00000'} AND {ts '2016-07-01 23:59:59.999999'}

Performance: 2.339 seconds  2109755 global references 14768692 lines executed
(the number of selected records is 968476, used the normal index)

select distinct null from dc.TSOrder where for some %element(TS) (%key='date' and %value '2016-07-01')

Performance: 2.269 seconds  1936962 global references 15496098 lines executed
(the number of selected records is 968476, used the "smart" index)

select distinct null from dc.TSOrder where for some %element(TS) (%key='ddhh' and %value '01-13')

Performance: 0.096 seconds  80488 global references 644270 lines executed
(the number of selected records is 40239, used the "smart" index)

Localization in Caché DBMS

Simple example:

  1. Class dc.test Extends %ZEN.Component.page
    {
    
    Parameter DOMAIN = "DCTEST";
    
    XData Contents [ XMLNamespace "http://www.intersystems.com/zen" ]
    {
    <page xmlns="http://www.intersystems.com/zen">
      <radioSet id="lng"
        label="Текущий язык"
        layout="vertical"
        displayList="Португальский,Русский"
        valueList="pt-br,ru"
        value="ru"
        onchange="zenPage.changeLang(zenThis.value);"
      />
      <form>
        <text label="Описание" required="true" requiredMessage="обязательно." />
        <submit caption="Сохранить"/>
      </form>
    </page>
    }
    
    /// User clicked to change preferred language.
    ClientMethod changeLang(lng) [ Language = javascript ]
    {
      var ok this.SrvChangeLang(lng);
      self.document.location.reload();
    }
    
    /// Change preferred language for this session and page
    ClassMethod SrvChangeLang(lngAs %Boolean ZenMethod ]
    {
      %session.Language=lng
      %response.Language=lng
      q $$$YES
    }
    
    Method %OnAfterCreatePage() As %Status
    {
      ..%SetValueById("lng",%session.Language)
      Quit $$$OK
    }
    
    }
  2. USER>##class(%MessageDictionary).ExportDomainList("messages_ru.xml","DCTEST","ru")
    
    Result (messages_ru.xml):
    
    <?xml version="1.0" encoding="UTF-8"?>
    <MsgFile Language="ru">
       <MsgDomain Domain="DCTEST">
          <Message Id="358179803">Текущий язык</Message>
          <Message Id="1805419696">Португальский,Русский</Message>
          <Message Id="2153752096">Описание</Message>
          <Message Id="2835101332">обязательно.</Message>
          <Message Id="3683485237">Сохранить</Message>
       </MsgDomain>
    </MsgFile>
    
  3. messages_pt-br.xml:
    
    <?xml version="1.0" encoding="UTF-8"?>
    <MsgFile Language="pt-br">
       <MsgDomain Domain="DCTEST">
          <Message Id="358179803">Idioma atual</Message>
          <Message Id="1805419696">Português,Russo</Message>
          <Message Id="2153752096">Descrição</Message>
          <Message Id="2835101332">é obrigatório.</Message>
          <Message Id="3683485237">Salvar</Message>
       </MsgDomain>
    </MsgFile>
    
    USER>##class(%MessageDictionary).Import("messages_pt-br.xml")
  4. Profit!
USER>w $zv
Cache for Windows (x86-32) 2015.2 (Build 664_3U) Wed Aug 12 2015 12:29:34 EDT

The date is saved to the database correctly.

Class dc.A Extends %Persistent
{
Property DOB As %Date;
}

Class dc.MVC.A Extends %ZEN.DataModel.ObjectDataModel
{

Property DOB As %Date(ZENATTRS "id:DOB|format:DMY|separator:/"ZENCONTROL "dateText");

Method %OnNewSource(Output pSC As %Status = {$$$OK}) As %RegisteredObject
{
  ##class(dc.A).%New()
}

Method %OnOpenSource(
  pID As %String,
  pConcurrency As %Integer -1,
  Output pSC As %Status = {$$$OK}) As %RegisteredObject
{
  ##class(dc.A).%OpenId(pID,pConcurrency,.pSC)
}

Method %OnSaveSource(pSource As dc.AAs %Status
{
  pSource.%Save()
}

ClassMethod %OnDeleteSource(pID As %StringAs %Status
{
  ##class(dc.A).%DeleteId(pID)
}

Method %OnLoadModel(pSource As dc.AAs %Status
{
  ..DOB pSource.DOB
  q $$$OK
}

Method %OnStoreModel(pSource As dc.AAs %Status
{
  pSource.DOB = ..DOB
  q $$$OK
}

}

Class dc.test Extends %ZEN.Component.page
{

XData Contents [ XMLNamespace "http://www.intersystems.com/zen" ]
{
  <page xmlns="http://www.intersystems.com/zen">
    <dataController id="a-controller" modelClass="dc.MVC.A" modelId=""/>
    <dynaForm id="a-form"  controllerId="a-controller"/>
    <button caption="Save" onclick="zen('a-form').save();" />
  </page>
}

}