ape 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/CHANGELOG +1 -0
  2. data/LICENSE +19 -0
  3. data/Manifest +45 -0
  4. data/README +12 -0
  5. data/ape.gemspec +57 -0
  6. data/bin/ape_server +28 -0
  7. data/lib/ape.rb +982 -0
  8. data/lib/ape/atomURI.rb +73 -0
  9. data/lib/ape/auth/google_login_credentials.rb +96 -0
  10. data/lib/ape/auth/wsse_credentials.rb +25 -0
  11. data/lib/ape/authent.rb +42 -0
  12. data/lib/ape/categories.rb +95 -0
  13. data/lib/ape/collection.rb +51 -0
  14. data/lib/ape/crumbs.rb +39 -0
  15. data/lib/ape/entry.rb +151 -0
  16. data/lib/ape/escaper.rb +29 -0
  17. data/lib/ape/feed.rb +117 -0
  18. data/lib/ape/handler.rb +34 -0
  19. data/lib/ape/html.rb +17 -0
  20. data/lib/ape/invoker.rb +54 -0
  21. data/lib/ape/invokers/deleter.rb +31 -0
  22. data/lib/ape/invokers/getter.rb +80 -0
  23. data/lib/ape/invokers/poster.rb +57 -0
  24. data/lib/ape/invokers/putter.rb +46 -0
  25. data/lib/ape/layout/ape.css +56 -0
  26. data/lib/ape/layout/ape_logo.png +0 -0
  27. data/lib/ape/layout/index.html +54 -0
  28. data/lib/ape/layout/info.png +0 -0
  29. data/lib/ape/names.rb +24 -0
  30. data/lib/ape/print_writer.rb +21 -0
  31. data/lib/ape/samples.rb +180 -0
  32. data/lib/ape/samples/atom_schema.txt +338 -0
  33. data/lib/ape/samples/basic_entry.eruby +16 -0
  34. data/lib/ape/samples/categories_schema.txt +69 -0
  35. data/lib/ape/samples/mini_entry.eruby +8 -0
  36. data/lib/ape/samples/service_schema.txt +187 -0
  37. data/lib/ape/samples/unclean_xhtml_entry.eruby +21 -0
  38. data/lib/ape/server.rb +32 -0
  39. data/lib/ape/service.rb +12 -0
  40. data/lib/ape/validator.rb +65 -0
  41. data/lib/ape/version.rb +9 -0
  42. data/scripts/go.rb +29 -0
  43. data/test/test_helper.rb +17 -0
  44. data/test/unit/authent_test.rb +35 -0
  45. data/test/unit/invoker_test.rb +25 -0
  46. data/test/unit/samples_test.rb +36 -0
  47. metadata +111 -0
