1 / 73

ODS Markup: The power of choice and change.

Introduction. Tagsets are SimpleTagsets are PowerfulTagsets are Fun. Why Tagsets?. FreedomFlexibilityWork Avoidance. ODS HTML: Towing the line of Mediocrity. Anyone thinking that ODS produces garbage html?Then have a look at the clean html that the followingprogram produces (release 8.2 re

liora
Télécharger la présentation

ODS Markup: The power of choice and change.

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


    2. Introduction The goals of this presentation are to get you started using tagsets. Tagsets are useful in many ways. Obviously they can be used to create markup output the way you want it. But not so obvious is that tagsets can be a very useful for understanding and using ODS. Another goal is to show you enough about tagsets that you will begin to think of new and amazing ways to use them for your own needs. This is going to be our mantra. Tagsets are Simple. Tagsets are Powerful. Tagsets are Fun. The goals of this presentation are to get you started using tagsets. Tagsets are useful in many ways. Obviously they can be used to create markup output the way you want it. But not so obvious is that tagsets can be a very useful for understanding and using ODS. Another goal is to show you enough about tagsets that you will begin to think of new and amazing ways to use them for your own needs. This is going to be our mantra. Tagsets are Simple. Tagsets are Powerful. Tagsets are Fun.

    3. Why Tagsets? Freedom Flexibility Work Avoidance How many of you use ODS HTML? How many of you would like to change the html that ODS creates? Yep. Me too!How many of you use ODS HTML? How many of you would like to change the html that ODS creates? Yep. Me too!

    4. ODS HTML: Towing the line of Mediocrity I think Victor Bos said it best. Viktor posted this to a newsgroup one day after learning about the existance of tagsets. He had already created his very own html tagset. I think Victor Bos said it best. Viktor posted this to a newsgroup one day after learning about the existance of tagsets. He had already created his very own html tagset.

    5. ODS HTML: On the path to mediocrity Six years old 14000 lines of C-code Heavy Maintenance requirement Inflexible A little history lesson is in order. Six years ago, when I created ODS HTML, I also created ODS LaTeX, and ODS SGML. Those last two never saw the light of day. A tribute to the amount of work required to maintain ODS HTML. There weren't a lot of choices te be made about html. The corporate browser was still Mosaic. Table support had just been added to html. HTML didn't have a lot of features. Even so, ODS HTML was thousands of lines of code. It was so much work, that I couldn't do anything else. I knew then something needed to change. Probably the worst thing about ODS HTML is that it is impossible to do all the things that were needed or wanted by users. Every change impacts everyone. That really limits what features can be changed or added. A little history lesson is in order. Six years ago, when I created ODS HTML, I also created ODS LaTeX, and ODS SGML. Those last two never saw the light of day. A tribute to the amount of work required to maintain ODS HTML. There weren't a lot of choices te be made about html. The corporate browser was still Mosaic. Table support had just been added to html. HTML didn't have a lot of features. Even so, ODS HTML was thousands of lines of code. It was so much work, that I couldn't do anything else. I knew then something needed to change. Probably the worst thing about ODS HTML is that it is impossible to do all the things that were needed or wanted by users. Every change impacts everyone. That really limits what features can be changed or added.

    6. It's not just HTML anymore HTML 4.0 508 Compliance XHTML WML Docbook XML LaTeX Troff Over time the HTML specifications grew. The Browser wars began, and nothing seemed to be standard. ODS HTML stayed the middle ground. I quickly learned to be careful about what we did with the html ODS created. I was limited to doing things that would upset the least number of people. Mediocre HTML was the result. 14000 lines of C-code. Then XML came along. I couldn't imagine creating a new ODS output destiniation for each XML DTD we decided to support. 14000 lines of code apiece? No way, Not Me! Clearly we needed a better way to create and support new output types. Supporting the various XML formats was impossible Over time the HTML specifications grew. The Browser wars began, and nothing seemed to be standard. ODS HTML stayed the middle ground. I quickly learned to be careful about what we did with the html ODS created. I was limited to doing things that would upset the least number of people. Mediocre HTML was the result. 14000 lines of C-code. Then XML came along. I couldn't imagine creating a new ODS output destiniation for each XML DTD we decided to support. 14000 lines of code apiece? No way, Not Me! Clearly we needed a better way to create and support new output types. Supporting the various XML formats was impossible

    7. Benefits of tagsets 1000's of lines of Code shorter Much more flexible Easier maintenance Supports multiple types of output Downloadable fixes and updates User contributions So, really I created ods Markup for selfish reasons. Less Code. More flexibility, easier maintenance. Easily supports different types of markup Downloadable updates. One of my favorites: Users can make themselves happy. Users can add value to ODS, actually contribute new output destinations, and fixes. Much more like open source. Encourages sharing of new output types. So, really I created ods Markup for selfish reasons. Less Code. More flexibility, easier maintenance. Easily supports different types of markup Downloadable updates. One of my favorites: Users can make themselves happy. Users can add value to ODS, actually contribute new output destinations, and fixes. Much more like open source. Encourages sharing of new output types.

    8. Knowledge is Power Tagsets are Proc Template. Tagsets are Simple. Self Revealing Knowledge is power. So lets get started. Tagsets are templates. That means Proc Template. Tagsets are simple. Oxymoron? Not this time. The other key to learning ODS Markup and tagsets is ODS Markup it's self. Using the right tagset/output can illuminate how ODS Markup works. Knowledge is power. So lets get started. Tagsets are templates. That means Proc Template. Tagsets are simple. Oxymoron? Not this time. The other key to learning ODS Markup and tagsets is ODS Markup it's self. Using the right tagset/output can illuminate how ODS Markup works.

    9. Getting a list So now your wondering what tagsets there are and how to find them. This part is proc template. If you are famiilar with proc template you know that it keeps templates in subdirectories. We keep all of our tagsets in the tagsets directory. So to list all the tagsets you would do this. Proc template; list tagsets; run; This is just a piece of the list you will get. The current count of supplied tagsets is 43.So now your wondering what tagsets there are and how to find them. This part is proc template. If you are famiilar with proc template you know that it keeps templates in subdirectories. We keep all of our tagsets in the tagsets directory. So to list all the tagsets you would do this. Proc template; list tagsets; run; This is just a piece of the list you will get. The current count of supplied tagsets is 43.

    10. HTML Tagsets PHTML snands for plain html. It uses limited stylesheets and formatting. HTMLCSS is a more complex html that uses stylesheets as much as possible. HTML4 is what you get if you use ODS HTML. It has modifications above and beyond HTMLCSS to make it look and act like the old ODS HTML. CHTML is Compact HTML it is a subset of the HTML specification intended for use with phones and pda's. I-Mode is a subset of CHTML used in Japan for all phones. WML is Wireless Markup Language and is used in Europe for pda's and phones.PHTML snands for plain html. It uses limited stylesheets and formatting. HTMLCSS is a more complex html that uses stylesheets as much as possible. HTML4 is what you get if you use ODS HTML. It has modifications above and beyond HTMLCSS to make it look and act like the old ODS HTML. CHTML is Compact HTML it is a subset of the HTML specification intended for use with phones and pda's. I-Mode is a subset of CHTML used in Japan for all phones. WML is Wireless Markup Language and is used in Europe for pda's and phones.

    11. If I had a dollar.... Here's a simple example of what we can do with tagsets. I wish I had a dollar for everytime someone asked for this change in ods html. Lots of people never noticed this. The people that did, seemed to hate it. In version 8.1 I put a non-breaking space in all anchors. This makes the anchors work better. But it also puts a space in the output. The output looked like this: <a name=IDX> </a> It's an easy thing to remove the non-breaking space in tagsets. Here's a simple example of what we can do with tagsets. I wish I had a dollar for everytime someone asked for this change in ods html. Lots of people never noticed this. The people that did, seemed to hate it. In version 8.1 I put a non-breaking space in all anchors. This makes the anchors work better. But it also puts a space in the output. The output looked like this: <a name=IDX> </a> It's an easy thing to remove the non-breaking space in tagsets.

    12. Creating a new tagset Changing a tagset through inheritance is the easiest way to get the output you want. We know which tagset we want to change. Why start over from scratch? This is how you tell your new tagset about it's parents. Parent = tagsets.htmlcss; We want to make a modification to the htmlcss tagset So that is what we use as our new tagset's parent.. Tagset inheritance is very simple. Your new tagset gets everything your parent and grandparents define. A tagset can only have one parent. If you want to change an event you have to completely redefine it. That's it. Simple. Not like styles.... Changing a tagset through inheritance is the easiest way to get the output you want. We know which tagset we want to change. Why start over from scratch? This is how you tell your new tagset about it's parents. Parent = tagsets.htmlcss; We want to make a modification to the htmlcss tagset So that is what we use as our new tagset's parent.. Tagset inheritance is very simple. Your new tagset gets everything your parent and grandparents define. A tagset can only have one parent. If you want to change an event you have to completely redefine it. That's it. Simple. Not like styles....

    13. A completely new output destination! The next part is a redefinition of the anchor event. The anchor event is what prints out the anchor code. We just create a completely new one that does what we want. The next part is a redefinition of the anchor event. The anchor event is what prints out the anchor code. We just create a completely new one that does what we want.

    14. Using our New tagset. To use our new tagset we just have tell ods markup. Here's what your ods statement might look like. BTW. This change was so sought after, I made it a part of the html tagsets that we provide. There is no longer a need for anyone to do this. But if you want the non-breaking space back.... To use our new tagset we just have tell ods markup. Here's what your ods statement might look like. BTW. This change was so sought after, I made it a part of the html tagsets that we provide. There is no longer a need for anyone to do this. But if you want the non-breaking space back....

    15. May the source be with you You can see the source code for any tagset you like. Here's how you would get the source code for the csv tagset. Proc template; source tagsets.csv; run; You can also download the most current version at this link. (V8). Then use your favorite editor.You can see the source code for any tagset you like. Here's how you would get the source code for the csv tagset. Proc template; source tagsets.csv; run; You can also download the most current version at this link. (V8). Then use your favorite editor.

    16. Using an Existing stylesheet Another common request is to use an existing corporate stylesheet with ods output. This is really easy to do with tagsets. To create a tagset that uses an existing tagset is a simple thing. Everything is simple with tagsets, isn't it? Just pick the tags you want to assign style classes too, and redefine them with new class attributes.Another common request is to use an existing corporate stylesheet with ods output. This is really easy to do with tagsets. To create a tagset that uses an existing tagset is a simple thing. Everything is simple with tagsets, isn't it? Just pick the tags you want to assign style classes too, and redefine them with new class attributes.

    17. This is part of the source for the csv tagset. One of the common complaints about this tagset is that the output starts off with two blank lines. If I had a dollar, for every time I've been asked to fix this, I might mbe able to buy 2 hot dogs on pleasure Island. Looking at what we have here we can see that each table starts with a newline and so does each row. So right off we have 2 newlines before any data comes out.This is part of the source for the csv tagset. One of the common complaints about this tagset is that the output starts off with two blank lines. If I had a dollar, for every time I've been asked to fix this, I might mbe able to buy 2 hot dogs on pleasure Island. Looking at what we have here we can see that each table starts with a newline and so does each row. So right off we have 2 newlines before any data comes out.

    18. Using the same trick as before. We create a completely new tagset that inherits all the functionality of the tagset we wish to change. We only needed to change the table and row events. So we redefine them using the original code as a reference. We just moved the newlines from the beginning of the table and row to the end of the table and each row. Now the first table will be at the very top of the file. Each table will be separated by one blank line. BTW, there is no need for anyone to do this. I have incorporated the change into all of the csv tagsets. Using the same trick as before. We create a completely new tagset that inherits all the functionality of the tagset we wish to change. We only needed to change the table and row events. So we redefine them using the original code as a reference. We just moved the newlines from the beginning of the table and row to the end of the table and each row. Now the first table will be at the very top of the file. Each table will be separated by one blank line. BTW, there is no need for anyone to do this. I have incorporated the change into all of the csv tagsets.

    19. Using your new tagset Using our new csv tagset is easy. Here's what the ods statement might look like. This is my favorite way of closing my ods output. It doesn't matter how many ods statements I open and it doesn't matter if I change a tagset name. It always closes any ods destinations I have opened.Using our new csv tagset is easy. Here's what the ods statement might look like. This is my favorite way of closing my ods output. It doesn't matter how many ods statements I open and it doesn't matter if I change a tagset name. It always closes any ods destinations I have opened.

    20. The ODS Markup statement With ODS Markup it is up to the tagset to support stylesheets or any other file for that matter. If the tagsets put in support, embedded stylesheets are automatic if a stylesheet is not specified on the ods statement.With ODS Markup it is up to the tagset to support stylesheets or any other file for that matter. If the tagsets put in support, embedded stylesheets are automatic if a stylesheet is not specified on the ods statement.

    21. Events. What, Where, When? Packages of data There is no 'Where', Only when. Created as needed to provide context to the data they contain. Vary according to the procedure and job being run. Events can be thought of as a package of data. The event it's self provides context to the data it contains. Events are requested from within sas as needed. Worst of all they vary according to what you do with sas. Not very helpful, so far... So, what are these events and why do they vary so much. Events can be thought of as a package of data. The event it's self provides context to the data it contains. Events are requested from within sas as needed. Worst of all they vary according to what you do with sas. Not very helpful, so far... So, what are these events and why do they vary so much.

    22. Looking under the hood How many of you read the paper before coming here? Did that diagram on the second page scare the daylights out of you? This one isn't so bad... It leaves some pesky details out. Everything starts with the ODS statement. Each ods destination statement creates an ODA, Output Delivery Agent. The ODA keeps track of the files, the style, and other various things. The ODA is also what gives the output it's over-all structure. The ODA handles system titles, notes and bylines, Even page breaks and the directory structure of the results. Then we run a procedure. The procedure has data and in most cases a table template to go with that data. The Procedure talks to the ODS server when it wants to create output objects. The server talks to each ODA, telling it about the output object coming it's way. It also creates a viewer for the type of data that it is working with. The most common viewer is the table viewer. It makes a table out of a set of observations. Each observation, and value becomes an event. By this time our event has data, formatting information from the table template and style information from the ods style. But the Viewer's arent' the only ones creating output. The ODA is creating events too. Among many others the ODA is generating events for Titles, Notes, and bylines. also events. So there are all these requests going to the tagset. The tagset looks for the requested event. If it's there then something happens. We get output. If the event isn't there. Nothing happens. Very quietly. WHEW!How many of you read the paper before coming here? Did that diagram on the second page scare the daylights out of you? This one isn't so bad... It leaves some pesky details out. Everything starts with the ODS statement. Each ods destination statement creates an ODA, Output Delivery Agent. The ODA keeps track of the files, the style, and other various things. The ODA is also what gives the output it's over-all structure. The ODA handles system titles, notes and bylines, Even page breaks and the directory structure of the results. Then we run a procedure. The procedure has data and in most cases a table template to go with that data. The Procedure talks to the ODS server when it wants to create output objects. The server talks to each ODA, telling it about the output object coming it's way. It also creates a viewer for the type of data that it is working with. The most common viewer is the table viewer. It makes a table out of a set of observations. Each observation, and value becomes an event. By this time our event has data, formatting information from the table template and style information from the ods style. But the Viewer's arent' the only ones creating output. The ODA is creating events too. Among many others the ODA is generating events for Titles, Notes, and bylines. also events. So there are all these requests going to the tagset. The tagset looks for the requested event. If it's there then something happens. We get output. If the event isn't there. Nothing happens. Very quietly. WHEW!

    23. Do all events go to all files? Ok, but how do the events get to the different files? All files created by ods markup have a basic set of events. The most basic set is the open and close of the doocument it's self, and the head section if that document. In HTML that would be <head> and <meta> <link> etc. they all have a <body> section as well. The exception to this is the stylesheet file which has only an open and close event. The frame has more events unique to it. Theses events are for creating HTML framesets. The stylesheet file has events specifically for creating stylesheet definitions. But for the most part, all events are sent to the the Body file.Ok, but how do the events get to the different files? All files created by ods markup have a basic set of events. The most basic set is the open and close of the doocument it's self, and the head section if that document. In HTML that would be <head> and <meta> <link> etc. they all have a <body> section as well. The exception to this is the stylesheet file which has only an open and close event. The frame has more events unique to it. Theses events are for creating HTML framesets. The stylesheet file has events specifically for creating stylesheet definitions. But for the most part, all events are sent to the the Body file.

    24. The Body File These are all the events that will go to the body file if nothing is run between the ods statement and the ods close.These are all the events that will go to the body file if nothing is run between the ods statement and the ods close.

    25. The Contents File The Contents file has a different set of events that automatically occur. Any table of contents that might get created by running procedures would go inside contents_list.The Contents file has a different set of events that automatically occur. Any table of contents that might get created by running procedures would go inside contents_list.

    26. The Frame File The frame file is complete with just the few events that it has. But it is possible to writeother things to it. It is possible to create more frames, or use it for an entirely different purpose as with our default ods markup destination. The frame file is complete with just the few events that it has. But it is possible to writeother things to it. It is possible to create more frames, or use it for an entirely different purpose as with our default ods markup destination.

    27. The Stylesheet File The stylesheet file gets a lot of information written directly to it. Most of it is the style_class event, which is fired over and over with different style information.The stylesheet file gets a lot of information written directly to it. Most of it is the style_class event, which is fired over and over with different style information.

    28. The Data & Code Files The Data and Code files are very similar. They are meant to be used and abused for any purpose you might dream up. For example, I've used them to capture warnings and display them in a separate frame.The Data and Code files are very similar. They are meant to be used and abused for any purpose you might dream up. For example, I've used them to capture warnings and display them in a separate frame.

    29. Event attributes File Values: Body Contents Pages Frame Stylesheet Code Data Events have 3 attributes. The most used is the file attribute. The file attribute directs the event's output to the desired file. Most events are initially requested from the body file. They are then redirected to the other files. The entire table of contents is created this way. The style attribute tells the event which ods style it wants to use. This will over-ride any style it would have used normally. pure_style tells ODS markup to allow all style attributes to be seen. Without it, Any style attributes that are already defined in the stylesheet will be screened out.Events have 3 attributes. The most used is the file attribute. The file attribute directs the event's output to the desired file. Most events are initially requested from the body file. They are then redirected to the other files. The entire table of contents is created this way. The style attribute tells the event which ods style it wants to use. This will over-ride any style it would have used normally. pure_style tells ODS markup to allow all style attributes to be seen. Without it, Any style attributes that are already defined in the stylesheet will be screened out.

    30. Getting output to other files define event leaf; file=CONTENTS; put "<li>"; trigger hyperlink / if any(URL, ANCHOR); put VALUE / if !any(URL, ANCHOR); put "</li>" CR; end; This is how the chtml tagset creates a table of contents in the contents file. The branch and leaf tagsets are generated form within ODS everytime a folder or node is created in the results window. We use the trigger command to trigger an event of my own, the contents_leaf event is shown here. The contents leaf attribute uses the file attribute to redirect the event to the contents file. If there was no contents file specified on the ODS statement then nothing will happen.This is how the chtml tagset creates a table of contents in the contents file. The branch and leaf tagsets are generated form within ODS everytime a folder or node is created in the results window. We use the trigger command to trigger an event of my own, the contents_leaf event is shown here. The contents leaf attribute uses the file attribute to redirect the event to the contents file. If there was no contents file specified on the ODS statement then nothing will happen.

    31. For every beginning... Since it is desirable to divide an event into a beginning and an ending Events can contain a begin section and an end section. You're probably wondering about the start and finish. Well, I really wanted them to be begin and end. But SAS wouldn't let me. Specifically the grammar processor, but that's another geeky story. Each event can have a start and finish. Most events are scoped. They may contain other events. Generally this means that the event is requested twice. Once at the beginning and once at the end. The html table event is obvious in this case.Since it is desirable to divide an event into a beginning and an ending Events can contain a begin section and an end section. You're probably wondering about the start and finish. Well, I really wanted them to be begin and end. But SAS wouldn't let me. Specifically the grammar processor, but that's another geeky story. Each event can have a start and finish. Most events are scoped. They may contain other events. Generally this means that the event is requested twice. Once at the beginning and once at the end. The html table event is obvious in this case.

    32. The Mapping Tagsets Event_map Short_map Text_map Tpl_style_map Tpl_style_list OdsXrpcs Pyx This is how tagssets are self revealing. I'm sure you're still wondering about those events, what are they and how can I find them? I'm not telling you what all the events are. You could create a tagset that would list out the event names for you. But despite what you might think, knowing the names of all the events doesn't do you much good. It's more important to know the context of the event and how it fits in with all the others. These tagsets will help with that. With the exception of text_map all the 'map' tagsets create xml. Text map is a purely textual output with comments explaining things along the way. Event map is rather verbose and can be raather painful to look at unless you have a nice xml editor/viewer. If you like XML with tags that are 3 lines long you'll love this tagset. Short map is just like event map but with a few choice attributes instead of every single one. The tpl tagsets show information about the table and style templates being used for any given event Best of all, if there is a tagset that has the information you want then look at it's source. This is how tagssets are self revealing. I'm sure you're still wondering about those events, what are they and how can I find them? I'm not telling you what all the events are. You could create a tagset that would list out the event names for you. But despite what you might think, knowing the names of all the events doesn't do you much good. It's more important to know the context of the event and how it fits in with all the others. These tagsets will help with that. With the exception of text_map all the 'map' tagsets create xml. Text map is a purely textual output with comments explaining things along the way. Event map is rather verbose and can be raather painful to look at unless you have a nice xml editor/viewer. If you like XML with tags that are 3 lines long you'll love this tagset. Short map is just like event map but with a few choice attributes instead of every single one. The tpl tagsets show information about the table and style templates being used for any given event Best of all, if there is a tagset that has the information you want then look at it's source.

    33. The Default Event These mapping tagsets use the default event as their primary way of creating output. If the tagset has a default event, then it needs no other event. Generally when an event is requested, If the tagset doesn't define it, nothing happens. But, If there is a default event then it will be used instead. If you really want a list of events this tagset will doit. This is a tagset that will create a non-unique, unordered list of event names. Of course the event list will vary, depending on the sas job you run.... The doit event will fire for every single event request the tagset receives.These mapping tagsets use the default event as their primary way of creating output. If the tagset has a default event, then it needs no other event. Generally when an event is requested, If the tagset doesn't define it, nothing happens. But, If there is a default event then it will be used instead. If you really want a list of events this tagset will doit. This is a tagset that will create a non-unique, unordered list of event names. Of course the event list will vary, depending on the sas job you run.... The doit event will fire for every single event request the tagset receives.

    34. Creating a tagset from scratch The easiest way to create a tagset from scratch is to use one of the mapping tagsets as a parent. The first time you run your new tagset you will see all sorts of things you never want to see again. The easiest thing to do is identify them and create an empty event definition. Once you get your tagset the way you want it you can remove the parent and all the empty events. The easiest way to create a tagset from scratch is to use one of the mapping tagsets as a parent. The first time you run your new tagset you will see all sorts of things you never want to see again. The easiest thing to do is identify them and create an empty event definition. Once you get your tagset the way you want it you can remove the parent and all the empty events.

    35. Tagsets, the syntax Purposely Simple Painfully Simple The experience of explaining table temlpates or Style templates was enough to convince me that I needed to do something different with tagsets. Tagsets are very simple. Very Consistent. The simplicity sometimes causes complexity but it does not usually prevent anything. The experience of explaining table temlpates or Style templates was enough to convince me that I needed to do something different with tagsets. Tagsets are very simple. Very Consistent. The simplicity sometimes causes complexity but it does not usually prevent anything.

    36. Tagset Attributes Tagsets have several attributes which can be set. The most important is probably the map and mapsub attributes. Most markup languages have special characters that will wreak havoc if they are found within the markup. For html the most important ones are <> and &. Map and Mapsub tell ODS characters to look out for and what it should replace them with when it finds them. The nobreakspace attribute tell ODS markup what to use for non-breaking spaces Many markup languages do not have such a thing. But it is nice to specify if they do. The split attribute tells ODS markup what to use for splitting lines. This comes into play in split character processing. The embedded_stylesheet attribute tells ODS markup that this tagset supports embedded stylesheets. This will cause the events which create stylesheets to be triggered in the head section of the body and contents files. But only if a stylesheet is not specified. The Output_type attribute tells SAS what the output type of the files will be. In the case of latex style information is also processed differently. Lognote causes text to be printed to the log whenever the tagset is used.Tagsets have several attributes which can be set. The most important is probably the map and mapsub attributes. Most markup languages have special characters that will wreak havoc if they are found within the markup. For html the most important ones are <> and &. Map and Mapsub tell ODS characters to look out for and what it should replace them with when it finds them. The nobreakspace attribute tell ODS markup what to use for non-breaking spaces Many markup languages do not have such a thing. But it is nice to specify if they do. The split attribute tells ODS markup what to use for splitting lines. This comes into play in split character processing. The embedded_stylesheet attribute tells ODS markup that this tagset supports embedded stylesheets. This will cause the events which create stylesheets to be triggered in the head section of the body and contents files. But only if a stylesheet is not specified. The Output_type attribute tells SAS what the output type of the files will be. In the case of latex style information is also processed differently. Lognote causes text to be printed to the log whenever the tagset is used.

    37. Tagsets are not Data Null. Tagsets are not Datastep. They are not like legacy sas languages. Here's what Jack Hamilton had to say about tagsets after Sugi 26. Anyone that knows me will tell you my loyalties lie elsewhere. Python! And it does show. Once you know. Jack has learned to love tagsets since. :-)Tagsets are not Datastep. They are not like legacy sas languages. Here's what Jack Hamilton had to say about tagsets after Sugi 26. Anyone that knows me will tell you my loyalties lie elsewhere. Python! And it does show. Once you know. Jack has learned to love tagsets since. :-)

    38. The put statement Strings Variables Newlines ( nl | cr | lf ) In any combination. Ok, so what is the syntax. Most simply there is the put statement. Put takes a list of strings and variables. It concatenates them together and prints them to the output file. NL may also be specified at any time. NL will insert a newline at that point. Assuming foreground has the value of blue, this put would create output that looks like this. Ok, so what is the syntax. Most simply there is the put statement. Put takes a list of strings and variables. It concatenates them together and prints them to the output file. NL may also be specified at any time. NL will insert a newline at that point. Assuming foreground has the value of blue, this put would create output that looks like this.

    39. Label, Value Pairs When a string is followed by a variable, which often happens in xml and html, the string and variable become a pair. If the variable has no value then neither of them are output. In this example if none of the attributes have no value then the output becomes very simple. Put and putq, the difference, is putq will automatically double quote any values which come from variables. Put will not. That's all. There is also 'putl'. Putl is just like put but it automatically adds a newline to the end. This is handy for wrapping blocks of existing text that is already formatted. When a string is followed by a variable, which often happens in xml and html, the string and variable become a pair. If the variable has no value then neither of them are output. In this example if none of the attributes have no value then the output becomes very simple. Put and putq, the difference, is putq will automatically double quote any values which come from variables. Put will not. That's all. There is also 'putl'. Putl is just like put but it automatically adds a newline to the end. This is handy for wrapping blocks of existing text that is already formatted.

    40. The problem with Variables... Event variables: ~113 of them. Basic things like name, label, value. Many of them originate in the table template. Style variables: ~72 of them. Anything you can define in a style template. Dynamic variables: ??? Generated by graph and the XML engine. Dynamic say's it all. Memory variables: User defined within the tagset. Stream variables: User defined within the tagset. The problem with variables, is that they are so variable. Variables, there are lots of them. There are event variables. There are style variables. There are dynamic variables. There are user variables. Event variables: ~114. Most of the variables you'll use. Things like name, value, label, event_name, etc. Style variables.: ~72. Anything you can define in a style template. Dynamic variables: Created internally by graph and the XML engine. Completely variable by event and job. User variables: Created entirely within the tagset. Streams: Created entirely within the tagset. Persisted to Disk.The problem with variables, is that they are so variable. Variables, there are lots of them. There are event variables. There are style variables. There are dynamic variables. There are user variables. Event variables: ~114. Most of the variables you'll use. Things like name, value, label, event_name, etc. Style variables.: ~72. Anything you can define in a style template. Dynamic variables: Created internally by graph and the XML engine. Completely variable by event and job. User variables: Created entirely within the tagset. Streams: Created entirely within the tagset. Persisted to Disk.

    41. Dynamic Variables Dynamic variables are particularly problematic. I don't even know how many there are. Let alone what there names are. I know there are a lot of them. Just take a look at the graph tagset. Dynamic variables are created on the fly as needed. You will get a different set of them if you do a scatterplot instead of a bar chart.Dynamic variables are particularly problematic. I don't even know how many there are. Let alone what there names are. I know there are a lot of them. Just take a look at the graph tagset. Dynamic variables are created on the fly as needed. You will get a different set of them if you do a scatterplot instead of a bar chart.

    42. Memory variables Designated by $ Set syntax is just like put, Without the newlines. Variables can be set to themselves. Variables are global to all events. I already gave some of this away in some of the previous examples. User variables can be created at any time. They are always designated by a preceding $. The syntax is just like everything else. A list of strings and variables followed by an optional if. The set statement is evaluated left side first, so setting a variable to it's self works fine. Unset all or unset _all_ will completely romeve all user variables. I already gave some of this away in some of the previous examples. User variables can be created at any time. They are always designated by a preceding $. The syntax is just like everything else. A list of strings and variables followed by an optional if. The set statement is evaluated left side first, so setting a variable to it's self works fine. Unset all or unset _all_ will completely romeve all user variables.

    43. The set statement Strings Variables In any combination. Righthand evaluation first Set is just like put in every way. Except that the first variable must be a $ or $$ variable. This is where all the other values will be put. Set does not accept newlines. Assuming foreground has the value of blue, this put would create output that looks like this. Set is just like put in every way. Except that the first variable must be a $ or $$ variable. This is where all the other values will be put. Set does not accept newlines. Assuming foreground has the value of blue, this put would create output that looks like this.

    44. Memory variables User variables can be created at any time. They are always designated by a preceding $. The syntax is just like everything else. A list of strings and variables followed by an optional if. The set statement is evaluated left side first, so setting a variable to it's self works fine. Unset all or unset _all_ will completely romeve all user variables. There is also the putlog statment. Putlog is just like put, without the newlines. The output from putlog goes to log. It's pretty handy for debugging... User variables can be created at any time. They are always designated by a preceding $. The syntax is just like everything else. A list of strings and variables followed by an optional if. The set statement is evaluated left side first, so setting a variable to it's self works fine. Unset all or unset _all_ will completely romeve all user variables. There is also the putlog statment. Putlog is just like put, without the newlines. The output from putlog goes to log. It's pretty handy for debugging...

    45. Stream variables Just like User variables Designated by $$ Can hold large amounts of data Not limited by memory Can be opened, closed and flushed. Streams are just like user variables with some additions. Most importantly Streams can hold very large amounts of data. They are not limited by memory. They are kept on disk as needed. Streams can be opened and closed for output. It might be easiest to think of them as files that always open for append. Streams are just like user variables with some additions. Most importantly Streams can hold very large amounts of data. They are not limited by memory. They are kept on disk as needed. Streams can be opened and closed for output. It might be easiest to think of them as files that always open for append.

    46. Streams by example Doing an Open will cause all puts to be redirected to the stream until it encounters another open or a close. Put $$<stream> and putstream <stream> are roughly equivalent. Except that putstream only takes one argument, a stream name. Set wil erase the contents of a stream and start over. Setting a stream to it's self can be expensive if the lefthand arguments don't have the stream as the first argument. In other words, prepending to stream using set is a bad idea. Delstream and unset are identical. Doing an Open will cause all puts to be redirected to the stream until it encounters another open or a close. Put $$<stream> and putstream <stream> are roughly equivalent. Except that putstream only takes one argument, a stream name. Set wil erase the contents of a stream and start over. Setting a stream to it's self can be expensive if the lefthand arguments don't have the stream as the first argument. In other words, prepending to stream using set is a bad idea. Delstream and unset are identical.

    47. A variable, variable list For version 9 there is a new statement that helps out a lot. Putvars will loop over the group of variables given and print them out according to the format. Just like put. The effect is to list all the variables for a given group. Event, style, dynamic, user, stream. Anyone familiar with xmlrpc may already be seeing the possibilities of this simple tagset. This may be way too geeky. For version 9 there is a new statement that helps out a lot. Putvars will loop over the group of variables given and print them out according to the format. Just like put. The effect is to list all the variables for a given group. Event, style, dynamic, user, stream. Anyone familiar with xmlrpc may already be seeing the possibilities of this simple tagset. This may be way too geeky.

    48. The output. For version 9 there is a new statement that helps out a lot. Putvars will loop over the group of variables given and print them out according to the format. Just like put. The effect is to list all the variables for a given group. Event, style, dynamic, user, stream. Anyone familiar with xmlrpc may already be seeing the possibilities of this simple tagset. This may be way too geeky. For version 9 there is a new statement that helps out a lot. Putvars will loop over the group of variables given and print them out according to the format. Just like put. The effect is to list all the variables for a given group. Event, style, dynamic, user, stream. Anyone familiar with xmlrpc may already be seeing the possibilities of this simple tagset. This may be way too geeky.

    49. Making your ODS life easier. Lets put events and variables on simmer and take a diversion. Working with ods styles is not usually the most pleasant thing to do. The ODS style tagset can help. When you run this code, you will get a nice flattened ods style as output. No inheritance, everything completely flattened, every attribute repeated every place it needs to be. The file created can be run directly in sas to create a new ods style. Albeit just like the one you used to create it. While this is probably not how you want to create new styles, it will definitly help. Lets put events and variables on simmer and take a diversion. Working with ods styles is not usually the most pleasant thing to do. The ODS style tagset can help. When you run this code, you will get a nice flattened ods style as output. No inheritance, everything completely flattened, every attribute repeated every place it needs to be. The file created can be run directly in sas to create a new ods style. Albeit just like the one you used to create it. While this is probably not how you want to create new styles, it will definitly help.

    50. ODS Diagnostic Tagsets These tagsets are very useful for anyone working with ods style and table templates.These tagsets are very useful for anyone working with ods style and table templates.

    51. Style VS. Style ODS Style will take the current style and write it out with no inheritance. Every attribute is redefined on every style element. It's not what I would want to maintain but it's a lot easier to understand.ODS Style will take the current style and write it out with no inheritance. Every attribute is redefined on every style element. It's not what I would want to maintain but it's a lot easier to understand.

    52. Style VS. Style

    53. More style relief Another nice tagset for for figuring out ods styles is style_popup. Style_popup creates html just like the htmlcss tagset. But if you are a Microsoft fan, and use IE, then everything on the web page will be highlighted in a lovely salmon color as you mouse over it. If you click the code to create an ods style element for that item will be displayed in a popup window. Style display can also help. When it is used like this, it will create a simple web page with an example of every style element. It also creates an example of the hierarchy used by most of the ods styles.Another nice tagset for for figuring out ods styles is style_popup. Style_popup creates html just like the htmlcss tagset. But if you are a Microsoft fan, and use IE, then everything on the web page will be highlighted in a lovely salmon color as you mouse over it. If you click the code to create an ods style element for that item will be displayed in a popup window. Style display can also help. When it is used like this, it will create a simple web page with an example of every style element. It also creates an example of the hierarchy used by most of the ods styles.

    54. Style Display Here is the part of the output from the style_display tagset. An example of all the usual style elments are displayed. They are also clickable just like style_popup.Here is the part of the output from the style_display tagset. An example of all the usual style elments are displayed. They are also clickable just like style_popup.

    55. HTML with ODS trace Named html is another nice tagset that can help with ods styles and table templates. Have you ever run a job with 'ods trace' Then tried to match the trace output to the actual output? The resulting html output from this tagset displays the template and style names for each object in the output. Named html is another nice tagset that can help with ods styles and table templates. Have you ever run a job with 'ods trace' Then tried to match the trace output to the actual output? The resulting html output from this tagset displays the template and style names for each object in the output.

    56. Named HTML Here is an example of the named html tagset. Each output object is is labeled so it is easy to identfy the template that controls it.Here is an example of the named html tagset. Each output object is is labeled so it is easy to identfy the template that controls it.

    57. There is no happiness without Action Triggers are a way to request events of your own making. A sort of subroutine to other events. Triggers maintain the state (start,finish) of the calling event. Triggers can also specify which part of an event they wish to occur. In this example I used the doc event as my starting point. The doc event is the first event for the body file. You can also indention to your tagsets. First set the indent attribute on the tagset. This will be how far each indention level will be. Then add ndent and xdent to your events where you want the indention level to change. Simply doing the ods statement with a body file and then closing it is enough to see the results of this tagset. Triggers are a way to request events of your own making. A sort of subroutine to other events. Triggers maintain the state (start,finish) of the calling event. Triggers can also specify which part of an event they wish to occur. In this example I used the doc event as my starting point. The doc event is the first event for the body file. You can also indention to your tagsets. First set the indent attribute on the tagset. This will be how far each indention level will be. Then add ndent and xdent to your events where you want the indention level to change. Simply doing the ods statement with a body file and then closing it is enough to see the results of this tagset.

    58. Fun with Triggers The output is much easier to understand with indention.The output is much easier to understand with indention.

    59. Limitations, Not! Conditions can be put on any statement. If the condition is true then the statement will be executed. This the Perlish part of the syntax... cmp compares a list of strings, if they all match the test is true. Exists is true if all variables for a value. Not much good for strings... any is true if one or more of the variables has a value. Contains is true if the first value contains the second value. Conditions can be put on any statement. If the condition is true then the statement will be executed. This the Perlish part of the syntax... cmp compares a list of strings, if they all match the test is true. Exists is true if all variables for a value. Not much good for strings... any is true if one or more of the variables has a value. Contains is true if the first value contains the second value.

    60. Break Break is only useful in combination with a condition. Break causes the event to cease execution. In this example we use break to create empty tags. When the event is flagged as empty. The result is that when it is empty it doesn't indent. On the finish It exits as soon as it gets there, so it doesn't do anything at all. Break is only useful in combination with a condition. Break causes the event to cease execution. In this example we use break to create empty tags. When the event is flagged as empty. The result is that when it is empty it doesn't indent. On the finish It exits as soon as it gets there, so it doesn't do anything at all.

    61. Solving Complex Problems This is how I coded the vertical and horizontal justification for version 8.2. The key point of this is how the 'And' is accomplished. It is not possible to do an 'And' in a single if. The trick is to put the second part of the 'and' in another event. Then trigger that event based upon the first part of the 'and'.This is how I coded the vertical and horizontal justification for version 8.2. The key point of this is how the 'And' is accomplished. It is not possible to do an 'And' in a single if. The trick is to put the second part of the 'and' in another event. Then trigger that event based upon the first part of the 'and'.

    62. Round Two This is how the justification code looks today. I didn't even need an 'and'. This code is much simpler. It even includes a translation from decimal alignment to right alignment. The previous version didn't have.This is how the justification code looks today. I didn't even need an 'and'. This code is much simpler. It even includes a translation from decimal alignment to right alignment. The previous version didn't have.

    63. This and That Using set we have the ability to do 'and' and 'or' type comparisons. It takes a little more typing than what we are used to with higher level languages but it gets the job done. Just think of exists as a list of anded values. Any is a list of Or'd values.Using set we have the ability to do 'and' and 'or' type comparisons. It takes a little more typing than what we are used to with higher level languages but it gets the job done. Just think of exists as a list of anded values. Any is a list of Or'd values.

    64. The Hard Way. This is how it would have to be done in version 8.2, without the set statement. As you come to understand this, you'll see that this example falls on it's face. Without the ability to set variables there really is no 'OR'. By the time we get that far we know if something is or isn't. The problem is that you have to code for each possible outcome.This is how it would have to be done in version 8.2, without the set statement. As you come to understand this, you'll see that this example falls on it's face. Without the ability to set variables there really is no 'OR'. By the time we get that far we know if something is or isn't. The problem is that you have to code for each possible outcome.

    65. Green Bar : The Styles To get a tagset that does striped tables we need to create a couple of styles for our alternating rows. One will be just like the data style, the other will have a new, medium green background. These styles will be written to the stylesheet automatically for us. There is no need to do anything special in the tagset. To get these new styles into the stylesheet. Not for htmlcss or html4 anyway. Phtml is different though.To get a tagset that does striped tables we need to create a couple of styles for our alternating rows. One will be just like the data style, the other will have a new, medium green background. These styles will be written to the stylesheet automatically for us. There is no need to do anything special in the tagset. To get these new styles into the stylesheet. Not for htmlcss or html4 anyway. Phtml is different though.

    66. Greenbar : Part 1 To get this to work we need to change the table, row, and data events. The table event needs to initialize our row class to dark. The data event needs to be changed to use the new rowclass variable as it's class name, So it will use the right stylesheet class. The row event is changed to trigger a new event called swapclass. So every time a row starts, The swapclass event changes the rowclass between light and dark. The heart of this tagset is the swapclass event. To get this to work we need to change the table, row, and data events. The table event needs to initialize our row class to dark. The data event needs to be changed to use the new rowclass variable as it's class name, So it will use the right stylesheet class. The row event is changed to trigger a new event called swapclass. So every time a row starts, The swapclass event changes the rowclass between light and dark. The heart of this tagset is the swapclass event.

    67. Greenbar : Part 2 This is a good example of how tagsets can be painfully simple. Simple to the point of being painful, that is. We start out by setting the user defined rowclass variable to dark when the table starts. After that we trigger the swapclass event at the beginning of each row. Swap class starts by unsetting the swapped variable. This variable is going to tell us if rowclass was dark when swapclass started The next three lines happen if rowclass is dark. We set swapped. Set rowclass to light. Break. Otherwise, We set rowclass to dark. Whew!This is a good example of how tagsets can be painfully simple. Simple to the point of being painful, that is. We start out by setting the user defined rowclass variable to dark when the table starts. After that we trigger the swapclass event at the beginning of each row. Swap class starts by unsetting the swapped variable. This variable is going to tell us if rowclass was dark when swapclass started The next three lines happen if rowclass is dark. We set swapped. Set rowclass to light. Break. Otherwise, We set rowclass to dark. Whew!

    68. Voila! Green Bar! Here's what we get. Of course, if you don't really want green you can change the dark and light style classes as desired.Here's what we get. Of course, if you don't really want green you can change the dark and light style classes as desired.

    69. Checkers anyone ? Here's what we get. Of course, if you don't really want green you can change the dark and light style classes as desired.Here's what we get. Of course, if you don't really want green you can change the dark and light style classes as desired.

    70. Future Exploration Using tagsets to communicate with external programs is something worth exploring. Frank Poppe has already created a atagset which can communicate directly with excel using it's DDE interface. It should also be easy to communicate using Soap envelopes over the web. Remote Program Control is another easy way to communicate with external applications. It should also be easy to communicate with shell scripts or to even write them from a tagset. All of these things can be done just by connecting the output to sockets or to processes via pipes.Using tagsets to communicate with external programs is something worth exploring. Frank Poppe has already created a atagset which can communicate directly with excel using it's DDE interface. It should also be easy to communicate using Soap envelopes over the web. Remote Program Control is another easy way to communicate with external applications. It should also be easy to communicate with shell scripts or to even write them from a tagset. All of these things can be done just by connecting the output to sockets or to processes via pipes.

    71. New in Version 9.1 New for version 9.1. Datastep functions can be called from anywhere in the tagset. A new Eval statement allows arithmetic and any expression using the sas language operators. If statemnts have also been enhanced to handle all operators. There are now two variable types, numeric and string. Ods html is now directed to the html4 tagset. There is a new html tagset called MSOffice2K which has special attributes and tags added that make it load better into Microsoft Word and Excel. New for version 9.1. Datastep functions can be called from anywhere in the tagset. A new Eval statement allows arithmetic and any expression using the sas language operators. If statemnts have also been enhanced to handle all operators. There are now two variable types, numeric and string. Ods html is now directed to the html4 tagset. There is a new html tagset called MSOffice2K which has special attributes and tags added that make it load better into Microsoft Word and Excel.

    72. Data Step Functions Datastep functions can be called from anywhere in the tagset. They cannot be nested in puts or sets, but can be nested in Eval statements or if's. Along with functions and where processing it became apparent that stronger data types were needed. The Set statement always creates strings. Eval can create strings or numbers depending upon what functions, comparisons or arithmetic is done within the statement. It is up to the tagset writer to ensure that the datatypes of the variables match the types expected for functionss and expressions.Datastep functions can be called from anywhere in the tagset. They cannot be nested in puts or sets, but can be nested in Eval statements or if's. Along with functions and where processing it became apparent that stronger data types were needed. The Set statement always creates strings. Eval can create strings or numbers depending upon what functions, comparisons or arithmetic is done within the statement. It is up to the tagset writer to ensure that the datatypes of the variables match the types expected for functionss and expressions.

    73. Data step Functions Data step functions can be used anywhere, set, put, eval, etc. Functions cannot be nested except for within Eval or if statements. Also notice that, in the last two lines, that set creates a string while eval creates a number even though both call the length function.Data step functions can be used anywhere, set, put, eval, etc. Functions cannot be nested except for within Eval or if statements. Also notice that, in the last two lines, that set creates a string while eval creates a number even though both call the length function.

    74. Where clauses If statemnts use th e full capabilities of the sas language where processing. In reality tagsets rarely get this complicated, but on occasion it's very nice to have this power available.If statemnts use th e full capabilities of the sas language where processing. In reality tagsets rarely get this complicated, but on occasion it's very nice to have this power available.

    75. Eval Eval does everything if does. It just assigns it's return value to a variable. In most cases it is used to create numbers or do math. Eval does everything if does. It just assigns it's return value to a variable. In most cases it is used to create numbers or do math.

    76. Fun example Here's an example that uses Eval to create a counter. Event Loop recurses on it's self 100 times and then unwinds. It's as simple as looping can be. The maximum function call stack depth is 200. The events will automatically unwind at that point. Th e result of this tagset is a count upwards to 100 and then downwards to 0.Here's an example that uses Eval to create a counter. Event Loop recurses on it's self 100 times and then unwinds. It's as simple as looping can be. The maximum function call stack depth is 200. The events will automatically unwind at that point. Th e result of this tagset is a count upwards to 100 and then downwards to 0.

    77. Practical example Here is an example that has a little bit of everything, eval, functions, and looping. Many people have wanted a way to specify multiple stylesheet urls. This code is a part of the html tagsets and allows just that. It loops over the url given and breaks it into pieces where there are spaces. Each piece becomes a separate stylesheet link.Here is an example that has a little bit of everything, eval, functions, and looping. Many people have wanted a way to specify multiple stylesheet urls. This code is a part of the html tagsets and allows just that. It loops over the url given and breaks it into pieces where there are spaces. Each piece becomes a separate stylesheet link.

    78. Multiple Stylesheets Here's how you would use this new stylesheet url functinality. If you were to look in the head section of your html output you would see a link tag for each url you gave.Here's how you would use this new stylesheet url functinality. If you were to look in the head section of your html output you would see a link tag for each url you gave.

    79. Conclusion Tagsets are Simple. Tagsets are Powerful. Tagsets are Fun. Hopefully you are now prepared to explore tagsets and have a little fun. Tagsets are worth learning and exploring no matter what you needs. Hopefully you are now prepared to explore tagsets and have a little fun. Tagsets are worth learning and exploring no matter what you needs.

    80. Questions ?

    81. Acknowledgements

    82. About the Speaker

More Related