@@ -0,0 +1,56 @@
1
+ h2 {
2
+ color: #086;
3
+ }
4
+
5
+ body {
6
+ margin-left: 50px;
7
+ margin-right: 100px;
8
+ font-family: verdana, arial, sans-serif;
9
+ }
10
+
11
+ .dialog {
12
+ border-left: 8px solid #eee;
13
+ }
14
+ .from {
15
+ padding: 0 10px 0 10px;
16
+ background: #ddf;
17
+ }
18
+ .to {
19
+ padding: 0 10px 0 10px;
20
+ background: #dfd;
21
+ }
22
+ .dialab {
23
+ padding: 3px 10px 3px 0px;
24
+ background: #eee;
25
+ }
26
+
27
+ a.diaref {
28
+ text-decoration: none;
29
+ font-weight: bold;
30
+ color: #086;
31
+ }
32
+
33
+ span.good {
34
+ font-size: 125%;
35
+ color: green;
36
+ }
37
+ span.warning {
38
+ font-size: 125%;
39
+ font-weight: bold;
40
+ color: #fa0;
41
+ }
42
+ span.error {
43
+ font-size: 125%;
44
+ font-weight: bold;
45
+ color: red;
46
+ }
47
+
48
+ #ape {
49
+ position: absolute; right: 100px; top: 8px;
50
+ padding-left: 20px;
51
+ padding-right: 10px;
52
+
53
+ width: 170px;
54
+ border: 1px solid #086;
55
+ }
56
+
@@ -0,0 +1,54 @@
1
+ <html>
2
+ <head>
3
+ <title>Atom Protocol Exerciser</title>
4
+ <link rel='stylesheet' type='text/css' href='/layout/ape.css' />
5
+ <meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>
6
+ </head>
7
+ <body>
8
+ <img align="right" src="/ape/ape_logo.png" alt="The Ape logo by Greg Borenstein" />
9
+ <div id="main">
10
+ <h2>Atom Protocol Exerciser</h2>
11
+
12
+ <p>(Only the URI argument is required)</p>
13
+ <form action="/atompub/go" method="post">
14
+ <table>
15
+ <tr>
16
+ <td align="right">URI:</td>
17
+ <td align="left"><input name="uri" type="text" size="55" maxlength="255" /></td>
18
+ </tr>
19
+ <tr>
20
+
21
+ <td align="right">Username:</td>
22
+ <td align="left"><input name="username" type="text" size="55" maxlength="55" /></td>
23
+ </tr>
24
+ <tr>
25
+ <td align="right">Password:</td>
26
+ <td align="left"><input name="password" type="password" /></td>
27
+ </tr>
28
+ <tr>
29
+
30
+ <td></td>
31
+ <td align="left"><input type="submit" value="Exercise!" /></td>
32
+ </tr>
33
+ </table>
34
+ </form>
35
+ </div>
36
+ <!--
37
+ <div id="ape"><p>(It would be nice if there were a cute picture of an ape
38
+ here, ideally grooming another ape for fleas and parasites.
39
+ Got one?)</p></div>
40
+ -->
41
+ <hr/>
42
+ <p>The Ape source code may be found at
43
+ <a href="http://ape.dev.java.net">ape.dev.java.net</a>. It's a little
44
+ tricky to get going;
45
+ until I fix that, see this
46
+ <a href="http://rollerweblogger.org/roller/entry/atom_protocol_exerciser_ape_setup">discussion by Dave Johnson</a>.</p>
47
+
48
+ <hr/>
49
+ <p>Ape Logo by <a href="http://www.urbanhonking.com/ideasfordozens">Greg
50
+ Borenstein</a>.</p>
51
+ </div>
52
+ </body>
53
+ </html>
54
+
Binary file
@@ -0,0 +1,24 @@
1
+ # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
2
+ # Use is subject to license terms - see file "LICENSE"
3
+
4
+ module Ape
5
+ module Names
6
+ AtomNamespace = 'http://www.w3.org/2005/Atom' unless defined?(AtomNamespace)
7
+ AppNamespace = 'http://www.w3.org/2007/app' unless defined?(AppNamespace)
8
+ DcNamespace = 'http://purl.org/dc/elements/1.1/' unless defined?(DcNamespace)
9
+ XhtmlNamespace = 'http://www.w3.org/1999/xhtml' unless defined?(XhtmlNamespace)
10
+ XmlNamespaces = {
11
+ 'app' => AppNamespace,
12
+ 'atom' => AtomNamespace,
13
+ 'dc' => DcNamespace,
14
+ 'xhtml' => XhtmlNamespace
15
+ } unless defined?(XmlNamespaces)
16
+
17
+ AtomMediaType = 'application/atom+xml' unless defined?(AtomMediaType)
18
+ AtomEntryMediaType = 'application/atom+xml;type=entry' unless defined?(AtomEntryMediaType)
19
+ AppMediaType = 'application/atomsvc+xml' unless defined?(AppMediaType)
20
+
21
+ end
22
+ end
23
+
24
+
@@ -0,0 +1,21 @@
1
+ # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
2
+ # Use is subject to license terms - see file "LICENSE"
3
+
4
+ # this is a wrapper for the weird derived-from-PrintWriter class that comes
5
+ # out of HttpResponse.getWriter
6
+
7
+ module Ape
8
+ class Printwriter
9
+ def initialize(java_writer)
10
+ @w = java_writer
11
+ end
12
+
13
+ def puts(s)
14
+ @w.println s
15
+ end
16
+
17
+ def << (s)
18
+ @w.print s
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,180 @@
1
+ # Copyright © 2006 Sun Microsystems, Inc. All rights reserved
2
+ # Use is subject to license terms - see file "LICENSE"
3
+ require 'rexml/xpath'
4
+ require 'date'
5
+ require 'base64'
6
+ require 'erubis'
7
+
8
+ module Ape
9
+ class Samples
10
+
11
+ @@service_schema = nil
12
+ @@categories_schema = nil
13
+ @@atom_schema = nil
14
+ @@home = nil
15
+
16
+ def Samples.home=(home)
17
+ @@home = home
18
+ end
19
+
20
+ def Samples.foreign_child
21
+ 'subject'
22
+ end
23
+ def Samples.foreign_namespace
24
+ Names::DcNamespace
25
+ end
26
+ def Samples.foreign_child_content
27
+ 'Simians'
28
+ end
29
+
30
+ def Samples.load_schema(file_name)
31
+ schema = ""
32
+ File.open(File.join(File.dirname(__FILE__), "/samples/#{file_name}_schema.txt")) do |file|
33
+ while(line = file.gets)
34
+ schema << line
35
+ end
36
+ end
37
+ schema
38
+ end
39
+
40
+ def Samples.service_RNC
41
+ @@service_schema = load_schema('service') unless @@service_schema
42
+ @@service_schema
43
+ end
44
+
45
+ def Samples.categories_RNC
46
+ @@categories_schema = load_schema('categories') unless @@categories_schema
47
+ @@categories_schema
48
+ end
49
+
50
+ def Samples.atom_RNC
51
+ @@atom_schema = load_schema('atom') unless @@atom_schema
52
+ @@atom_schema
53
+ end
54
+
55
+ #recipe from cap
56
+ def Samples.home_directory
57
+ ENV["HOME"] || (ENV["HOMEPATH"] && "#{ENV["HOMEDRIVE"]}#{ENV["HOMEPATH"]}") ||
58
+ "/"
59
+ end
60
+
61
+ def Samples.home
62
+ @@home || ENV["APE_HOME"] || File.join(home_directory,".ape")
63
+ end
64
+
65
+ def Samples.make_id
66
+ id = ''
67
+ 5.times { id += rand(1000000).to_s }
68
+ "tag:tbray.org,2005:#{id}"
69
+ end
70
+
71
+ def Samples.entry_path(type)
72
+ File.exist?(File.join(home, "/#{type}.eruby"))?
73
+ File.join(home, "/#{type}.eruby") :
74
+ File.join(File.dirname(__FILE__), "/samples/#{type}.eruby")
75
+ end
76
+
77
+ def Samples.load_template(type)
78
+ entry_path = entry_path(type)
79
+ input = File.read(entry_path)
80
+ eruby = Erubis::Eruby.new(input)
81
+ end
82
+
83
+
84
+ def Samples.mini_entry
85
+ eruby = load_template('mini_entry')
86
+
87
+ now = DateTime::now.strftime("%Y-%m-%dT%H:%M:%S%z").sub(/(..)$/, ':\1')
88
+ id = make_id
89
+
90
+ eruby.result(binding())
91
+ end
92
+
93
+ def Samples.basic_entry
94
+ eruby = load_template('basic_entry')
95
+
96
+ id = make_id
97
+ now = DateTime::now.strftime("%Y-%m-%dT%H:%M:%S%z").sub(/(..)$/, ':\1')
98
+ title = Escaper.escape('From the <APE> (サル)')
99
+ summary = "Summary from the &lt;b>&amp;lt;&amp;nbsp;APE&amp;nbsp;>&lt;/b> at #{now}"
100
+ subject = Names::DcNamespace
101
+
102
+ eruby.result(binding())
103
+ end
104
+
105
+ def Samples.unclean_xhtml_entry
106
+ eruby = load_template('unclean_xhtml_entry')
107
+
108
+ id = make_id
109
+ now = DateTime::now.strftime("%Y-%m-%dT%H:%M:%S%z").sub(/(..)$/, ':\1')
110
+
111
+ eruby.result(binding())
112
+ end
113
+
114
+ def Samples.cat_test_entry
115
+ e = retitled_entry('Testing category posting')
116
+ end
117
+
118
+ def Samples.retitled_entry(new_title, new_id = nil)
119
+ e = basic_entry
120
+ e.gsub!(/<title>.*<\/title>/, "<title>#{new_title}</title>")
121
+ new_id = make_id unless new_id
122
+ e.gsub(/<id>.*<\/id>/, "<id>#{new_id}</id>")
123
+ end
124
+
125
+ def Samples.picture
126
+ b64 =<<END_OF_PICTURE
127
+ /9j/4AAQSkZJRgABAQEASABIAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQE
128
+ BQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/
129
+ 2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU
130
+ FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAA8AEYDASIAAhEBAxEB/8QA
131
+ HwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUF
132
+ BAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkK
133
+ FhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1
134
+ dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG
135
+ x8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEB
136
+ AQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAEC
137
+ AxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRom
138
+ JygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE
139
+ hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU
140
+ 1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD5fgS0knaT
141
+ 7C4UddzHINab3lrboz/2ezop5LAnj3qtbK1xJbwbcZYZwc4GeprpZLNZY5Tu
142
+ Ai8skhjgHk5FfJNtWVm/m/8Agn7M6dOGzSdvzdvKxl2VzaAec2n7uMqDnBHr
143
+ W1FPEY7YDTrVYXUNl5fw/wAf0qjps0NzbCEMUZMBhnJz9PwqzZbISIpQj27s
144
+ A+4ZK99w/wAKycU229fm/wDM9aVDlhF09uv9eRrG6jmgeGLTrR2kPG2T5uMe
145
+ /wDnNcnrGuxpdGMWCM4cR7V+bkehzXSatJBo4S3sZFkmkQ75x0QZ7H1965z7
146
+ MjIxDsAEJyTkgY5b9KjlitV+N/yuXSoyrO/T835FKe/Ms8khsMFRtIwR2780
147
+ 77bGqQObHZORwgz8wHGRz6/yqOxkZmnO1isjYXeMZB4yPyqvrSJa30DF2bdu
148
+ 4TtycZq7Nbp2stdTgapyqciaerTXp899uhFc6qLmQg23X5iTxz9aKZes0RRX
149
+ yxA+7n9aK6YcvKrq/wAzkr4W1R8rsu1rnSWD2UcLPIgE3BIDHJPt7ZqBXmiv
150
+ ZnuSTaMCpUAHy89P8j1pdKtGe7jllI+i84H9TW7JIt/PLYCGREyfKbachu5P
151
+ P/6q578rf3/1/X/B6mkopTV2/wAv6X4fNRp4ZitLQXsLZlUL8p5LAjg/XFVz
152
+ fRKky+YMbCd7L19himadPLat9lD5jALKqtyuPT2961bfT4LlELQA+ZuyVXJz
153
+ nrWMlKKbk9j0KOIVLlp2321f/D9bmH9uihxNJgKNqnjJxjJx+NFuh1RONjRE
154
+ gkgYIHOFH+eK0tV0m3adDFEjgEbuM5yAc5z+lVpA+nRGOMrGGOPNPAXHO0de
155
+ abkqvNKP9alLETgoULJfPy+X6kGqWovJRaQqqxx4DzoM9Ow9ag8iNmeN4wXG
156
+ V3tgluevsa0WlSw0uS5hj82RW2kOp2ITzuPHX6etQ3CJcWkdyymJpGLbTxsI
157
+ /iXjp9aiOiv8vnv+Wv8AWtT9m6iw6Wq6+d7Wt2vp6+W3NyqtlIEm6c7XKZ9O
158
+ MGirjz/aUDOFkOfvOOG9+aK9OnhnUipXt8m/0PCq5p7Cbpyim115kv1ROzSy
159
+ SR3UK5jx8yg8tzXW3Gtw3Gm26QQBbtkCO6DmQjp2yP61yGk34SQIGco5ypHY
160
+ 9s1q2MS2l1LdKCNg3le3ORj8a5uTbmt5f1/XT5ae1UVZpvr3/r0/4ZpNp76X
161
+ Os2AZW6KTgZJ6Y9OKgOszpAyu6IEPAKA5IPT2qxFeG/Xz5mbzpAQikFsL6/l
162
+ WXcp5832e2KuZPmKsCNo9/50KN07nalCmvaTev8AwxqrqNxe2kzI0RkGAFMa
163
+ 7j1PXHHpVqKZLm3WK+aOUld5IbAb6+9Zl5af2WqnzQ1lN0lQcbsY4PXr61Qa
164
+ 5JixvIJbCgDGCO9KUFq1szWm4VaSS0kvw/H8jcivItPlSDasqEfJuAAYd1PH
165
+ A9aoavOt3O4wTC5y74+97cDoMdRUEc39p2brONoiOHOcYb1pmrShII7W2Oc8
166
+ LzyB2rPka1fb+v8AP8fSXiVN8qXv7X8lv/lfsmvWhqBF5MiWsqQhF+ZiePoK
167
+ KpzfuI1C/OqkqV2d/WiuqMqkFaLsjklh8K3erFN9/wCmSaNqBSPdNE5KkMML
168
+ 1561tJrqSSylbaQh49oXgdjyea5S21W5ReJPb8KvW2sXQlzvByhPI+tdUqDb
169
+ TaX3v/I+bjjHy2v+C/UmfWjazbYopHToQeorSj1WGGzkRSwuZT+8kdSSB/d6
170
+ dKw/t0skr7tp2kAfKKsapqs4kiAKjChshRnOBS9k7ar8f+AavFyqa30Xl/wT
171
+ RjvtkDW8zs9q+NyMh+96ism4umR9kSSSgEqrn+Jc/pSf23ds0zFwdvIBUYzV
172
+ aLU55DG5Khj3CipdKTbdlr5/8A1hifZ2af8AX3luDUC0dxCySoJG3byBj8fy
173
+ qG71KW4nSRomwq7CV4OO2KonVLgblDAAcDAqAanO8+0kADngURoXbdvxZM8Y
174
+ 001+n/Dk41B8ZaFmx8oyx6UVVn1m5jRCGXOMfdorRU2lqvx/4BhPFzlK6lb5
175
+ I//Z
176
+ END_OF_PICTURE
177
+ Base64.decode64(b64)
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,338 @@
1
+ # -*- rnc -*-
2
+ # RELAX NG Compact Syntax Grammar for the
3
+ # Atom Format Specification Version 11
4
+
5
+ namespace atom = "http://www.w3.org/2005/Atom"
6
+ namespace xhtml = "http://www.w3.org/1999/xhtml"
7
+ namespace s = "http://www.ascc.net/xml/schematron"
8
+ namespace local = ""
9
+
10
+ start = atomFeed | atomEntry
11
+
12
+ # Common attributes
13
+
14
+ atomCommonAttributes =
15
+ attribute xml:base { atomUri }?,
16
+ attribute xml:lang { atomLanguageTag }?,
17
+ undefinedAttribute*
18
+
19
+ # Text Constructs
20
+
21
+ atomPlainTextConstruct =
22
+ atomCommonAttributes,
23
+ attribute type { "text" | "html" }?,
24
+ text
25
+
26
+ atomXHTMLTextConstruct =
27
+ atomCommonAttributes,
28
+ attribute type { "xhtml" },
29
+ xhtmlDiv
30
+
31
+ atomTextConstruct = atomPlainTextConstruct | atomXHTMLTextConstruct
32
+
33
+ # Person Construct
34
+
35
+ atomPersonConstruct =
36
+ atomCommonAttributes,
37
+ (element atom:name { text }
38
+ & element atom:uri { atomUri }?
39
+ & element atom:email { atomEmailAddress }?
40
+ & extensionElement*)
41
+
42
+ # Date Construct
43
+
44
+ atomDateConstruct =
45
+ atomCommonAttributes,
46
+ xsd:dateTime
47
+
48
+ # atom:feed
49
+
50
+ atomFeed =
51
+ [
52
+ s:rule [
53
+ context = "atom:feed"
54
+ s:assert [
55
+ test = "atom:author or not(atom:entry[not(atom:author)])"
56
+ "An atom:feed must have an atom:author unless all "
57
+ ~ "of its atom:entry children have an atom:author."
58
+ ]
59
+ ]
60
+ ]
61
+ element atom:feed {
62
+ atomCommonAttributes,
63
+ (atomAuthor*
64
+ & atomCategory*
65
+ & atomContributor*
66
+ & atomGenerator?
67
+ & atomIcon?
68
+ & atomId
69
+ & atomLink*
70
+ & atomLogo?
71
+ & atomRights?
72
+ & atomSubtitle?
73
+ & atomTitle
74
+ & atomUpdated
75
+ & extensionElement*),
76
+ atomEntry*
77
+ }
78
+
79
+ # atom:entry
80
+
81
+ atomEntry =
82
+ [
83
+ s:rule [
84
+ context = "atom:entry"
85
+ s:assert [
86
+ test = "atom:link[@rel='alternate'] "
87
+ ~ "or atom:link[not(@rel)] "
88
+ ~ "or atom:content"
89
+ "An atom:entry must have at least one atom:link element "
90
+ ~ "with a rel attribute of 'alternate' "
91
+ ~ "or an atom:content."
92
+ ]
93
+ ]
94
+ s:rule [
95
+ context = "atom:entry"
96
+ s:assert [
97
+ test = "atom:author or "
98
+ ~ "../atom:author or atom:source/atom:author"
99
+ "An atom:entry must have an atom:author "
100
+ ~ "if its feed does not."
101
+ ]
102
+ ]
103
+ ]
104
+ element atom:entry {
105
+ atomCommonAttributes,
106
+ (atomAuthor*
107
+ & atomCategory*
108
+ & atomContent?
109
+ & atomContributor*
110
+ & atomId
111
+ & atomLink*
112
+ & atomPublished?
113
+ & atomRights?
114
+ & atomSource?
115
+ & atomSummary?
116
+ & atomTitle
117
+ & atomUpdated
118
+ & extensionElement*)
119
+ }
120
+
121
+ # atom:content
122
+
123
+ atomInlineTextContent =
124
+ element atom:content {
125
+ atomCommonAttributes,
126
+ attribute type { "text" | "html" }?,
127
+ (text)*
128
+ }
129
+
130
+ atomInlineXHTMLContent =
131
+ element atom:content {
132
+ atomCommonAttributes,
133
+ attribute type { "xhtml" },
134
+ xhtmlDiv
135
+ }
136
+
137
+ atomInlineOtherContent =
138
+ element atom:content {
139
+ atomCommonAttributes,
140
+ attribute type { atomMediaType }?,
141
+ (text|anyElement)*
142
+ }
143
+
144
+ atomOutOfLineContent =
145
+ element atom:content {
146
+ atomCommonAttributes,
147
+ attribute type { atomMediaType }?,
148
+ attribute src { atomUri },
149
+ empty
150
+ }
151
+
152
+ atomContent = atomInlineTextContent
153
+ | atomInlineXHTMLContent
154
+ | atomInlineOtherContent
155
+ | atomOutOfLineContent
156
+
157
+ # atom:author
158
+
159
+ atomAuthor = element atom:author { atomPersonConstruct }
160
+
161
+ # atom:category
162
+
163
+ atomCategory =
164
+ element atom:category {
165
+ atomCommonAttributes,
166
+ attribute term { text },
167
+ attribute scheme { atomUri }?,
168
+ attribute label { text }?,
169
+ undefinedContent
170
+ }
171
+
172
+ # atom:contributor
173
+
174
+ atomContributor = element atom:contributor { atomPersonConstruct }
175
+
176
+ # atom:generator
177
+
178
+ atomGenerator = element atom:generator {
179
+ atomCommonAttributes,
180
+ attribute uri { atomUri }?,
181
+ attribute version { text }?,
182
+ text
183
+ }
184
+
185
+ # atom:icon
186
+
187
+ atomIcon = element atom:icon {
188
+ atomCommonAttributes,
189
+ (atomUri)
190
+ }
191
+
192
+ # atom:id
193
+
194
+ atomId = element atom:id {
195
+ atomCommonAttributes,
196
+ (atomUri)
197
+ }
198
+
199
+ # atom:logo
200
+
201
+ atomLogo = element atom:logo {
202
+ atomCommonAttributes,
203
+ (atomUri)
204
+ }
205
+
206
+ # atom:link
207
+
208
+ atomLink =
209
+ element atom:link {
210
+ atomCommonAttributes,
211
+ attribute href { atomUri },
212
+ attribute rel { atomNCName | atomUri }?,
213
+ attribute type { atomMediaType }?,
214
+ attribute hreflang { atomLanguageTag }?,
215
+ attribute title { text }?,
216
+ attribute length { text }?,
217
+ undefinedContent
218
+ }
219
+
220
+ # atom:published
221
+
222
+ atomPublished = element atom:published { atomDateConstruct }
223
+
224
+ # atom:rights
225
+
226
+ atomRights = element atom:rights { atomTextConstruct }
227
+
228
+ # atom:source
229
+
230
+ atomSource =
231
+ element atom:source {
232
+ atomCommonAttributes,
233
+ (atomAuthor*
234
+ & atomCategory*
235
+ & atomContributor*
236
+ & atomGenerator?
237
+ & atomIcon?
238
+ & atomId?
239
+ & atomLink*
240
+ & atomLogo?
241
+ & atomRights?
242
+ & atomSubtitle?
243
+ & atomTitle?
244
+ & atomUpdated?
245
+ & extensionElement*)
246
+ }
247
+
248
+ # atom:subtitle
249
+
250
+ atomSubtitle = element atom:subtitle { atomTextConstruct }
251
+
252
+ # atom:summary
253
+
254
+ atomSummary = element atom:summary { atomTextConstruct }
255
+
256
+ # atom:title
257
+
258
+ atomTitle = element atom:title { atomTextConstruct }
259
+
260
+ # atom:updated
261
+
262
+ atomUpdated = element atom:updated { atomDateConstruct }
263
+
264
+ # Low-level simple types
265
+
266
+ atomNCName = xsd:string { minLength = "1" pattern = "[^:]*" }
267
+
268
+ # Whatever a media type is, it contains at least one slash
269
+ atomMediaType = xsd:string { pattern = ".+/.+" }
270
+
271
+ # As defined in RFC 3066
272
+ atomLanguageTag = xsd:string {
273
+ pattern = "[A-Za-z]{1,8}(-[A-Za-z0-9]{1,8})*"
274
+ }
275
+
276
+ # Unconstrained; it's not entirely clear how IRI fit into
277
+ # xsd:anyURI so let's not try to constrain it here
278
+ atomUri = text
279
+
280
+ # Whatever an email address is, it contains at least one @
281
+ atomEmailAddress = xsd:string { pattern = ".+@.+" }
282
+
283
+ # Simple Extension
284
+
285
+ simpleExtensionElement =
286
+ element * - atom:* {
287
+ text
288
+ }
289
+
290
+ # Structured Extension
291
+
292
+ structuredExtensionElement =
293
+ element * - atom:* {
294
+ (attribute * { text }+,
295
+ (text|anyElement)*)
296
+ | (attribute * { text }*,
297
+ (text?, anyElement+, (text|anyElement)*))
298
+ }
299
+
300
+ # Other Extensibility
301
+
302
+ extensionElement =
303
+ simpleExtensionElement | structuredExtensionElement
304
+
305
+ undefinedAttribute =
306
+ attribute * - (xml:base | xml:lang | local:*) { text }
307
+
308
+ undefinedContent = (text|anyForeignElement)*
309
+
310
+ anyElement =
311
+ element * {
312
+ (attribute * { text }
313
+ | text
314
+ | anyElement)*
315
+ }
316
+
317
+ anyForeignElement =
318
+ element * - atom:* {
319
+ (attribute * { text }
320
+ | text
321
+ | anyElement)*
322
+ }
323
+
324
+ # XHTML
325
+
326
+ anyXHTML = element xhtml:* {
327
+ (attribute * { text }
328
+ | text
329
+ | anyXHTML)*
330
+ }
331
+
332
+ xhtmlDiv = element xhtml:div {
333
+ (attribute * { text }
334
+ | text
335
+ | anyXHTML)*
336
+ }
337
+
338
+ # EOF