Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ec6cc5f2b8 | |||
| 40018d36eb | |||
| 8e18329063 | |||
| c372494713 | |||
| 10f4a99ee1 | |||
| e9085c614c | |||
| f7d3f519a7 | |||
| 4eb22d0272 | |||
| bbcc9ef44b | |||
| 801caa4e69 | |||
| 1b49445077 | |||
| f005d8bb06 | |||
| a278537f7b | |||
| 78b5b6f5fe | |||
| a215c1e4d2 | |||
| fe3726677b | |||
| 94d4909644 | |||
| 0da4b7d61b | |||
| 5c5ba9f038 | |||
| d65c8de1d2 | |||
| 3d93dbc66d | |||
| 436f2afd00 | |||
| 73284575d4 | |||
| e9b05ffc0b | |||
| 012bb2f6c9 | |||
| e883163fe9 | |||
| e5fb3f8328 | |||
| 302ae8d030 | |||
| 694c0a193c | |||
| 4075b08b7e | |||
| 65800371cd | |||
| 2c6196017e | |||
| 5a155e0cee | |||
| 08bec49da4 | |||
| 4940ddb8f5 | |||
| 6599bf1a07 | |||
| 0ef88eadf5 | |||
| e1ef4c390e | |||
| 11baeefe1c | |||
| ac7f58722d | |||
| acd395376a | |||
| a7a27bee0a | |||
| fc0a25acc9 | |||
| 0d363f065b | |||
| 51b70a2490 | |||
| 917f80c0c8 | |||
| 73e5fd6cd6 | |||
| 0abab83566 | |||
| f764e63b45 | |||
| 0a1c4676a4 | |||
| 8a38519fb0 | |||
| 54c9addb30 | |||
| 30d4843a5b | |||
| 3e73298806 | |||
| 01b16d0a82 | |||
| ac97d6019b | |||
| 4e2f0dbc72 | |||
| 9d1fb56cc1 | |||
| bc98c5a317 | |||
| 681bb19ab7 | |||
| 026b4f8682 | |||
| f74ac66569 | |||
| 4fb42df5fb | |||
| f1d8d18811 | |||
| 545db51fc9 | |||
| 20bf9efaa0 | |||
| a168cc62e7 | |||
| b9e89758ce | |||
| 19da9436ff |
@@ -0,0 +1,2 @@
|
||||
EIFGENs
|
||||
tests/temp/
|
||||
@@ -0,0 +1,9 @@
|
||||
[submodule "doc/wiki"]
|
||||
path = doc/wiki
|
||||
url = https://github.com/Eiffel-World/Eiffel-Web-Framework.wiki.git
|
||||
[submodule "ext/server/nino"]
|
||||
path = ext/server/nino
|
||||
url = http://github.com/Eiffel-World/EiffelWebNino.git
|
||||
[submodule "ext/text/json"]
|
||||
path = ext/text/json
|
||||
url = http://github.com/Eiffel-World/ejson-svn.git
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
History file for EJSON
|
||||
======================
|
||||
|
||||
team: ""
|
||||
date: "2011-07-06"
|
||||
revision: "0.3.0"
|
||||
|
||||
|
||||
+++++++++++++++++++++Important Changes since 0.2.0 version++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
*Updated skip_white_spaces, now check %U and %T codes
|
||||
|
||||
*Undo changes to is_a_valid_number, because it's doesn't follow the
|
||||
JSON spec. Tests : fail13.json, fail29.json and fail30.json are valid
|
||||
with this implementation, so we go back to the previous
|
||||
implementation.
|
||||
|
||||
*Added autotest test suite
|
||||
|
||||
*Added getest based test program
|
||||
|
||||
*Updated Eiffel configuration file, updated to the new clusters
|
||||
|
||||
*Added converters and factory classes
|
||||
|
||||
*Added new top level directories; library, test, build and example
|
||||
@@ -0,0 +1,20 @@
|
||||
Eiffel Forum License, version 2
|
||||
|
||||
1. Permission is hereby granted to use, copy, modify and/or distribute
|
||||
this package, provided that:
|
||||
* copyright notices are retained unchanged,
|
||||
* any distribution of this package, whether modified or not,
|
||||
includes this license text.
|
||||
|
||||
2. Permission is hereby also granted to distribute binary programs which
|
||||
depend on this package. If the binary program depends on a modified
|
||||
version of this package, you are encouraged to publicly release the
|
||||
modified version of this package.
|
||||
|
||||
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT WARRANTY. ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE TO ANY PARTY FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THIS PACKAGE.
|
||||
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
Copyright (c) 2010 Javier Velilla and others, http://ejson.origo.ethz.ch
|
||||
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,30 @@
|
||||
Official project site for Eiffel Web Framework:
|
||||
* https://github.com/Eiffel-World/Eiffel-Web-Framework
|
||||
|
||||
For more information please have a look at the related wiki:
|
||||
* https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki
|
||||
|
||||
How to get the source code?
|
||||
---------------------------
|
||||
|
||||
git clone https://github.com/Eiffel-World/Eiffel-Web-Framework.git
|
||||
cd Eiffel-Web-Framework
|
||||
git submodule update --init
|
||||
git submodule foreach git pull origin master
|
||||
git submodule foreach git checkout master
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
* library/server/ewsgi: Eiffel Web Server Gateway Interface
|
||||
* library/server/ewsgi/connectors: various web server connectors for EWSGI
|
||||
* library/server/libfcgi: Wrapper for libfcgi SDK
|
||||
|
||||
* library/protocol/http: HTTP related classes, constants for status code, content types, ...
|
||||
* library/protocol/uri_template: URI Template library (parsing and expander)
|
||||
|
||||
* library/error: very simple/basic library to handle error
|
||||
* library/text/encoder: Various simpler encoder: base64, url-encoder, xml entities, html entities
|
||||
|
||||
For more information please have a look at the related wiki:
|
||||
* https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki
|
||||
-97
@@ -1,97 +0,0 @@
|
||||
Readme file for eJSON
|
||||
=====================
|
||||
|
||||
team: "Javier Velilla,Jocelyn Fiat, Paul Cohen"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
1. Introduction
|
||||
---------------
|
||||
|
||||
eJSON stands for Eiffel JSON library and is a small Eiffel library for dealing
|
||||
with the JSON format. The objective of the library is to provide two basic
|
||||
features Eiffel2JSON and JSON2Eiffel.
|
||||
|
||||
2. Legal stuff
|
||||
--------------
|
||||
|
||||
eJSON is copyrighted by the author Javier Velilla and others. It is licensed
|
||||
under the MIT License. See the file license.txt in the same directory as this
|
||||
readme file.
|
||||
|
||||
3. Versioning scheme
|
||||
--------------------
|
||||
|
||||
eJSON version numbers has the form:
|
||||
|
||||
«major number».«minor number».«patch level»
|
||||
|
||||
eJSON will retain the major number 0 as long as it has beta status. A change in
|
||||
major number indicates that a release is not backward compatible. A change in
|
||||
minor number indicates that a release is backward compatible (within that major
|
||||
number) but that new useful features may have been added. A change in patch
|
||||
level simply indicates that the release contains bug fixes for the previous
|
||||
release. Note that as long as eJSON is in beta status (0.Y.Z) backward
|
||||
compatibility is not guranteed for changes in minor numbers!
|
||||
|
||||
4. Documentation
|
||||
---------------
|
||||
|
||||
Currently the only documentation on eJSON is available at:
|
||||
|
||||
http://ejson.origo.ethz.ch/wiki/user_guide
|
||||
|
||||
5. Requirements and installation
|
||||
--------------------------------
|
||||
|
||||
EJSON requires that you have:
|
||||
|
||||
1. Gobo 3.9 installed or later
|
||||
2. One of the following compiler combinations installed:
|
||||
* ISE Eiffel 6.5 or later.
|
||||
* gec [try to test]
|
||||
* tecomp [try to test]
|
||||
|
||||
eJSON probably works fine with other versions of the above compilers.
|
||||
There are no known platform dependencies (Windows, Linux).
|
||||
|
||||
To install eJSON simply extract the ejson-X.Y.Z.zip file to some appropriate
|
||||
place on your hard disk. There are no requirements on environment variables or
|
||||
registry variables.
|
||||
|
||||
To verify that everything works you should compile the example programs and/or
|
||||
the test program.
|
||||
|
||||
6. Contents of eJSON
|
||||
--------------------
|
||||
|
||||
All directory names below are relative to the root directory of your ejson
|
||||
installation.
|
||||
|
||||
Directory Description
|
||||
--------- -----------
|
||||
doc Contains the eJSON.pdf documentation file.
|
||||
examples Contains the two example programs.
|
||||
ejson Contains the actual eJSON library classes.
|
||||
test Contains a test program for eJSON.
|
||||
|
||||
7. Contacting the Team
|
||||
----------------------
|
||||
|
||||
Contact the team:
|
||||
|
||||
Javier Velilla «javier.hector@gmail.com»
|
||||
Paul Cohen «paco@seibostudios.se»
|
||||
Jocelyn Fiat «jfiat@eiffel.com»
|
||||
|
||||
8. Releases
|
||||
-----------
|
||||
|
||||
For more information on what was changed in each release look in the file
|
||||
history.txt.
|
||||
|
||||
Version Date Description
|
||||
------- ---- -----------
|
||||
0.3.0 2011-07-06 JSON Factory Converters
|
||||
0.2.0 2010-02-07 Adapted to EiffelStudio 6.4 or later, supports void-safety
|
||||
0.1.0 2010-02-07 First release, Adapted to SmartEiffel 1.2r7 and EiffelStudio 6.2 or previous
|
||||
Submodule
+1
Submodule doc/wiki added at a2a1f89299
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="hello_routed_world" uuid="7C9887BD-4AE4-47F2-A0AA-4BBB6736D433">
|
||||
<target name="hello_routed_world">
|
||||
<root class="HELLO_ROUTED_WORLD" feature="make"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/\.git$</exclude>
|
||||
<exclude>/\.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="provisional">
|
||||
<assertions precondition="true" postcondition="true" invariant="true" supplier_precondition="true"/>
|
||||
</option>
|
||||
<setting name="concurrency" value="thread"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="connector_nino" location="..\..\library\server\ewsgi\connectors\nino\nino-safe.ecf" readonly="false"/>
|
||||
<library name="default_nino" location="..\..\library\server\ewsgi\default\ewsgi_nino-safe.ecf" readonly="false"/>
|
||||
<library name="encoder" location="..\..\library\text\encoder\encoder-safe.ecf" readonly="false"/>
|
||||
<library name="ewsgi" location="..\..\library\server\ewsgi\ewsgi-safe.ecf" readonly="false"/>
|
||||
<library name="http" location="..\..\library\protocol\http\http-safe.ecf" readonly="false"/>
|
||||
<library name="router" location="..\..\library\server\request\router\router-safe.ecf" readonly="false"/>
|
||||
<library name="uri_template" location="..\..\library\protocol\uri_template\uri_template-safe.ecf" readonly="false"/>
|
||||
<cluster name="src" location="src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,10 @@
|
||||
${NOTE_KEYWORD}
|
||||
copyright: "2011-${YEAR}, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
@@ -0,0 +1,145 @@
|
||||
note
|
||||
description: "Summary description for {ROUTED_APPLICATION_HELPER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
ROUTED_APPLICATION_HELPER
|
||||
|
||||
inherit
|
||||
ANY
|
||||
|
||||
HTTP_FORMAT_CONSTANTS
|
||||
export
|
||||
{NONE} all
|
||||
end
|
||||
|
||||
feature -- Helper
|
||||
|
||||
execute_content_type_not_allowed (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER; a_content_types: detachable ARRAY [STRING]; a_uri_formats: detachable ARRAY [STRING])
|
||||
local
|
||||
s, uri_s: detachable STRING
|
||||
i, n: INTEGER
|
||||
h: GW_HEADER
|
||||
do
|
||||
create h.make
|
||||
h.put_status ({HTTP_STATUS_CODE}.unsupported_media_type)
|
||||
h.put_content_type_text_plain
|
||||
|
||||
if a_content_types /= Void then
|
||||
create s.make (10)
|
||||
from
|
||||
i := a_content_types.lower
|
||||
n := a_content_types.upper
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
s.append_string (a_content_types[i])
|
||||
if i < n then
|
||||
s.append_character (',')
|
||||
s.append_character (' ')
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
h.put_header_key_value ("Accept", s)
|
||||
end
|
||||
if a_uri_formats /= Void then
|
||||
create uri_s.make (10)
|
||||
from
|
||||
i := a_uri_formats.lower
|
||||
n := a_uri_formats.upper
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
uri_s.append_string (a_uri_formats[i])
|
||||
if i < n then
|
||||
uri_s.append_character (',')
|
||||
uri_s.append_character (' ')
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
if s /= Void then
|
||||
res.write_string ("Unsupported request content-type, Accept: " + s + "%N")
|
||||
end
|
||||
if uri_s /= Void then
|
||||
res.write_string ("Unsupported request format from the URI: " + uri_s + "%N")
|
||||
end
|
||||
end
|
||||
|
||||
execute_method_not_allowed (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER; a_methods: ARRAY [STRING])
|
||||
local
|
||||
s: STRING
|
||||
i, n: INTEGER
|
||||
do
|
||||
create s.make (10)
|
||||
from
|
||||
i := a_methods.lower
|
||||
n := a_methods.upper
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
s.append_string (a_methods[i])
|
||||
if i < n then
|
||||
s.append_character (',')
|
||||
s.append_character (' ')
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
|
||||
res.write_header ({HTTP_STATUS_CODE}.method_not_allowed, <<
|
||||
["Content-Type", {HTTP_CONSTANTS}.plain_text],
|
||||
["Allow", s]
|
||||
>>)
|
||||
res.write_string ("Unsupported request method, Allow: " + s + "%N")
|
||||
end
|
||||
|
||||
feature -- Context helper
|
||||
|
||||
request_format_id (ctx: REQUEST_HANDLER_CONTEXT; a_format_variable_name: detachable STRING; content_type_supported: detachable ARRAY [STRING]): INTEGER
|
||||
-- Format id for the request based on {HTTP_FORMAT_CONSTANTS}
|
||||
local
|
||||
l_format: detachable STRING_8
|
||||
do
|
||||
if a_format_variable_name /= Void and then attached ctx.parameter (a_format_variable_name) as ctx_format then
|
||||
l_format := ctx_format.as_string_8
|
||||
else
|
||||
l_format := content_type_to_request_format (ctx.request_content_type (content_type_supported))
|
||||
end
|
||||
if l_format /= Void then
|
||||
Result := format_id (l_format)
|
||||
else
|
||||
Result := 0
|
||||
end
|
||||
end
|
||||
|
||||
content_type_to_request_format (a_content_type: detachable STRING): detachable STRING
|
||||
-- `a_content_type' converted into a request format name
|
||||
do
|
||||
if a_content_type /= Void then
|
||||
if a_content_type.same_string ({HTTP_CONSTANTS}.json_text) then
|
||||
Result := {HTTP_FORMAT_CONSTANTS}.json_name
|
||||
elseif a_content_type.same_string ({HTTP_CONSTANTS}.json_app) then
|
||||
Result := {HTTP_FORMAT_CONSTANTS}.json_name
|
||||
elseif a_content_type.same_string ({HTTP_CONSTANTS}.xml_text) then
|
||||
Result := {HTTP_FORMAT_CONSTANTS}.xml_name
|
||||
elseif a_content_type.same_string ({HTTP_CONSTANTS}.html_text) then
|
||||
Result := {HTTP_FORMAT_CONSTANTS}.html_name
|
||||
elseif a_content_type.same_string ({HTTP_CONSTANTS}.plain_text) then
|
||||
Result := {HTTP_FORMAT_CONSTANTS}.text_name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,183 @@
|
||||
note
|
||||
description : "Objects that ..."
|
||||
author : "$Author$"
|
||||
date : "$Date$"
|
||||
revision : "$Revision$"
|
||||
|
||||
class
|
||||
HELLO_ROUTED_WORLD
|
||||
|
||||
inherit
|
||||
ANY
|
||||
|
||||
ROUTED_APPLICATION
|
||||
|
||||
ROUTED_APPLICATION_HELPER
|
||||
|
||||
DEFAULT_EWSGI_APPLICATION
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
initialize_router
|
||||
make_and_launch
|
||||
end
|
||||
|
||||
create_router
|
||||
do
|
||||
-- create {REQUEST_URI_ROUTER} router.make (5)
|
||||
create {REQUEST_URI_TEMPLATE_ROUTER} router.make (5)
|
||||
end
|
||||
|
||||
setup_router
|
||||
local
|
||||
ra: REQUEST_AGENT_HANDLER
|
||||
do
|
||||
router.map_agent ("/home", agent execute_home)
|
||||
|
||||
create ra.make (agent handle_hello)
|
||||
router.map ("/hello/{name}.{format}", ra)
|
||||
router.map ("/hello.{format}/{name}", ra)
|
||||
router.map ("/hello/{name}", ra)
|
||||
|
||||
create ra.make (agent handle_anonymous_hello)
|
||||
router.map ("/hello", ra)
|
||||
router.map ("/hello.{format}", ra)
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
execute_default (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
|
||||
local
|
||||
h: GW_HEADER
|
||||
l_url: STRING
|
||||
e: EXECUTION_ENVIRONMENT
|
||||
n: INTEGER
|
||||
i: INTEGER
|
||||
do
|
||||
create h.make
|
||||
l_url := req.script_url ("/home")
|
||||
n := 3
|
||||
h.put_refresh (l_url, 5, 200)
|
||||
res.set_status_code (200)
|
||||
res.write_headers_string (h.string)
|
||||
from
|
||||
create e
|
||||
until
|
||||
n = 0
|
||||
loop
|
||||
if n > 1 then
|
||||
res.write_string ("Redirected to " + l_url + " in " + n.out + " seconds :%N")
|
||||
else
|
||||
res.write_string ("Redirected to " + l_url + " in 1 second :%N")
|
||||
end
|
||||
res.flush
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i = 1001
|
||||
loop
|
||||
res.write_string (".")
|
||||
if i \\ 100 = 0 then
|
||||
res.write_string ("%N")
|
||||
end
|
||||
res.flush
|
||||
e.sleep (1_000_000)
|
||||
i := i + 1
|
||||
end
|
||||
res.write_string ("%N")
|
||||
n := n - 1
|
||||
end
|
||||
res.write_string ("You are now being redirected...%N")
|
||||
res.flush
|
||||
end
|
||||
|
||||
execute_home (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
|
||||
do
|
||||
res.write_header (200, <<["Content-Type", "text/html"]>>)
|
||||
res.write_string ("<html><body>Hello World ?!%N")
|
||||
res.write_string ("<h3>Please try the following links</h3><ul>%N")
|
||||
res.write_string ("<li><a href=%""+ req.script_url ("/") + "%">default</a></li>%N")
|
||||
res.write_string ("<li><a href=%""+ req.script_url ("/hello") + "%">/hello</a></li>%N")
|
||||
res.write_string ("<li><a href=%""+ req.script_url ("/hello.html/Joce") + "%">/hello.html/Joce</a></li>%N")
|
||||
res.write_string ("<li><a href=%""+ req.script_url ("/hello.json/Joce") + "%">/hello.json/Joce</a></li>%N")
|
||||
res.write_string ("<li><a href=%""+ req.script_url ("/hello/Joce.html") + "%">/hello/Joce.html</a></li>%N")
|
||||
res.write_string ("<li><a href=%""+ req.script_url ("/hello/Joce.xml") + "%">/hello/Joce.xml</a></li>%N")
|
||||
res.write_string ("<li><a href=%""+ req.script_url ("/hello/Joce") + "%">/hello/Joce</a></li>%N")
|
||||
res.write_string ("</ul>%N")
|
||||
|
||||
if attached req.environment_variable ("REQUEST_COUNT") as rqc then
|
||||
res.write_string ("request #"+ rqc + "%N")
|
||||
end
|
||||
res.write_string ("</body></html>%N")
|
||||
end
|
||||
|
||||
execute_hello (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER; a_name: detachable STRING_32; ctx: REQUEST_HANDLER_CONTEXT)
|
||||
local
|
||||
l_response_content_type: detachable STRING
|
||||
msg: STRING
|
||||
h: GW_HEADER
|
||||
content_type_supported: ARRAY [STRING]
|
||||
do
|
||||
if a_name /= Void then
|
||||
msg := "Hello %"" + a_name + "%" !%N"
|
||||
else
|
||||
msg := "Hello anonymous visitor !%N"
|
||||
end
|
||||
content_type_supported := <<{HTTP_CONSTANTS}.json_app, {HTTP_CONSTANTS}.html_text, {HTTP_CONSTANTS}.xml_text, {HTTP_CONSTANTS}.plain_text>>
|
||||
inspect request_format_id (ctx, "format", content_type_supported)
|
||||
when {HTTP_FORMAT_CONSTANTS}.json then
|
||||
l_response_content_type := {HTTP_CONSTANTS}.json_app
|
||||
msg := "{%N%"application%": %"/hello%",%N %"message%": %"" + msg + "%" %N}"
|
||||
when {HTTP_FORMAT_CONSTANTS}.html then
|
||||
l_response_content_type := {HTTP_CONSTANTS}.html_text
|
||||
when {HTTP_FORMAT_CONSTANTS}.xml then
|
||||
l_response_content_type := {HTTP_CONSTANTS}.xml_text
|
||||
msg := "<response><application>/hello</application><message>" + msg + "</message></response>%N"
|
||||
when {HTTP_FORMAT_CONSTANTS}.text then
|
||||
l_response_content_type := {HTTP_CONSTANTS}.plain_text
|
||||
else
|
||||
execute_content_type_not_allowed (req, res, content_type_supported,
|
||||
<<{HTTP_FORMAT_CONSTANTS}.json_name, {HTTP_FORMAT_CONSTANTS}.html_name, {HTTP_FORMAT_CONSTANTS}.xml_name, {HTTP_FORMAT_CONSTANTS}.text_name>>
|
||||
)
|
||||
end
|
||||
if l_response_content_type /= Void then
|
||||
create h.make
|
||||
h.put_status (200)
|
||||
h.put_content_type (l_response_content_type)
|
||||
h.put_content_length (msg.count)
|
||||
res.set_status_code (200)
|
||||
res.write_string (h.string)
|
||||
-- res.write_header (200, <<
|
||||
-- ["Content-Type", l_response_content_type],
|
||||
-- ["Content-Length", msg.count.out
|
||||
-- >>)
|
||||
res.write_string (msg)
|
||||
end
|
||||
end
|
||||
|
||||
handle_hello (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
|
||||
do
|
||||
execute_hello (req, res, Void, ctx)
|
||||
end
|
||||
|
||||
handle_anonymous_hello (ctx: REQUEST_HANDLER_CONTEXT; req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER)
|
||||
do
|
||||
execute_hello (req, res, ctx.parameter ("name"), ctx)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1 @@
|
||||
Examples ...
|
||||
Submodule
+1
Submodule ext/server/nino added at 9dd1439a2f
Submodule
+1
Submodule ext/text/json added at e1c66b50ca
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="error" uuid="C2DFF741-7091-43C7-B8B1-B075E7FF914F" library_target="error">
|
||||
<target name="error">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<cluster name="src" location="src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="error" uuid="C2DFF741-7091-43C7-B8B1-B075E7FF914F" library_target="error">
|
||||
<target name="error">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<cluster name="src" location="src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,10 @@
|
||||
${NOTE_KEYWORD}
|
||||
copyright: "2011-${YEAR}, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
@@ -0,0 +1,80 @@
|
||||
note
|
||||
description : "Objects that represent an error"
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
ERROR
|
||||
|
||||
feature -- Access
|
||||
|
||||
code: INTEGER
|
||||
deferred
|
||||
ensure
|
||||
result_not_zero: Result /= 0
|
||||
end
|
||||
|
||||
name: STRING
|
||||
deferred
|
||||
ensure
|
||||
result_attached: Result /= Void
|
||||
end
|
||||
|
||||
message: detachable STRING_32
|
||||
-- Potential error message
|
||||
deferred
|
||||
end
|
||||
|
||||
parent: detachable ERROR
|
||||
-- Eventual error prior to Current
|
||||
|
||||
feature -- String representation
|
||||
|
||||
string_representation: STRING_32
|
||||
-- String representation for Current
|
||||
do
|
||||
create Result.make_from_string (name.as_string_32)
|
||||
Result.append_character (' ')
|
||||
Result.append_character ('(')
|
||||
Result.append_integer (code)
|
||||
Result.append_character (')')
|
||||
if attached message as m then
|
||||
Result.append_character (':')
|
||||
Result.append_character (' ')
|
||||
Result.append_string (m)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Change
|
||||
|
||||
set_parent (a_parent: like parent)
|
||||
-- Set `parent' to `a_parent'
|
||||
do
|
||||
parent := a_parent
|
||||
end
|
||||
|
||||
feature -- Visitor
|
||||
|
||||
process (a_visitor: ERROR_VISITOR)
|
||||
-- Process Current using `a_visitor'.
|
||||
require
|
||||
a_visitor_not_void: a_visitor /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
invariant
|
||||
name_attached: name /= Void
|
||||
|
||||
note
|
||||
copyright: "Copyright (c) 1984-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,57 @@
|
||||
note
|
||||
description : "Objects that represent a custom error"
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
ERROR_CUSTOM
|
||||
|
||||
inherit
|
||||
ERROR
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_code: INTEGER; a_name: STRING; a_message: detachable like message)
|
||||
-- Initialize `Current'.
|
||||
do
|
||||
code := a_code
|
||||
name := a_name
|
||||
if attached a_message then
|
||||
message := a_message
|
||||
else
|
||||
message := "Error: " + a_name + " (code=" + a_code.out + ")"
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
code: INTEGER
|
||||
|
||||
name: STRING
|
||||
|
||||
message: STRING_32
|
||||
|
||||
feature -- Visitor
|
||||
|
||||
process (a_visitor: ERROR_VISITOR)
|
||||
-- Process Current using `a_visitor'.
|
||||
do
|
||||
a_visitor.process_custom (Current)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "Copyright (c) 1984-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,75 @@
|
||||
note
|
||||
description : "Objects that represent a group of errors"
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
ERROR_GROUP
|
||||
|
||||
inherit
|
||||
ERROR
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_errors: LIST [ERROR])
|
||||
-- Initialize `Current'.
|
||||
do
|
||||
name := a_errors.count.out + " errors"
|
||||
create {ARRAYED_LIST [ERROR]} sub_errors.make (a_errors.count)
|
||||
sub_errors.fill (a_errors)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
code: INTEGER = -1
|
||||
|
||||
name: STRING
|
||||
|
||||
message: detachable STRING_32
|
||||
do
|
||||
create Result.make_from_string (name)
|
||||
from
|
||||
sub_errors.start
|
||||
until
|
||||
sub_errors.after
|
||||
loop
|
||||
if
|
||||
attached sub_errors.item as e and then
|
||||
attached e.message as m
|
||||
then
|
||||
|
||||
Result.append_character ('%N')
|
||||
Result.append_string (m)
|
||||
end
|
||||
sub_errors.forth
|
||||
end
|
||||
end
|
||||
|
||||
sub_errors: LIST [ERROR]
|
||||
-- Error contained by Current
|
||||
|
||||
feature -- Visitor
|
||||
|
||||
process (a_visitor: ERROR_VISITOR)
|
||||
-- Process Current using `a_visitor'.
|
||||
do
|
||||
a_visitor.process_group (Current)
|
||||
end
|
||||
|
||||
|
||||
note
|
||||
copyright: "Copyright (c) 1984-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,129 @@
|
||||
note
|
||||
description : "Objects that handle error..."
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
ERROR_HANDLER
|
||||
|
||||
inherit
|
||||
ANY
|
||||
|
||||
DEBUG_OUTPUT
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
-- Initialize `Current'.
|
||||
do
|
||||
create {ARRAYED_LIST [ERROR]} errors.make (3)
|
||||
end
|
||||
|
||||
feature -- Status
|
||||
|
||||
has_error: BOOLEAN
|
||||
-- Has error?
|
||||
do
|
||||
Result := count > 0
|
||||
end
|
||||
|
||||
count: INTEGER
|
||||
do
|
||||
Result := errors.count
|
||||
end
|
||||
|
||||
errors: LIST [ERROR]
|
||||
-- Errors container
|
||||
|
||||
feature -- Status report
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
if has_error then
|
||||
Result := count.out + " errors"
|
||||
else
|
||||
Result := "no error"
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Basic operation
|
||||
|
||||
add_error (a_error: ERROR)
|
||||
-- Add `a_error' to the stack of error
|
||||
do
|
||||
errors.force (a_error)
|
||||
end
|
||||
|
||||
add_error_details, add_custom_error (a_code: INTEGER; a_name: STRING; a_message: detachable STRING_32)
|
||||
-- Add custom error to the stack of error
|
||||
local
|
||||
e: ERROR_CUSTOM
|
||||
do
|
||||
create e.make (a_code, a_name, a_message)
|
||||
add_error (e)
|
||||
end
|
||||
|
||||
append (a_err_handler: ERROR_HANDLER)
|
||||
-- Append errors from `a_err_handler'
|
||||
do
|
||||
errors.append (a_err_handler.errors)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
as_single_error: detachable ERROR
|
||||
do
|
||||
if count > 1 then
|
||||
create {ERROR_GROUP} Result.make (errors)
|
||||
elseif count > 0 then
|
||||
Result := errors.first
|
||||
end
|
||||
ensure
|
||||
has_error_implies_result_attached: has_error implies Result /= Void
|
||||
end
|
||||
|
||||
as_string_representation: STRING
|
||||
require
|
||||
has_error
|
||||
do
|
||||
if attached as_single_error as e then
|
||||
Result := e.string_representation
|
||||
else
|
||||
check has_error: False end
|
||||
Result := "Error occured"
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Element changes
|
||||
|
||||
concatenate
|
||||
-- Concatenate into a single error if any
|
||||
do
|
||||
if count > 1 and then attached as_single_error as e then
|
||||
wipe_out
|
||||
add_error (e)
|
||||
end
|
||||
end
|
||||
|
||||
reset, wipe_out
|
||||
do
|
||||
errors.wipe_out
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "Copyright (c) 1984-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,49 @@
|
||||
note
|
||||
description : "Error list iterator"
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
ERROR_ITERATOR
|
||||
|
||||
inherit
|
||||
ERROR_VISITOR
|
||||
|
||||
feature -- Access
|
||||
|
||||
process_error (e: ERROR)
|
||||
do
|
||||
end
|
||||
|
||||
process_custom (e: ERROR_CUSTOM)
|
||||
do
|
||||
process_error (e)
|
||||
end
|
||||
|
||||
process_group (g: ERROR_GROUP)
|
||||
do
|
||||
if attached g.sub_errors as err then
|
||||
from
|
||||
err.start
|
||||
until
|
||||
err.after
|
||||
loop
|
||||
process_error (err.item)
|
||||
err.forth
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "Copyright (c) 1984-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,38 @@
|
||||
note
|
||||
description : "Null error visitor"
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
ERROR_NULL_VISITOR
|
||||
|
||||
inherit
|
||||
ERROR_VISITOR
|
||||
|
||||
feature -- Access
|
||||
|
||||
process_error (e: ERROR)
|
||||
do
|
||||
end
|
||||
|
||||
process_custom (e: ERROR_CUSTOM)
|
||||
do
|
||||
end
|
||||
|
||||
process_group (g: ERROR_GROUP)
|
||||
do
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "Copyright (c) 1984-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,35 @@
|
||||
note
|
||||
description : "Objects to visit an ERROR"
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
ERROR_VISITOR
|
||||
|
||||
feature -- Access
|
||||
|
||||
process_error (e: ERROR)
|
||||
deferred
|
||||
end
|
||||
|
||||
process_custom (e: ERROR_CUSTOM)
|
||||
deferred
|
||||
end
|
||||
|
||||
process_group (g: ERROR_GROUP)
|
||||
deferred
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "Copyright (c) 1984-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,53 @@
|
||||
note
|
||||
description: "File error output visitor"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
FILE_OUTPUT_ERROR_VISITOR
|
||||
|
||||
inherit
|
||||
OUTPUT_ERROR_VISITOR
|
||||
redefine
|
||||
output_integer,
|
||||
output_new_line
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make (f: like file)
|
||||
require
|
||||
f_open_write: f /= Void and then f.is_open_write
|
||||
do
|
||||
file := f
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
file: FILE
|
||||
|
||||
feature -- Output
|
||||
|
||||
output_string (a_str: detachable STRING_GENERAL)
|
||||
-- Output Unicode string
|
||||
do
|
||||
if a_str /= Void then
|
||||
to_implement ("Convert into UTF-8 or console encoding before output")
|
||||
file.put_string (a_str.as_string_8)
|
||||
end
|
||||
end
|
||||
|
||||
output_integer (i: INTEGER)
|
||||
do
|
||||
file.put_integer (i)
|
||||
end
|
||||
|
||||
output_new_line
|
||||
do
|
||||
file.put_new_line
|
||||
end
|
||||
|
||||
end
|
||||
@@ -0,0 +1,93 @@
|
||||
note
|
||||
description: "General error output visitor"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
OUTPUT_ERROR_VISITOR
|
||||
|
||||
inherit
|
||||
ERROR_VISITOR
|
||||
|
||||
REFACTORING_HELPER
|
||||
|
||||
feature -- Output
|
||||
|
||||
output_string (a_str: detachable STRING_GENERAL)
|
||||
-- Output Unicode string
|
||||
deferred
|
||||
end
|
||||
|
||||
output_any (obj: detachable ANY)
|
||||
-- Output Unicode string
|
||||
do
|
||||
if attached {STRING_GENERAL} obj as l_str then
|
||||
to_implement ("Convert into UTF-8 or console encoding before output")
|
||||
output_string (l_str)
|
||||
elseif obj /= Void then
|
||||
output_string (obj.out)
|
||||
end
|
||||
end
|
||||
|
||||
output_integer (i: INTEGER)
|
||||
do
|
||||
output_string (i.out)
|
||||
end
|
||||
|
||||
output_new_line
|
||||
do
|
||||
output_string ("%N")
|
||||
end
|
||||
|
||||
feature -- Process
|
||||
|
||||
process_error (e: ERROR)
|
||||
do
|
||||
output_string ({STRING_32}"Error Name: ")
|
||||
output_string (e.name)
|
||||
output_string ({STRING_32}"Code: ")
|
||||
output_integer (e.code)
|
||||
output_new_line
|
||||
output_string ({STRING_32}"%TMessage: ")
|
||||
output_string (e.message)
|
||||
output_new_line
|
||||
end
|
||||
|
||||
process_custom (e: ERROR_CUSTOM)
|
||||
do
|
||||
output_string ({STRING_32}"Error Name: ")
|
||||
output_string (e.name)
|
||||
output_string ({STRING_32}"Code: ")
|
||||
output_integer (e.code)
|
||||
output_new_line
|
||||
output_string ({STRING_32}"%TMessage: ")
|
||||
output_string (e.message)
|
||||
output_new_line
|
||||
end
|
||||
|
||||
process_group (g: ERROR_GROUP)
|
||||
local
|
||||
l_errors: LIST [ERROR]
|
||||
do
|
||||
from
|
||||
l_errors := g.sub_errors
|
||||
l_errors.start
|
||||
until
|
||||
l_errors.after
|
||||
loop
|
||||
l_errors.item.process (Current)
|
||||
l_errors.forth
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "Copyright (c) 1984-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,53 @@
|
||||
note
|
||||
description: "Text error output visitor"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
TEXT_OUTPUT_ERROR_VISITOR
|
||||
|
||||
inherit
|
||||
OUTPUT_ERROR_VISITOR
|
||||
redefine
|
||||
output_integer,
|
||||
output_new_line
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make (buf: like buffer)
|
||||
require
|
||||
buf_attached: buf /= Void
|
||||
do
|
||||
buffer := buf
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
buffer: STRING
|
||||
|
||||
feature -- Output
|
||||
|
||||
output_string (a_str: detachable STRING_GENERAL)
|
||||
-- Output Unicode string
|
||||
do
|
||||
if a_str /= Void then
|
||||
to_implement ("Convert into UTF-8 or console encoding before output")
|
||||
buffer.append_string_general (a_str)
|
||||
end
|
||||
end
|
||||
|
||||
output_integer (i: INTEGER)
|
||||
do
|
||||
buffer.append_integer (i)
|
||||
end
|
||||
|
||||
output_new_line
|
||||
do
|
||||
buffer.append_character ('%N')
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,38 +0,0 @@
|
||||
note
|
||||
description: "Objects that ..."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
JSON_FILE_READER
|
||||
|
||||
feature -- Access
|
||||
|
||||
read_json_from (a_path: STRING): detachable STRING
|
||||
local
|
||||
l_file: PLAIN_TEXT_FILE
|
||||
template_content: STRING
|
||||
l_last_string: detachable STRING
|
||||
do
|
||||
create l_file.make (a_path)
|
||||
-- We perform several checks until we make a real attempt to open the file.
|
||||
if not l_file.exists then
|
||||
print ("error: '" + a_path + "' does not exist%N")
|
||||
else
|
||||
if not l_file.is_readable then
|
||||
print ("error: '" + a_path + "' is not readable.%N")
|
||||
else
|
||||
l_file.open_read
|
||||
create template_content.make_empty
|
||||
l_file.read_stream (l_file.count)
|
||||
l_last_string := l_file.last_string
|
||||
check l_last_string /= Void end -- implied by postcondition of `l_file.read_stream'
|
||||
template_content.append (l_last_string.string)
|
||||
Result := template_content
|
||||
l_file.close
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,175 +0,0 @@
|
||||
note
|
||||
description: "JSON_PRETTY_STRING_VISITOR Generates the JSON-String for a JSON_VALUE"
|
||||
revision: "0.1"
|
||||
|
||||
class
|
||||
JSON_PRETTY_STRING_VISITOR
|
||||
|
||||
inherit
|
||||
JSON_VISITOR
|
||||
|
||||
create
|
||||
make,
|
||||
make_custom
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make (a_output: like output)
|
||||
-- Create a new instance
|
||||
do
|
||||
make_custom (a_output, 1, 1)
|
||||
end
|
||||
|
||||
make_custom (a_output: like output; a_object_count_inlining, a_array_count_inlining: INTEGER)
|
||||
-- Create a new instance
|
||||
do
|
||||
output := a_output
|
||||
create indentation.make_empty
|
||||
indentation_step := "%T"
|
||||
|
||||
object_count_inlining := a_object_count_inlining
|
||||
array_count_inlining := a_array_count_inlining
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
output: STRING_32
|
||||
-- JSON representation
|
||||
|
||||
indentation: like output
|
||||
|
||||
indentation_step: like indentation
|
||||
|
||||
line_number: INTEGER
|
||||
|
||||
indent
|
||||
do
|
||||
indentation.append (indentation_step)
|
||||
end
|
||||
|
||||
exdent
|
||||
do
|
||||
indentation.remove_tail (indentation_step.count)
|
||||
end
|
||||
|
||||
new_line
|
||||
do
|
||||
output.append ("%N")
|
||||
output.append (indentation)
|
||||
line_number := line_number + 1
|
||||
end
|
||||
|
||||
object_count_inlining: INTEGER
|
||||
array_count_inlining: INTEGER
|
||||
|
||||
feature -- Visitor Pattern
|
||||
|
||||
visit_json_array (a_json_array: JSON_ARRAY)
|
||||
-- Visit `a_json_array'.
|
||||
local
|
||||
value: JSON_VALUE
|
||||
l_json_array: ARRAYED_LIST [JSON_VALUE]
|
||||
l_line: like line_number
|
||||
l_multiple_lines: BOOLEAN
|
||||
do
|
||||
l_json_array := a_json_array.array_representation
|
||||
l_multiple_lines := l_json_array.count >= array_count_inlining or across l_json_array as p some attached {JSON_OBJECT} p.item or attached {JSON_ARRAY} p.item end
|
||||
output.append ("[")
|
||||
l_line := line_number
|
||||
indent
|
||||
from
|
||||
l_json_array.start
|
||||
until
|
||||
l_json_array.off
|
||||
loop
|
||||
if
|
||||
line_number > l_line or
|
||||
l_multiple_lines
|
||||
then
|
||||
new_line
|
||||
end
|
||||
value := l_json_array.item
|
||||
value.accept (Current)
|
||||
l_json_array.forth
|
||||
if not l_json_array.after then
|
||||
output.append (", ")
|
||||
end
|
||||
end
|
||||
exdent
|
||||
if
|
||||
line_number > l_line or
|
||||
l_json_array.count >= array_count_inlining
|
||||
then
|
||||
new_line
|
||||
end
|
||||
output.append ("]")
|
||||
end
|
||||
|
||||
visit_json_boolean (a_json_boolean: JSON_BOOLEAN)
|
||||
-- Visit `a_json_boolean'.
|
||||
do
|
||||
output.append (a_json_boolean.item.out)
|
||||
end
|
||||
|
||||
visit_json_null (a_json_null: JSON_NULL)
|
||||
-- Visit `a_json_null'.
|
||||
do
|
||||
output.append ("null")
|
||||
end
|
||||
|
||||
visit_json_number (a_json_number: JSON_NUMBER)
|
||||
-- Visit `a_json_number'.
|
||||
do
|
||||
output.append (a_json_number.item)
|
||||
end
|
||||
|
||||
visit_json_object (a_json_object: JSON_OBJECT)
|
||||
-- Visit `a_json_object'.
|
||||
local
|
||||
l_pairs: HASH_TABLE [JSON_VALUE, JSON_STRING]
|
||||
l_line: like line_number
|
||||
l_multiple_lines: BOOLEAN
|
||||
do
|
||||
l_pairs := a_json_object.map_representation
|
||||
l_multiple_lines := l_pairs.count >= object_count_inlining or across l_pairs as p some attached {JSON_OBJECT} p.item or attached {JSON_ARRAY} p.item end
|
||||
output.append ("{")
|
||||
l_line := line_number
|
||||
indent
|
||||
from
|
||||
l_pairs.start
|
||||
until
|
||||
l_pairs.off
|
||||
loop
|
||||
if
|
||||
line_number > l_line or
|
||||
l_multiple_lines
|
||||
then
|
||||
new_line
|
||||
end
|
||||
l_pairs.key_for_iteration.accept (Current)
|
||||
output.append (": ")
|
||||
l_pairs.item_for_iteration.accept (Current)
|
||||
l_pairs.forth
|
||||
if not l_pairs.after then
|
||||
output.append (", ")
|
||||
end
|
||||
end
|
||||
exdent
|
||||
if
|
||||
line_number > l_line or
|
||||
l_pairs.count >= object_count_inlining
|
||||
then
|
||||
new_line
|
||||
end
|
||||
output.append ("}")
|
||||
end
|
||||
|
||||
visit_json_string (a_json_string: JSON_STRING)
|
||||
-- Visit `a_json_string'.
|
||||
do
|
||||
output.append ("%"")
|
||||
output.append (a_json_string.item)
|
||||
output.append ("%"")
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,59 +0,0 @@
|
||||
note
|
||||
description:
|
||||
|
||||
"JSON Visitor"
|
||||
|
||||
pattern: "Visitor"
|
||||
author: "Javier Velilla"
|
||||
license:"MIT (see http://www.opensource.org/licenses/mit-license.php)"
|
||||
date: "2008/08/24"
|
||||
revision: "Revision 0.1"
|
||||
|
||||
deferred class
|
||||
JSON_VISITOR
|
||||
|
||||
feature -- Visitor Pattern
|
||||
|
||||
visit_json_array (a_json_array: JSON_ARRAY)
|
||||
-- Visit `a_json_array'.
|
||||
require
|
||||
a_json_array_not_void: a_json_array /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
visit_json_boolean (a_json_boolean: JSON_BOOLEAN)
|
||||
-- Visit `a_json_boolean'.
|
||||
require
|
||||
a_json_boolean_not_void: a_json_boolean /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
visit_json_null (a_json_null: JSON_NULL)
|
||||
-- Visit `a_json_null'.
|
||||
require
|
||||
a_json_null_not_void: a_json_null /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
visit_json_number (a_json_number: JSON_NUMBER)
|
||||
-- Visit `a_json_number'.
|
||||
require
|
||||
a_json_number_not_void: a_json_number /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
visit_json_object (a_json_object: JSON_OBJECT)
|
||||
-- Visit `a_json_object'.
|
||||
require
|
||||
a_json_object_not_void: a_json_object /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
visit_json_string (a_json_string: JSON_STRING)
|
||||
-- Visit `a_json_string'.
|
||||
require
|
||||
a_json_string_not_void: a_json_string /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,102 +0,0 @@
|
||||
note
|
||||
description: "PRINT_JSON_VISITOR Generates the JSON-String for a JSON_VALUE"
|
||||
author: "jvelilla"
|
||||
date: "2008/08/24"
|
||||
revision: "0.1"
|
||||
|
||||
class
|
||||
PRINT_JSON_VISITOR
|
||||
|
||||
inherit
|
||||
JSON_VISITOR
|
||||
|
||||
create make
|
||||
|
||||
feature -- Initialization
|
||||
|
||||
make
|
||||
-- Create a new instance
|
||||
do
|
||||
create to_json.make_empty
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
to_json: STRING
|
||||
-- JSON representation
|
||||
|
||||
feature -- Visitor Pattern
|
||||
|
||||
visit_json_array (a_json_array: JSON_ARRAY)
|
||||
-- Visit `a_json_array'.
|
||||
local
|
||||
value: JSON_VALUE
|
||||
l_json_array: ARRAYED_LIST [JSON_VALUE]
|
||||
do
|
||||
l_json_array:=a_json_array.array_representation
|
||||
to_json.append ("[")
|
||||
from
|
||||
l_json_array.start
|
||||
until
|
||||
l_json_array.off
|
||||
loop
|
||||
value := l_json_array.item
|
||||
value.accept (Current)
|
||||
l_json_array.forth
|
||||
if not l_json_array.after then
|
||||
to_json.append(",")
|
||||
end
|
||||
end
|
||||
to_json.append ("]")
|
||||
end
|
||||
|
||||
visit_json_boolean (a_json_boolean: JSON_BOOLEAN)
|
||||
-- Visit `a_json_boolean'.
|
||||
do
|
||||
to_json.append (a_json_boolean.item.out)
|
||||
end
|
||||
|
||||
visit_json_null (a_json_null: JSON_NULL)
|
||||
-- Visit `a_json_null'.
|
||||
do
|
||||
to_json.append ("null")
|
||||
end
|
||||
|
||||
visit_json_number (a_json_number: JSON_NUMBER)
|
||||
-- Visit `a_json_number'.
|
||||
do
|
||||
to_json.append (a_json_number.item)
|
||||
end
|
||||
|
||||
visit_json_object (a_json_object: JSON_OBJECT)
|
||||
-- Visit `a_json_object'.
|
||||
local
|
||||
l_pairs: HASH_TABLE[JSON_VALUE,JSON_STRING]
|
||||
do
|
||||
l_pairs := a_json_object.map_representation
|
||||
to_json.append ("{")
|
||||
from
|
||||
l_pairs.start
|
||||
until
|
||||
l_pairs.off
|
||||
loop
|
||||
l_pairs.key_for_iteration.accept (Current)
|
||||
to_json.append (":")
|
||||
l_pairs.item_for_iteration.accept (Current)
|
||||
l_pairs.forth
|
||||
if not l_pairs.after then
|
||||
to_json.append (",")
|
||||
end
|
||||
end
|
||||
to_json.append ("}")
|
||||
end
|
||||
|
||||
visit_json_string (a_json_string: JSON_STRING)
|
||||
-- Visit `a_json_string'.
|
||||
do
|
||||
to_json.append ("%"")
|
||||
to_json.append (a_json_string.item)
|
||||
to_json.append ("%"")
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,81 +0,0 @@
|
||||
note
|
||||
description: "A JSON converter for DS_HASH_TABLE [ANY, HASHABLE]"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date: $"
|
||||
revision: "$Revision: $"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
class JSON_DS_HASH_TABLE_CONVERTER
|
||||
|
||||
inherit
|
||||
JSON_CONVERTER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
create object.make (0)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
value: JSON_OBJECT
|
||||
|
||||
object: DS_HASH_TABLE [ANY, HASHABLE]
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
from_json (j: like value): detachable like object
|
||||
local
|
||||
keys: ARRAY [JSON_STRING]
|
||||
i: INTEGER
|
||||
h: HASHABLE
|
||||
a: ANY
|
||||
do
|
||||
keys := j.current_keys
|
||||
create Result.make (keys.count)
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > keys.count
|
||||
loop
|
||||
h ?= json.object (keys [i], void)
|
||||
check h /= Void end
|
||||
a := json.object (j.item (keys [i]), Void)
|
||||
Result.put (a, h)
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
to_json (o: like object): like value
|
||||
local
|
||||
c: DS_HASH_TABLE_CURSOR [ANY, HASHABLE]
|
||||
js: JSON_STRING
|
||||
jv: JSON_VALUE
|
||||
failed: BOOLEAN
|
||||
do
|
||||
create Result.make
|
||||
from
|
||||
c := o.new_cursor
|
||||
c.start
|
||||
until
|
||||
c.after
|
||||
loop
|
||||
create js.make_json (c.key.out)
|
||||
jv := json.value (c.item)
|
||||
if jv /= Void then
|
||||
Result.put (jv, js)
|
||||
else
|
||||
failed := True
|
||||
end
|
||||
c.forth
|
||||
end
|
||||
if failed then
|
||||
Result := Void
|
||||
end
|
||||
end
|
||||
|
||||
end -- class JSON_DS_HASH_TABLE_CONVERTER
|
||||
@@ -1,62 +0,0 @@
|
||||
note
|
||||
description: "A JSON converter for DS_LINKED_LIST [ANY]"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date: $"
|
||||
revision: "$Revision: $"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
class JSON_DS_LINKED_LIST_CONVERTER
|
||||
|
||||
inherit
|
||||
JSON_CONVERTER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
create object.make
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
value: JSON_ARRAY
|
||||
|
||||
object: DS_LINKED_LIST [ANY]
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
from_json (j: like value): detachable like object
|
||||
local
|
||||
i: INTEGER
|
||||
do
|
||||
create Result.make
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > j.count
|
||||
loop
|
||||
Result.put_last (json.object (j [i], Void))
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
to_json (o: like object): like value
|
||||
local
|
||||
c: DS_LIST_CURSOR [ANY]
|
||||
do
|
||||
create Result.make_array
|
||||
from
|
||||
c := o.new_cursor
|
||||
c.start
|
||||
until
|
||||
c.after
|
||||
loop
|
||||
Result.add (json.value (c.item))
|
||||
c.forth
|
||||
end
|
||||
end
|
||||
|
||||
end -- class JSON_DS_LINKED_LIST_CONVERTER
|
||||
@@ -1,32 +0,0 @@
|
||||
note
|
||||
description: "[
|
||||
Shared factory class for creating JSON objects. Maps JSON
|
||||
objects to Gobo DS_HASH_TABLEs and JSON arrays to Gobo
|
||||
DS_LINKED_LISTs. Use non-conforming inheritance from this
|
||||
class to ensure that your classes share the same
|
||||
JSON_FACTORY instance.
|
||||
]"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
class SHARED_GOBO_EJSON
|
||||
|
||||
feature
|
||||
|
||||
json: EJSON
|
||||
-- A shared EJSON instance with default converters for
|
||||
-- DS_LINKED_LIST [ANY] and DS_HASH_TABLE [ANY, HASHABLE]
|
||||
local
|
||||
jllc: JSON_DS_LINKED_LIST_CONVERTER
|
||||
jhtc: JSON_DS_HASH_TABLE_CONVERTER
|
||||
once
|
||||
create Result
|
||||
create jllc.make
|
||||
Result.add_converter (jllc)
|
||||
create jhtc.make
|
||||
Result.add_converter (jhtc)
|
||||
end
|
||||
|
||||
end -- class SHARED_GOBO_EJSON
|
||||
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="json" uuid="4E21C3BD-7951-4C6E-A673-431E762D7414" library_target="json">
|
||||
<target name="json">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option trace="false" profile="false" debug="false" warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all" syntax="standard" namespace="EJSON.Library">
|
||||
<assertions/>
|
||||
<warning name="export_class_missing" enabled="false"/>
|
||||
<warning name="old_verbatim_strings" enabled="false"/>
|
||||
<warning name="syntax" enabled="false"/>
|
||||
<warning name="vjrv" enabled="false"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf" readonly="true"/>
|
||||
<cluster name="json" location=".\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>^/gobo$</exclude>
|
||||
<exclude>^/kernel$</exclude>
|
||||
<exclude>^/extras$</exclude>
|
||||
</file_rule>
|
||||
<cluster name="kernel" location=".\kernel\" recursive="true"/>
|
||||
<cluster name="extras" location=".\extras\" recursive="true"/>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
@@ -1,28 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="json" uuid="4E21C3BD-7951-4C6E-A673-431E762D7414" library_target="json">
|
||||
<target name="json">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option trace="false" profile="false" debug="false" warning="true" full_class_checking="true" void_safety="none" syntax="standard" namespace="EJSON.Library">
|
||||
<assertions/>
|
||||
<warning name="export_class_missing" enabled="false"/>
|
||||
<warning name="old_verbatim_strings" enabled="false"/>
|
||||
<warning name="syntax" enabled="false"/>
|
||||
<warning name="vjrv" enabled="false"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf" readonly="true"/>
|
||||
<cluster name="json" location=".\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>^/gobo$</exclude>
|
||||
<exclude>^/kernel$</exclude>
|
||||
<exclude>^/extras$</exclude>
|
||||
</file_rule>
|
||||
<cluster name="kernel" location=".\kernel\" recursive="true"/>
|
||||
<cluster name="extras" location=".\extras\" recursive="true"/>
|
||||
</cluster>
|
||||
</target>
|
||||
</system>
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="json_gobo" uuid="437195AB-8B3C-4238-8998-A932A1423449" library_target="json_gobo">
|
||||
<target name="json_gobo">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/CVS$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option trace="false" profile="false" debug="false" warning="true" full_class_checking="true" void_safety="none" syntax="standard" namespace="EJSON.Library">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf" readonly="true"/>
|
||||
<library name="json" location="json.ecf" readonly="true"/>
|
||||
<library name="gobo_structure" location="$ISE_LIBRARY\library\gobo\gobo_structure.ecf"/>
|
||||
<cluster name="json_gobo" location=".\gobo" recursive="true" />
|
||||
</target>
|
||||
</system>
|
||||
@@ -1,30 +0,0 @@
|
||||
note
|
||||
description: "A JSON converter for ARRAYED_LIST [ANY]"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
class JSON_ARRAYED_LIST_CONVERTER
|
||||
|
||||
inherit
|
||||
JSON_LIST_CONVERTER
|
||||
redefine
|
||||
object
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Access
|
||||
|
||||
object: ARRAYED_LIST [detachable ANY]
|
||||
|
||||
feature {NONE} -- Factory
|
||||
|
||||
new_object (nb: INTEGER): like object
|
||||
do
|
||||
create Result.make (nb)
|
||||
end
|
||||
|
||||
end -- class JSON_ARRAYED_LIST_CONVERTER
|
||||
@@ -1,36 +0,0 @@
|
||||
note
|
||||
description: "A JSON converter"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
deferred class JSON_CONVERTER
|
||||
|
||||
inherit
|
||||
SHARED_EJSON
|
||||
|
||||
feature -- Access
|
||||
|
||||
object: ANY
|
||||
-- Eiffel object
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
from_json (j: attached like to_json): detachable like object
|
||||
-- Convert from JSON value.
|
||||
-- Returns Void if unable to convert
|
||||
deferred
|
||||
end
|
||||
|
||||
to_json (o: like object): detachable JSON_VALUE
|
||||
-- Convert to JSON value
|
||||
deferred
|
||||
end
|
||||
|
||||
invariant
|
||||
has_eiffel_object: object /= Void -- An empty object must be created at creation time!
|
||||
|
||||
end
|
||||
@@ -1,88 +0,0 @@
|
||||
note
|
||||
description: "A JSON converter for HASH_TABLE [ANY, HASHABLE]"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
class JSON_HASH_TABLE_CONVERTER
|
||||
|
||||
inherit
|
||||
JSON_CONVERTER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
create object.make (0)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
object: HASH_TABLE [ANY, HASHABLE]
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
from_json (j: attached like to_json): like object
|
||||
local
|
||||
keys: ARRAY [JSON_STRING]
|
||||
i: INTEGER
|
||||
h: detachable HASHABLE
|
||||
jv: detachable JSON_VALUE
|
||||
a: detachable ANY
|
||||
do
|
||||
keys := j.current_keys
|
||||
create Result.make (keys.count)
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > keys.count
|
||||
loop
|
||||
h ?= json.object (keys [i], Void)
|
||||
check h /= Void end
|
||||
jv := j.item (keys [i])
|
||||
if jv /= Void then
|
||||
a := json.object (jv, Void)
|
||||
if a /= Void then
|
||||
Result.put (a, h)
|
||||
else
|
||||
check a_attached: a /= Void end
|
||||
end
|
||||
else
|
||||
check j_has_item: False end
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
to_json (o: like object): detachable JSON_OBJECT
|
||||
local
|
||||
c: HASH_TABLE_ITERATION_CURSOR [ANY, HASHABLE]
|
||||
js: JSON_STRING
|
||||
jv: detachable JSON_VALUE
|
||||
failed: BOOLEAN
|
||||
do
|
||||
create Result.make
|
||||
from
|
||||
c := o.new_cursor
|
||||
until
|
||||
c.after
|
||||
loop
|
||||
create js.make_json (c.key.out)
|
||||
jv := json.value (c.item)
|
||||
if jv /= Void then
|
||||
Result.put (jv, js)
|
||||
else
|
||||
failed := True
|
||||
end
|
||||
c.forth
|
||||
end
|
||||
if failed then
|
||||
Result := Void
|
||||
end
|
||||
end
|
||||
|
||||
end -- class JSON_HASH_TABLE_CONVERTER
|
||||
@@ -1,30 +0,0 @@
|
||||
note
|
||||
description: "A JSON converter for LINKED_LIST [ANY]"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
class JSON_LINKED_LIST_CONVERTER
|
||||
|
||||
inherit
|
||||
JSON_LIST_CONVERTER
|
||||
redefine
|
||||
object
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Access
|
||||
|
||||
object: LINKED_LIST [detachable ANY]
|
||||
|
||||
feature {NONE} -- Factory
|
||||
|
||||
new_object (nb: INTEGER): like object
|
||||
do
|
||||
create Result.make
|
||||
end
|
||||
|
||||
end -- class JSON_LINKED_LIST_CONVERTER
|
||||
@@ -1,74 +0,0 @@
|
||||
note
|
||||
description: "A JSON converter for LIST [ANY]"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
deferred class JSON_LIST_CONVERTER
|
||||
|
||||
inherit
|
||||
JSON_CONVERTER
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
object := new_object (0)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
object: LIST [detachable ANY]
|
||||
|
||||
feature {NONE} -- Factory
|
||||
|
||||
new_object (nb: INTEGER): like object
|
||||
deferred
|
||||
ensure
|
||||
Result /= Void
|
||||
end
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
from_json (j: attached like to_json): detachable like object
|
||||
local
|
||||
i: INTEGER
|
||||
do
|
||||
Result := new_object (j.count)
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > j.count
|
||||
loop
|
||||
Result.extend (json.object (j [i], Void))
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
to_json (o: like object): detachable JSON_ARRAY
|
||||
local
|
||||
c: ITERATION_CURSOR [detachable ANY]
|
||||
jv: detachable JSON_VALUE
|
||||
failed: BOOLEAN
|
||||
do
|
||||
create Result.make_array
|
||||
from
|
||||
c := o.new_cursor
|
||||
until
|
||||
c.after
|
||||
loop
|
||||
jv := json.value (c.item)
|
||||
if jv /= Void then
|
||||
Result.add (jv)
|
||||
else
|
||||
failed := True
|
||||
end
|
||||
c.forth
|
||||
end
|
||||
if failed then
|
||||
Result := Void
|
||||
end
|
||||
end
|
||||
|
||||
end -- class JSON_ARRAYED_LIST_CONVERTER
|
||||
@@ -1,268 +0,0 @@
|
||||
note
|
||||
description: "Core factory class for creating JSON objects and corresponding Eiffel objects."
|
||||
author: "Paul Cohen"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
class EJSON
|
||||
|
||||
inherit
|
||||
EXCEPTIONS
|
||||
|
||||
feature -- Access
|
||||
|
||||
value (an_object: detachable ANY): detachable JSON_VALUE
|
||||
-- JSON value from Eiffel object. Raises an "eJSON exception" if
|
||||
-- unable to convert value.
|
||||
local
|
||||
i: INTEGER
|
||||
ja: JSON_ARRAY
|
||||
do
|
||||
-- Try to convert from basic Eiffel types. Note that we check with
|
||||
-- `conforms_to' since the client may have subclassed the base class
|
||||
-- that these basic types are derived from.
|
||||
if an_object = Void then
|
||||
create {JSON_NULL} Result
|
||||
elseif attached {BOOLEAN} an_object as b then
|
||||
create {JSON_BOOLEAN} Result.make_boolean (b)
|
||||
elseif attached {INTEGER_8} an_object as i8 then
|
||||
create {JSON_NUMBER} Result.make_integer (i8)
|
||||
elseif attached {INTEGER_16} an_object as i16 then
|
||||
create {JSON_NUMBER} Result.make_integer (i16)
|
||||
elseif attached {INTEGER_32} an_object as i32 then
|
||||
create {JSON_NUMBER} Result.make_integer (i32)
|
||||
elseif attached {INTEGER_64} an_object as i64 then
|
||||
create {JSON_NUMBER} Result.make_integer (i64)
|
||||
elseif attached {NATURAL_8} an_object as n8 then
|
||||
create {JSON_NUMBER} Result.make_natural (n8)
|
||||
elseif attached {NATURAL_16} an_object as n16 then
|
||||
create {JSON_NUMBER} Result.make_natural (n16)
|
||||
elseif attached {NATURAL_32} an_object as n32 then
|
||||
create {JSON_NUMBER} Result.make_natural (n32)
|
||||
elseif attached {NATURAL_64} an_object as n64 then
|
||||
create {JSON_NUMBER} Result.make_natural (n64)
|
||||
elseif attached {REAL_32} an_object as r32 then
|
||||
create {JSON_NUMBER} Result.make_real (r32)
|
||||
elseif attached {REAL_64} an_object as r64 then
|
||||
create {JSON_NUMBER} Result.make_real (r64)
|
||||
elseif attached {ARRAY [detachable ANY]} an_object as a then
|
||||
create ja.make_array
|
||||
from
|
||||
i := a.lower
|
||||
until
|
||||
i > a.upper
|
||||
loop
|
||||
if attached value (a @ i) as v then
|
||||
ja.add (v)
|
||||
else
|
||||
check value_attached: False end
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
Result := ja
|
||||
elseif attached {CHARACTER_8} an_object as c8 then
|
||||
create {JSON_STRING} Result.make_json (c8.out)
|
||||
elseif attached {CHARACTER_32} an_object as c32 then
|
||||
create {JSON_STRING} Result.make_json (c32.out)
|
||||
|
||||
elseif attached {STRING_8} an_object as s8 then
|
||||
create {JSON_STRING} Result.make_json (s8)
|
||||
elseif attached {STRING_32} an_object as s32 then
|
||||
create {JSON_STRING} Result.make_json_from_string_32 (s32)
|
||||
end
|
||||
|
||||
if Result = Void then
|
||||
-- Now check the converters
|
||||
if an_object /= Void and then attached converter_for (an_object) as jc then
|
||||
Result := jc.to_json (an_object)
|
||||
else
|
||||
raise (exception_failed_to_convert_to_json (an_object))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
object (a_value: detachable JSON_VALUE; base_class: detachable STRING): detachable ANY
|
||||
-- Eiffel object from JSON value. If `base_class' /= Void an eiffel
|
||||
-- object based on `base_class' will be returned. Raises an "eJSON
|
||||
-- exception" if unable to convert value.
|
||||
local
|
||||
i: INTEGER
|
||||
ll: LINKED_LIST [detachable ANY]
|
||||
t: HASH_TABLE [detachable ANY, STRING_GENERAL]
|
||||
keys: ARRAY [JSON_STRING]
|
||||
do
|
||||
if a_value = Void then
|
||||
Result := Void
|
||||
else
|
||||
if base_class = Void then
|
||||
if a_value = Void then
|
||||
Result := Void
|
||||
elseif attached {JSON_NULL} a_value then
|
||||
Result := Void
|
||||
elseif attached {JSON_BOOLEAN} a_value as jb then
|
||||
Result := jb.item
|
||||
elseif attached {JSON_NUMBER} a_value as jn then
|
||||
if jn.item.is_integer_8 then
|
||||
Result := jn.item.to_integer_8
|
||||
elseif jn.item.is_integer_16 then
|
||||
Result := jn.item.to_integer_16
|
||||
elseif jn.item.is_integer_32 then
|
||||
Result := jn.item.to_integer_32
|
||||
elseif jn.item.is_integer_64 then
|
||||
Result := jn.item.to_integer_64
|
||||
elseif jn.item.is_natural_64 then
|
||||
Result := jn.item.to_natural_64
|
||||
elseif jn.item.is_double then
|
||||
Result := jn.item.to_double
|
||||
end
|
||||
elseif attached {JSON_STRING} a_value as js then
|
||||
create {STRING_32} Result.make_from_string (js.unescaped_string_32)
|
||||
elseif attached {JSON_ARRAY} a_value as ja then
|
||||
from
|
||||
create ll.make
|
||||
i := 1
|
||||
until
|
||||
i > ja.count
|
||||
loop
|
||||
ll.extend (object (ja [i], Void))
|
||||
i := i + 1
|
||||
end
|
||||
Result := ll
|
||||
elseif attached {JSON_OBJECT} a_value as jo then
|
||||
keys := jo.current_keys
|
||||
create t.make (keys.count)
|
||||
from
|
||||
i := keys.lower
|
||||
until
|
||||
i > keys.upper
|
||||
loop
|
||||
if attached {STRING_GENERAL} object (keys [i], Void) as s then
|
||||
t.put (object (jo.item (keys [i]), Void), s)
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
Result := t
|
||||
end
|
||||
else
|
||||
if converters.has_key (base_class) and then attached converters.found_item as jc then
|
||||
Result := jc.from_json (a_value)
|
||||
else
|
||||
raise (exception_failed_to_convert_to_eiffel (a_value, base_class))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
object_from_json (json: STRING; base_class: detachable STRING): detachable ANY
|
||||
-- Eiffel object from JSON representation. If `base_class' /= Void an
|
||||
-- Eiffel object based on `base_class' will be returned. Raises an
|
||||
-- "eJSON exception" if unable to convert value.
|
||||
require
|
||||
json_not_void: json /= Void
|
||||
local
|
||||
jv: detachable JSON_VALUE
|
||||
do
|
||||
json_parser.set_representation (json)
|
||||
jv := json_parser.parse
|
||||
if jv /= Void then
|
||||
Result := object (jv, base_class)
|
||||
end
|
||||
end
|
||||
|
||||
converter_for (an_object: ANY): detachable JSON_CONVERTER
|
||||
-- Converter for objects. Returns Void if none found.
|
||||
require
|
||||
an_object_not_void: an_object /= Void
|
||||
do
|
||||
if converters.has_key (an_object.generator) then
|
||||
Result := converters.found_item
|
||||
end
|
||||
end
|
||||
|
||||
json_reference (s: STRING): JSON_OBJECT
|
||||
-- A JSON (Dojo style) reference object using `s' as the
|
||||
-- reference value. The caller is responsable for ensuring
|
||||
-- the validity of `s' as a json reference.
|
||||
require
|
||||
s_not_void: s /= Void
|
||||
local
|
||||
js_key, js_value: JSON_STRING
|
||||
do
|
||||
create Result.make
|
||||
create js_key.make_json ("$ref")
|
||||
create js_value.make_json (s)
|
||||
Result.put (js_value, js_key)
|
||||
end
|
||||
|
||||
json_references (l: LIST [STRING]): JSON_ARRAY
|
||||
-- A JSON array of JSON (Dojo style) reference objects using the
|
||||
-- strings in `l' as reference values. The caller is responsable
|
||||
-- for ensuring the validity of all strings in `l' as json
|
||||
-- references.
|
||||
require
|
||||
l_not_void: l /= Void
|
||||
local
|
||||
c: ITERATION_CURSOR [STRING]
|
||||
do
|
||||
create Result.make_array
|
||||
from
|
||||
c := l.new_cursor
|
||||
until
|
||||
c.after
|
||||
loop
|
||||
Result.add (json_reference (c.item))
|
||||
c.forth
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Change
|
||||
|
||||
add_converter (jc: JSON_CONVERTER)
|
||||
-- Add the converter `jc'.
|
||||
require
|
||||
jc_not_void: jc /= Void
|
||||
do
|
||||
converters.force (jc, jc.object.generator)
|
||||
ensure
|
||||
has_converter: converter_for (jc.object) /= Void
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
converters: HASH_TABLE [JSON_CONVERTER, STRING]
|
||||
-- Converters hashed by generator (base class)
|
||||
once
|
||||
create Result.make (10)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation (Exceptions)
|
||||
|
||||
exception_prefix: STRING = "eJSON exception: "
|
||||
|
||||
exception_failed_to_convert_to_eiffel (a_value: JSON_VALUE; base_class: detachable STRING): STRING
|
||||
-- Exception message for failing to convert a JSON_VALUE to an instance of `a'.
|
||||
do
|
||||
Result := exception_prefix + "Failed to convert JSON_VALUE to an Eiffel object: " + a_value.generator
|
||||
if base_class /= Void then
|
||||
Result.append (" -> " + base_class)
|
||||
end
|
||||
end
|
||||
|
||||
exception_failed_to_convert_to_json (an_object: detachable ANY): STRING
|
||||
-- Exception message for failing to convert `a' to a JSON_VALUE.
|
||||
do
|
||||
Result := exception_prefix + "Failed to convert Eiffel object to a JSON_VALUE"
|
||||
if an_object /= Void then
|
||||
Result := ": " + an_object.generator
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation (JSON parser)
|
||||
|
||||
json_parser: JSON_PARSER
|
||||
once
|
||||
create Result.make_parser ("")
|
||||
end
|
||||
|
||||
end -- class EJSON
|
||||
@@ -1,141 +0,0 @@
|
||||
note
|
||||
description: "[
|
||||
JSON_ARRAY represent an array in JSON.
|
||||
An array in JSON is an ordered set of names.
|
||||
Examples
|
||||
array
|
||||
[]
|
||||
[elements]
|
||||
]"
|
||||
|
||||
author: "Javier Velilla"
|
||||
date: "2008/08/24"
|
||||
revision: "Revision 0.1"
|
||||
|
||||
class
|
||||
JSON_ARRAY
|
||||
|
||||
inherit
|
||||
JSON_VALUE
|
||||
|
||||
DEBUG_OUTPUT
|
||||
|
||||
create
|
||||
make_array
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_array
|
||||
-- Initialize JSON Array
|
||||
do
|
||||
create values.make (10)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
i_th alias "[]" (i: INTEGER): JSON_VALUE
|
||||
-- Item at `i'-th position
|
||||
require
|
||||
is_valid_index: valid_index (i)
|
||||
do
|
||||
Result := values.i_th (i)
|
||||
end
|
||||
|
||||
representation: STRING
|
||||
local
|
||||
i: INTEGER
|
||||
do
|
||||
Result := "["
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > count
|
||||
loop
|
||||
Result.append (i_th (i).representation)
|
||||
i := i + 1
|
||||
if i <= count then
|
||||
Result.append_character (',')
|
||||
end
|
||||
end
|
||||
Result.append_character (']')
|
||||
end
|
||||
|
||||
feature -- Visitor pattern
|
||||
|
||||
accept (a_visitor: JSON_VISITOR)
|
||||
-- Accept `a_visitor'.
|
||||
-- (Call `visit_json_array' procedure on `a_visitor'.)
|
||||
do
|
||||
a_visitor.visit_json_array (Current)
|
||||
end
|
||||
|
||||
feature -- Mesurement
|
||||
|
||||
count: INTEGER
|
||||
-- Number of items.
|
||||
do
|
||||
Result := values.count
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
valid_index (i: INTEGER): BOOLEAN
|
||||
-- Is `i' a valid index?
|
||||
do
|
||||
Result := (1 <= i) and (i <= count)
|
||||
end
|
||||
|
||||
feature -- Change Element
|
||||
|
||||
add (value: JSON_VALUE)
|
||||
require
|
||||
value_not_null: value /= void
|
||||
do
|
||||
values.extend (value)
|
||||
ensure
|
||||
has_new_value: old values.count + 1 = values.count and
|
||||
values.has (value)
|
||||
end
|
||||
|
||||
feature -- Report
|
||||
|
||||
hash_code: INTEGER
|
||||
-- Hash code value
|
||||
do
|
||||
from
|
||||
values.start
|
||||
Result := values.item.hash_code
|
||||
until
|
||||
values.off
|
||||
loop
|
||||
Result:= ((Result \\ 8388593) |<< 8) + values.item.hash_code
|
||||
values.forth
|
||||
end
|
||||
Result := Result \\ values.count
|
||||
end
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
array_representation: ARRAYED_LIST [JSON_VALUE]
|
||||
-- Representation as a sequences of values
|
||||
do
|
||||
Result := values
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
Result := count.out
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
values: ARRAYED_LIST [JSON_VALUE]
|
||||
-- Value container
|
||||
|
||||
invariant
|
||||
value_not_void: values /= Void
|
||||
|
||||
end
|
||||
@@ -1,61 +0,0 @@
|
||||
note
|
||||
description: "JSON Truth values"
|
||||
author: "Javier Velilla"
|
||||
date: "2008/08/24"
|
||||
revision: "Revision 0.1"
|
||||
|
||||
class
|
||||
JSON_BOOLEAN
|
||||
|
||||
inherit
|
||||
JSON_VALUE
|
||||
|
||||
create
|
||||
make_boolean
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_boolean (an_item: BOOLEAN)
|
||||
--Initialize.
|
||||
do
|
||||
item := an_item
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
item: BOOLEAN
|
||||
-- Content
|
||||
|
||||
hash_code: INTEGER
|
||||
-- Hash code value
|
||||
do
|
||||
Result := item.hash_code
|
||||
end
|
||||
|
||||
representation: STRING
|
||||
do
|
||||
if item then
|
||||
Result := "true"
|
||||
else
|
||||
Result := "false"
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Visitor pattern
|
||||
|
||||
accept (a_visitor: JSON_VISITOR)
|
||||
-- Accept `a_visitor'.
|
||||
-- (Call `visit_json_boolean' procedure on `a_visitor'.)
|
||||
do
|
||||
a_visitor.visit_json_boolean (Current)
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
Result := item.out
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,47 +0,0 @@
|
||||
note
|
||||
description: "JSON Null Values"
|
||||
author: "Javier Velilla"
|
||||
date: "2008/08/24"
|
||||
revision: "Revision 0.1"
|
||||
|
||||
class
|
||||
JSON_NULL
|
||||
|
||||
inherit
|
||||
JSON_VALUE
|
||||
|
||||
feature --Access
|
||||
|
||||
hash_code: INTEGER
|
||||
-- Hash code value
|
||||
do
|
||||
Result := null_value.hash_code
|
||||
end
|
||||
|
||||
representation: STRING
|
||||
do
|
||||
Result := "null"
|
||||
end
|
||||
|
||||
feature -- Visitor pattern
|
||||
|
||||
accept (a_visitor: JSON_VISITOR)
|
||||
-- Accept `a_visitor'.
|
||||
-- (Call `visit_element_a' procedure on `a_visitor'.)
|
||||
do
|
||||
a_visitor.visit_json_null (Current)
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
Result := null_value
|
||||
end
|
||||
|
||||
feature {NONE}-- Implementation
|
||||
|
||||
null_value: STRING = "null"
|
||||
|
||||
end
|
||||
@@ -1,99 +0,0 @@
|
||||
note
|
||||
|
||||
description: "JSON Numbers, octal and hexadecimal formats are not used."
|
||||
author: "Javier Velilla"
|
||||
date: "2008/08/24"
|
||||
revision: "Revision 0.1"
|
||||
license:"MIT (see http://www.opensource.org/licenses/mit-license.php)"
|
||||
|
||||
class
|
||||
JSON_NUMBER
|
||||
|
||||
inherit
|
||||
JSON_VALUE
|
||||
redefine
|
||||
is_equal
|
||||
end
|
||||
|
||||
create
|
||||
make_integer,
|
||||
make_natural,
|
||||
make_real
|
||||
|
||||
feature {NONE} -- initialization
|
||||
|
||||
make_integer (an_argument: INTEGER_64)
|
||||
-- Initialize an instance of JSON_NUMBER from the integer value of `an_argument'.
|
||||
do
|
||||
item := an_argument.out
|
||||
numeric_type := INTEGER_TYPE
|
||||
end
|
||||
|
||||
make_natural (an_argument: NATURAL_64)
|
||||
-- Initialize an instance of JSON_NUMBER from the unsigned integer value of `an_argument'.
|
||||
do
|
||||
item := an_argument.out
|
||||
numeric_type := NATURAL_TYPE
|
||||
end
|
||||
|
||||
make_real (an_argument: DOUBLE)
|
||||
-- Initialize an instance of JSON_NUMBER from the floating point value of `an_argument'.
|
||||
do
|
||||
item := an_argument.out
|
||||
numeric_type := DOUBLE_TYPE
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
item: STRING
|
||||
-- Content
|
||||
|
||||
hash_code: INTEGER
|
||||
--Hash code value
|
||||
do
|
||||
Result := item.hash_code
|
||||
end
|
||||
|
||||
representation: STRING
|
||||
do
|
||||
Result := item
|
||||
end
|
||||
|
||||
feature -- Visitor pattern
|
||||
|
||||
accept (a_visitor: JSON_VISITOR)
|
||||
-- Accept `a_visitor'.
|
||||
-- (Call `visit_json_number' procedure on `a_visitor'.)
|
||||
do
|
||||
a_visitor.visit_json_number (Current)
|
||||
end
|
||||
|
||||
feature -- Status
|
||||
|
||||
is_equal (other: like Current): BOOLEAN
|
||||
-- Is `other' attached to an object of the same type
|
||||
-- as current object and identical to it?
|
||||
do
|
||||
Result := item.is_equal (other.item)
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
Result := item
|
||||
end
|
||||
|
||||
feature -- Implementation
|
||||
|
||||
INTEGER_TYPE: INTEGER = 1
|
||||
DOUBLE_TYPE: INTEGER = 2
|
||||
NATURAL_TYPE: INTEGER = 3
|
||||
|
||||
numeric_type: INTEGER
|
||||
|
||||
invariant
|
||||
item_not_void: item /= Void
|
||||
|
||||
end
|
||||
@@ -1,191 +0,0 @@
|
||||
note
|
||||
|
||||
description: "[
|
||||
An JSON_OBJECT represent an object in JSON.
|
||||
An object is an unordered set of name/value pairs
|
||||
|
||||
Examples:
|
||||
|
||||
object
|
||||
{}
|
||||
{"key","value"}
|
||||
|
||||
]"
|
||||
author: "Javier Velilla"
|
||||
date: "2008/08/24"
|
||||
revision: "Revision 0.1"
|
||||
license:"MIT (see http://www.opensource.org/licenses/mit-license.php)"
|
||||
|
||||
class
|
||||
JSON_OBJECT
|
||||
|
||||
inherit
|
||||
JSON_VALUE
|
||||
|
||||
TABLE_ITERABLE [JSON_VALUE, JSON_STRING]
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
-- Initialize
|
||||
do
|
||||
create object.make (10)
|
||||
end
|
||||
|
||||
feature -- Change Element
|
||||
|
||||
put (value: detachable JSON_VALUE; key: JSON_STRING)
|
||||
-- Assuming there is no item of key `key',
|
||||
-- insert `value' with `key'.
|
||||
require
|
||||
key_not_present: not has_key (key)
|
||||
local
|
||||
l_value: like value
|
||||
do
|
||||
l_value := value
|
||||
if l_value = Void then
|
||||
create {JSON_NULL} l_value
|
||||
end
|
||||
object.extend (l_value, key)
|
||||
end
|
||||
|
||||
replace (value: detachable JSON_VALUE; key: JSON_STRING)
|
||||
-- Assuming there is no item of key `key',
|
||||
-- insert `value' with `key'.
|
||||
local
|
||||
l_value: like value
|
||||
do
|
||||
l_value := value
|
||||
if l_value = Void then
|
||||
create {JSON_NULL} l_value
|
||||
end
|
||||
object.force (l_value, key)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
has_key (key: JSON_STRING): BOOLEAN
|
||||
-- has the JSON_OBJECT contains a specific key 'key'.
|
||||
do
|
||||
Result := object.has (key)
|
||||
end
|
||||
|
||||
has_item (value: JSON_VALUE): BOOLEAN
|
||||
-- has the JSON_OBJECT contain a specfic item 'value'
|
||||
do
|
||||
Result := object.has_item (value)
|
||||
end
|
||||
|
||||
item (key: JSON_STRING): detachable JSON_VALUE
|
||||
-- the json_value associated with a key.
|
||||
do
|
||||
Result := object.item (key)
|
||||
end
|
||||
|
||||
current_keys: ARRAY [JSON_STRING]
|
||||
-- array containing actually used keys
|
||||
do
|
||||
Result := object.current_keys
|
||||
end
|
||||
|
||||
representation: STRING
|
||||
local
|
||||
t: HASH_TABLE [JSON_VALUE, JSON_STRING]
|
||||
do
|
||||
Result := "{"
|
||||
from
|
||||
t := map_representation
|
||||
t.start
|
||||
until
|
||||
t.after
|
||||
loop
|
||||
Result.append (t.key_for_iteration.representation)
|
||||
Result.append (":")
|
||||
Result.append (t.item_for_iteration.representation)
|
||||
t.forth
|
||||
if not t.after then
|
||||
Result.append_character (',')
|
||||
end
|
||||
end
|
||||
Result.append_character ('}')
|
||||
end
|
||||
|
||||
feature -- Mesurement
|
||||
|
||||
count: INTEGER
|
||||
-- Number of field
|
||||
do
|
||||
Result := object.count
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
new_cursor: TABLE_ITERATION_CURSOR [JSON_VALUE, JSON_STRING]
|
||||
-- Fresh cursor associated with current structure
|
||||
do
|
||||
Result := object.new_cursor
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_empty: BOOLEAN
|
||||
-- Is empty object?
|
||||
do
|
||||
Result := object.is_empty
|
||||
end
|
||||
|
||||
feature -- Visitor pattern
|
||||
|
||||
accept (a_visitor: JSON_VISITOR)
|
||||
-- Accept `a_visitor'.
|
||||
-- (Call `visit_json_object' procedure on `a_visitor'.)
|
||||
do
|
||||
a_visitor.visit_json_object (Current)
|
||||
end
|
||||
|
||||
feature -- Conversion
|
||||
|
||||
map_representation: HASH_TABLE [JSON_VALUE, JSON_STRING]
|
||||
--A representation that maps keys to values
|
||||
do
|
||||
Result := object
|
||||
end
|
||||
|
||||
feature -- Report
|
||||
|
||||
hash_code: INTEGER
|
||||
-- Hash code value
|
||||
do
|
||||
from
|
||||
object.start
|
||||
Result := object.item_for_iteration.hash_code
|
||||
until
|
||||
object.off
|
||||
loop
|
||||
Result := ((Result \\ 8388593) |<< 8) + object.item_for_iteration.hash_code
|
||||
object.forth
|
||||
end
|
||||
-- Ensure it is a positive value.
|
||||
Result := Result.hash_code
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
Result := object.count.out
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
object: HASH_TABLE [JSON_VALUE, JSON_STRING]
|
||||
-- Value container
|
||||
|
||||
invariant
|
||||
object_not_null: object /= Void
|
||||
|
||||
end
|
||||
@@ -1,329 +0,0 @@
|
||||
note
|
||||
|
||||
description: "[
|
||||
A JSON_STRING represent a string in JSON.
|
||||
A string is a collection of zero or more Unicodes characters, wrapped in double
|
||||
quotes, using blackslash espaces.
|
||||
]"
|
||||
|
||||
author: "Javier Velilla"
|
||||
date: "2008/08/24"
|
||||
revision: "Revision 0.1"
|
||||
license:"MIT (see http://www.opensource.org/licenses/mit-license.php)"
|
||||
|
||||
|
||||
class
|
||||
JSON_STRING
|
||||
|
||||
inherit
|
||||
JSON_VALUE
|
||||
redefine
|
||||
is_equal
|
||||
end
|
||||
|
||||
create
|
||||
make_json,
|
||||
make_json_from_string_32,
|
||||
make_with_escaped_json
|
||||
|
||||
convert
|
||||
make_json ({READABLE_STRING_8, STRING_8, IMMUTABLE_STRING_8}),
|
||||
make_json_from_string_32 ({READABLE_STRING_32, STRING_32, IMMUTABLE_STRING_32})
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_json (s: READABLE_STRING_8)
|
||||
-- Initialize.
|
||||
require
|
||||
item_not_void: s /= Void
|
||||
do
|
||||
make_with_escaped_json (escaped_json_string (s))
|
||||
end
|
||||
|
||||
make_json_from_string_32 (s: READABLE_STRING_32)
|
||||
-- Initialize from STRING_32 `s'.
|
||||
require
|
||||
item_not_void: s /= Void
|
||||
do
|
||||
make_with_escaped_json (escaped_json_string_32 (s))
|
||||
end
|
||||
|
||||
make_with_escaped_json (s: READABLE_STRING_8)
|
||||
-- Initialize with an_item already escaped
|
||||
require
|
||||
item_not_void: s /= Void
|
||||
do
|
||||
item := s
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
item: STRING
|
||||
-- Contents with escaped entities if any
|
||||
|
||||
unescaped_string_8: STRING_8
|
||||
-- Unescaped string from `item'
|
||||
local
|
||||
s: like item
|
||||
i, n: INTEGER
|
||||
c: CHARACTER
|
||||
do
|
||||
s := item
|
||||
n := s.count
|
||||
create Result.make (n)
|
||||
from i := 1 until i > n loop
|
||||
c := s[i]
|
||||
if c = '\' then
|
||||
if i < n then
|
||||
inspect s[i+1]
|
||||
when '\' then
|
||||
Result.append_character ('\')
|
||||
i := i + 2
|
||||
when '%"' then
|
||||
Result.append_character ('%"')
|
||||
i := i + 2
|
||||
when 'n' then
|
||||
Result.append_character ('%N')
|
||||
i := i + 2
|
||||
when 'r' then
|
||||
Result.append_character ('%R')
|
||||
i := i + 2
|
||||
when 'u' then
|
||||
--| Leave unicode \uXXXX unescaped
|
||||
Result.append_character ('\')
|
||||
i := i + 1
|
||||
else
|
||||
Result.append_character ('\')
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
Result.append_character ('\')
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
Result.append_character (c)
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unescaped_string_32: STRING_32
|
||||
-- Unescaped string 32 from `item'
|
||||
local
|
||||
s: like item
|
||||
i, n: INTEGER
|
||||
c: CHARACTER
|
||||
hex: STRING
|
||||
do
|
||||
s := item
|
||||
n := s.count
|
||||
create Result.make (n)
|
||||
from i := 1 until i > n loop
|
||||
c := s[i]
|
||||
if c = '\' then
|
||||
if i < n then
|
||||
inspect s[i+1]
|
||||
when '\' then
|
||||
Result.append_character ('\')
|
||||
i := i + 2
|
||||
when '%"' then
|
||||
Result.append_character ('%"')
|
||||
i := i + 2
|
||||
when 'n' then
|
||||
Result.append_character ('%N')
|
||||
i := i + 2
|
||||
when 'r' then
|
||||
Result.append_character ('%R')
|
||||
i := i + 2
|
||||
when 'u' then
|
||||
hex := s.substring (i+2, i+2+4 - 1)
|
||||
if hex.count = 4 then
|
||||
Result.append_code (hexadecimal_to_natural_32 (hex))
|
||||
end
|
||||
i := i + 2 + 4
|
||||
else
|
||||
Result.append_character ('\')
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
Result.append_character ('\')
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
Result.append_character (c.to_character_32)
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
representation: STRING
|
||||
-- String representation of `item' with escaped entities if any
|
||||
do
|
||||
create Result.make (item.count + 2)
|
||||
Result.append_character ('%"')
|
||||
Result.append (item)
|
||||
Result.append_character ('%"')
|
||||
end
|
||||
|
||||
feature -- Visitor pattern
|
||||
|
||||
accept (a_visitor: JSON_VISITOR)
|
||||
-- Accept `a_visitor'.
|
||||
-- (Call `visit_json_string' procedure on `a_visitor'.)
|
||||
do
|
||||
a_visitor.visit_json_string (Current)
|
||||
end
|
||||
|
||||
feature -- Comparison
|
||||
|
||||
is_equal (other: like Current): BOOLEAN
|
||||
-- Is JSON_STRING made of same character sequence as `other'
|
||||
-- (possibly with a different capacity)?
|
||||
do
|
||||
Result := item.same_string (other.item)
|
||||
end
|
||||
|
||||
feature -- Change Element
|
||||
|
||||
append (a_string: STRING)
|
||||
-- Add a_string
|
||||
require
|
||||
a_string_not_void: a_string /= Void
|
||||
do
|
||||
item.append_string (a_string)
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
hash_code: INTEGER
|
||||
-- Hash code value
|
||||
do
|
||||
Result := item.hash_code
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
Result := item
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
is_hexadecimal (s: READABLE_STRING_8): BOOLEAN
|
||||
-- Is `s' an hexadecimal value?
|
||||
do
|
||||
Result := across s as scur all scur.item.is_hexa_digit end
|
||||
end
|
||||
|
||||
hexadecimal_to_natural_32 (s: READABLE_STRING_8): NATURAL_32
|
||||
-- Hexadecimal string `s' converted to NATURAL_32 value
|
||||
require
|
||||
s_not_void: s /= Void
|
||||
is_hexadecimal: is_hexadecimal (s)
|
||||
local
|
||||
i, nb: INTEGER
|
||||
char: CHARACTER
|
||||
do
|
||||
nb := s.count
|
||||
|
||||
if nb >= 2 and then s.item (2) = 'x' then
|
||||
i := 3
|
||||
else
|
||||
i := 1
|
||||
end
|
||||
|
||||
from
|
||||
until
|
||||
i > nb
|
||||
loop
|
||||
Result := Result * 16
|
||||
char := s.item (i)
|
||||
if char >= '0' and then char <= '9' then
|
||||
Result := Result + (char |-| '0').to_natural_32
|
||||
else
|
||||
Result := Result + (char.lower |-| 'a' + 10).to_natural_32
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
escaped_json_string (s: READABLE_STRING_8): STRING_8
|
||||
-- JSON string with '"' and '\' characters escaped
|
||||
require
|
||||
s_not_void: s /= Void
|
||||
local
|
||||
i, n: INTEGER
|
||||
c: CHARACTER_8
|
||||
do
|
||||
n := s.count
|
||||
create Result.make (n + n // 10)
|
||||
from i := 1 until i > n loop
|
||||
c := s.item (i)
|
||||
inspect c
|
||||
when '%"' then Result.append_string ("\%"")
|
||||
when '\' then Result.append_string ("\\")
|
||||
when '%R' then Result.append_string ("\r")
|
||||
when '%N' then Result.append_string ("\n")
|
||||
else
|
||||
Result.extend (c)
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
escaped_json_string_32 (s: READABLE_STRING_32): STRING_8
|
||||
-- JSON string with '"' and '\' characters and unicode escaped
|
||||
require
|
||||
s_not_void: s /= Void
|
||||
local
|
||||
i, j, n: INTEGER
|
||||
uc: CHARACTER_32
|
||||
c: CHARACTER_8
|
||||
h: STRING_8
|
||||
do
|
||||
n := s.count
|
||||
create Result.make (n + n // 10)
|
||||
from i := 1 until i > n loop
|
||||
uc := s.item (i)
|
||||
if uc.is_character_8 then
|
||||
c := uc.to_character_8
|
||||
inspect c
|
||||
when '%"' then Result.append_string ("\%"")
|
||||
when '\' then Result.append_string ("\\")
|
||||
when '%R' then Result.append_string ("\r")
|
||||
when '%N' then Result.append_string ("\n")
|
||||
else
|
||||
Result.extend (c)
|
||||
end
|
||||
else
|
||||
Result.append ("\u")
|
||||
h := uc.code.to_hex_string
|
||||
-- Remove first 0 and keep 4 hexa digit
|
||||
from
|
||||
j := 1
|
||||
until
|
||||
h.count = 4 or (j <= h.count and then h.item (j) /= '0')
|
||||
loop
|
||||
j := j + 1
|
||||
end
|
||||
h := h.substring (j, h.count)
|
||||
|
||||
from
|
||||
until
|
||||
h.count >= 4
|
||||
loop
|
||||
h.prepend_integer (0)
|
||||
end
|
||||
check h.count = 4 end
|
||||
Result.append (h)
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
invariant
|
||||
item_not_void: item /= Void
|
||||
|
||||
end
|
||||
@@ -1,43 +0,0 @@
|
||||
note
|
||||
description:"[
|
||||
JSON_VALUE represent a value in JSON.
|
||||
A value can be
|
||||
* a string in double quotes
|
||||
* a number
|
||||
* boolean value(true, false )
|
||||
* null
|
||||
* an object
|
||||
* an array
|
||||
]"
|
||||
author: "Javier Velilla"
|
||||
date: "2008/05/19"
|
||||
revision: "Revision 0.1"
|
||||
license:"MIT (see http://www.opensource.org/licenses/mit-license.php)"
|
||||
|
||||
|
||||
deferred class
|
||||
JSON_VALUE
|
||||
|
||||
inherit
|
||||
HASHABLE
|
||||
|
||||
DEBUG_OUTPUT
|
||||
|
||||
feature -- Access
|
||||
|
||||
representation: STRING
|
||||
-- UTF-8 encoded Unicode string representation of Current
|
||||
deferred
|
||||
end
|
||||
|
||||
feature -- Visitor pattern
|
||||
|
||||
accept (a_visitor: JSON_VISITOR)
|
||||
-- Accept `a_visitor'.
|
||||
-- (Call `visit_*' procedure on `a_visitor'.)
|
||||
require
|
||||
a_visitor_not_void: a_visitor /= Void
|
||||
deferred
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,513 +0,0 @@
|
||||
note
|
||||
|
||||
description: "Parse serialized JSON data"
|
||||
author: "jvelilla"
|
||||
date: "2008/08/24"
|
||||
revision: "Revision 0.1"
|
||||
|
||||
class
|
||||
JSON_PARSER
|
||||
|
||||
inherit
|
||||
JSON_READER
|
||||
JSON_TOKENS
|
||||
|
||||
create
|
||||
make_parser
|
||||
|
||||
feature {NONE} -- Initialize
|
||||
|
||||
make_parser (a_json: STRING)
|
||||
-- Initialize.
|
||||
require
|
||||
json_not_empty: a_json /= Void and then not a_json.is_empty
|
||||
do
|
||||
make (a_json)
|
||||
is_parsed := True
|
||||
create errors.make
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_parsed: BOOLEAN
|
||||
-- Is parsed?
|
||||
|
||||
errors: LINKED_LIST [STRING]
|
||||
-- Current errors
|
||||
|
||||
current_errors: STRING
|
||||
-- Current errors as string
|
||||
do
|
||||
create Result.make_empty
|
||||
from
|
||||
errors.start
|
||||
until
|
||||
errors.after
|
||||
loop
|
||||
Result.append_string (errors.item + "%N")
|
||||
errors.forth
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Element change
|
||||
|
||||
report_error (e: STRING)
|
||||
-- Report error `e'
|
||||
require
|
||||
e_not_void: e /= Void
|
||||
do
|
||||
errors.force (e)
|
||||
end
|
||||
|
||||
feature -- Commands
|
||||
|
||||
parse_json: detachable JSON_VALUE
|
||||
-- Parse JSON data `representation'
|
||||
-- start ::= object | array
|
||||
do
|
||||
if is_valid_start_symbol then
|
||||
Result := parse
|
||||
if extra_elements then
|
||||
is_parsed := False
|
||||
end
|
||||
else
|
||||
is_parsed := False
|
||||
report_error ("Syntax error unexpected token, expecting `{' or `['")
|
||||
end
|
||||
end
|
||||
|
||||
parse: detachable JSON_VALUE
|
||||
-- Parse JSON data `representation'
|
||||
local
|
||||
c: CHARACTER
|
||||
do
|
||||
if is_parsed then
|
||||
skip_white_spaces
|
||||
c := actual
|
||||
inspect c
|
||||
when j_OBJECT_OPEN then
|
||||
Result := parse_object
|
||||
when j_STRING then
|
||||
Result := parse_string
|
||||
when j_ARRAY_OPEN then
|
||||
Result := parse_array
|
||||
else
|
||||
if c.is_digit or c = j_MINUS then
|
||||
Result := parse_number
|
||||
elseif is_null then
|
||||
Result := create {JSON_NULL}
|
||||
next
|
||||
next
|
||||
next
|
||||
elseif is_true then
|
||||
Result := create {JSON_BOOLEAN}.make_boolean (True)
|
||||
next
|
||||
next
|
||||
next
|
||||
elseif is_false then
|
||||
Result := create {JSON_BOOLEAN}.make_boolean (False)
|
||||
next
|
||||
next
|
||||
next
|
||||
next
|
||||
else
|
||||
is_parsed := False
|
||||
report_error ("JSON is not well formed in parse")
|
||||
Result := Void
|
||||
end
|
||||
end
|
||||
end
|
||||
ensure
|
||||
is_parsed_implies_result_not_void: is_parsed implies Result /= Void
|
||||
end
|
||||
|
||||
parse_object: JSON_OBJECT
|
||||
-- object
|
||||
-- {}
|
||||
-- {"key" : "value" [,]}
|
||||
local
|
||||
has_more: BOOLEAN
|
||||
l_json_string: detachable JSON_STRING
|
||||
l_value: detachable JSON_VALUE
|
||||
do
|
||||
create Result.make
|
||||
-- check if is an empty object {}
|
||||
next
|
||||
skip_white_spaces
|
||||
if actual = j_OBJECT_CLOSE then
|
||||
--is an empty object
|
||||
else
|
||||
-- a complex object {"key" : "value"}
|
||||
previous
|
||||
from has_more := True until not has_more loop
|
||||
next
|
||||
skip_white_spaces
|
||||
l_json_string := parse_string
|
||||
next
|
||||
skip_white_spaces
|
||||
if actual = ':' then
|
||||
next
|
||||
skip_white_spaces
|
||||
else
|
||||
is_parsed := False
|
||||
report_error ("%N Input string is a not well formed JSON, expected: : found: " + actual.out)
|
||||
has_more := False
|
||||
end
|
||||
|
||||
l_value := parse
|
||||
if is_parsed and then (l_value /= Void and l_json_string /= Void) then
|
||||
Result.put (l_value, l_json_string)
|
||||
next
|
||||
skip_white_spaces
|
||||
if actual = j_OBJECT_CLOSE then
|
||||
has_more := False
|
||||
elseif actual /= ',' then
|
||||
has_more := False
|
||||
is_parsed := False
|
||||
report_error ("JSON Object syntactically malformed expected , found: [" + actual.out + "]")
|
||||
end
|
||||
else
|
||||
has_more := False
|
||||
-- explain the error
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
parse_string: detachable JSON_STRING
|
||||
-- Parsed string
|
||||
local
|
||||
has_more: BOOLEAN
|
||||
l_json_string: STRING
|
||||
l_unicode: STRING
|
||||
c: like actual
|
||||
do
|
||||
create l_json_string.make_empty
|
||||
if actual = j_STRING then
|
||||
from
|
||||
has_more := True
|
||||
until
|
||||
not has_more
|
||||
loop
|
||||
next
|
||||
c := actual
|
||||
if c = j_STRING then
|
||||
has_more := False
|
||||
elseif c = '%H' then
|
||||
next
|
||||
c := actual
|
||||
if c = 'u' then
|
||||
create l_unicode.make_from_string ("\u")
|
||||
l_unicode.append (read_unicode)
|
||||
c := actual
|
||||
if is_valid_unicode (l_unicode) then
|
||||
l_json_string.append (l_unicode)
|
||||
else
|
||||
has_more := False
|
||||
is_parsed := False
|
||||
report_error ("Input String is not well formed JSON, expected a Unicode value, found [" + c.out + " ]")
|
||||
end
|
||||
elseif (not is_special_character (c) and not is_special_control (c)) or c = '%N' then
|
||||
has_more := False
|
||||
is_parsed := False
|
||||
report_error ("Input String is not well formed JSON, found [" + c.out + " ]")
|
||||
else
|
||||
l_json_string.append_character ('\')
|
||||
l_json_string.append_character (c)
|
||||
end
|
||||
else
|
||||
if is_special_character (c) and c /= '/' then
|
||||
has_more := False
|
||||
is_parsed := False
|
||||
report_error ("Input String is not well formed JSON, found [" + c.out + " ]")
|
||||
else
|
||||
l_json_string.append_character (c)
|
||||
end
|
||||
end
|
||||
end
|
||||
create Result.make_with_escaped_json (l_json_string)
|
||||
else
|
||||
Result := Void
|
||||
end
|
||||
end
|
||||
|
||||
parse_array: JSON_ARRAY
|
||||
-- array
|
||||
-- []
|
||||
-- [elements [,]]
|
||||
local
|
||||
flag: BOOLEAN
|
||||
l_value: detachable JSON_VALUE
|
||||
c: like actual
|
||||
do
|
||||
create Result.make_array
|
||||
--check if is an empty array []
|
||||
next
|
||||
skip_white_spaces
|
||||
if actual = j_array_close then
|
||||
--is an empty array
|
||||
else
|
||||
previous
|
||||
from
|
||||
flag := True
|
||||
until
|
||||
not flag
|
||||
loop
|
||||
next
|
||||
skip_white_spaces
|
||||
l_value := parse
|
||||
if is_parsed and then l_value /= Void then
|
||||
Result.add (l_value)
|
||||
next
|
||||
skip_white_spaces
|
||||
c := actual
|
||||
if c = j_ARRAY_CLOSE then
|
||||
flag := False
|
||||
elseif c /= ',' then
|
||||
flag := False
|
||||
is_parsed := False
|
||||
report_error ("Array is not well formed JSON, found [" + c.out + " ]")
|
||||
end
|
||||
else
|
||||
flag := False
|
||||
report_error ("Array is not well formed JSON, found [" + actual.out + " ]")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
parse_number: detachable JSON_NUMBER
|
||||
-- Parsed number
|
||||
local
|
||||
sb: STRING
|
||||
flag: BOOLEAN
|
||||
is_integer: BOOLEAN
|
||||
c: like actual
|
||||
do
|
||||
create sb.make_empty
|
||||
sb.append_character (actual)
|
||||
|
||||
from
|
||||
flag := True
|
||||
until
|
||||
not flag
|
||||
loop
|
||||
next
|
||||
c := actual
|
||||
if not has_next or is_close_token (c)
|
||||
or c = ',' or c = '%N' or c = '%R'
|
||||
then
|
||||
flag := False
|
||||
previous
|
||||
else
|
||||
sb.append_character (c)
|
||||
end
|
||||
end
|
||||
|
||||
if is_valid_number (sb) then
|
||||
if sb.is_integer then
|
||||
create Result.make_integer (sb.to_integer)
|
||||
is_integer := True
|
||||
elseif sb.is_double and not is_integer then
|
||||
create Result.make_real (sb.to_double)
|
||||
end
|
||||
else
|
||||
is_parsed := False
|
||||
report_error ("Expected a number, found: [ " + sb + " ]")
|
||||
end
|
||||
end
|
||||
|
||||
is_null: BOOLEAN
|
||||
-- Word at index represents null?
|
||||
local
|
||||
l_null: STRING
|
||||
l_string: STRING
|
||||
do
|
||||
l_null := null_id
|
||||
l_string := json_substring (index,index + l_null.count - 1)
|
||||
if l_string.is_equal (l_null) then
|
||||
Result := True
|
||||
end
|
||||
end
|
||||
|
||||
is_false: BOOLEAN
|
||||
-- Word at index represents false?
|
||||
local
|
||||
l_false: STRING
|
||||
l_string: STRING
|
||||
do
|
||||
l_false := false_id
|
||||
l_string := json_substring (index, index + l_false.count - 1)
|
||||
if l_string.is_equal (l_false) then
|
||||
Result := True
|
||||
end
|
||||
end
|
||||
|
||||
is_true: BOOLEAN
|
||||
-- Word at index represents true?
|
||||
local
|
||||
l_true: STRING
|
||||
l_string: STRING
|
||||
do
|
||||
l_true := true_id
|
||||
l_string := json_substring (index,index + l_true.count - 1)
|
||||
if l_string.is_equal (l_true) then
|
||||
Result := True
|
||||
end
|
||||
end
|
||||
|
||||
read_unicode: STRING
|
||||
-- Read unicode and return value
|
||||
local
|
||||
i: INTEGER
|
||||
do
|
||||
create Result.make_empty
|
||||
from
|
||||
i := 1
|
||||
until
|
||||
i > 4 or not has_next
|
||||
loop
|
||||
next
|
||||
Result.append_character (actual)
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
is_valid_number (a_number: STRING): BOOLEAN
|
||||
-- is 'a_number' a valid number based on this regular expression
|
||||
-- "-?(?: 0|[1-9]\d+)(?: \.\d+)?(?: [eE][+-]?\d+)?\b"?
|
||||
local
|
||||
s: detachable STRING
|
||||
c: CHARACTER
|
||||
i,n: INTEGER
|
||||
do
|
||||
create s.make_empty
|
||||
n := a_number.count
|
||||
if n = 0 then
|
||||
Result := False
|
||||
else
|
||||
Result := True
|
||||
i := 1
|
||||
--| "-?"
|
||||
c := a_number[i]
|
||||
if c = '-' then
|
||||
s.extend (c); i := i + 1; c := a_number[i]
|
||||
end
|
||||
--| "0|[1-9]\d*
|
||||
if c.is_digit then
|
||||
if c = '0' then
|
||||
--| "0"
|
||||
s.extend (c); i := i + 1; c := a_number[i]
|
||||
else
|
||||
--| "[1-9]"
|
||||
s.extend (c); i := i + 1; c := a_number[i]
|
||||
--| "\d*"
|
||||
from until i > n or not c.is_digit loop
|
||||
s.extend (c); i := i + 1; c := a_number[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if Result then
|
||||
--| "(\.\d+)?"
|
||||
if c = '.' then
|
||||
--| "\.\d+" = "\.\d\d*"
|
||||
s.extend (c); i := i + 1; c := a_number[i]
|
||||
if c.is_digit then
|
||||
from until i > n or not c.is_digit loop
|
||||
s.extend (c); i := i + 1; c := a_number[i]
|
||||
end
|
||||
else
|
||||
Result := False --| expecting digit
|
||||
end
|
||||
end
|
||||
end
|
||||
if Result then --| "(?:[eE][+-]?\d+)?\b"
|
||||
if c = 'e' or c = 'E' then
|
||||
--| "[eE][+-]?\d+"
|
||||
s.extend (c); i := i + 1; c := a_number[i]
|
||||
if c = '+' or c = '-' then
|
||||
s.extend (c); i := i + 1; c := a_number[i]
|
||||
end
|
||||
if c.is_digit then
|
||||
from until i > n or not c.is_digit loop
|
||||
s.extend (c); i := i + 1; c := a_number[i]
|
||||
end
|
||||
else
|
||||
Result := False --| expecting digit
|
||||
end
|
||||
end
|
||||
end
|
||||
if Result then --| "\b"
|
||||
from until i > n or not c.is_space loop
|
||||
s.extend (c); i := i + 1; c := a_number[i]
|
||||
end
|
||||
Result := i > n and then s.same_string (a_number)
|
||||
end
|
||||
end
|
||||
|
||||
is_valid_unicode (a_unicode: STRING): BOOLEAN
|
||||
-- is 'a_unicode' a valid unicode based on this regular expression
|
||||
-- "\\u[0-9a-fA-F]{4}"
|
||||
local
|
||||
i: INTEGER
|
||||
do
|
||||
if
|
||||
a_unicode.count = 6 and then
|
||||
a_unicode[1] = '\' and then
|
||||
a_unicode[2] = 'u'
|
||||
then
|
||||
from
|
||||
Result := True
|
||||
i := 3
|
||||
until
|
||||
i > 6 or Result = False
|
||||
loop
|
||||
inspect a_unicode[i]
|
||||
when '0'..'9', 'a'..'f', 'A'..'F' then
|
||||
else
|
||||
Result := False
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
extra_elements: BOOLEAN
|
||||
-- has more elements?
|
||||
local
|
||||
c: like actual
|
||||
do
|
||||
if has_next then
|
||||
next
|
||||
end
|
||||
from
|
||||
c := actual
|
||||
until
|
||||
c /= ' ' or c /= '%R' or c /= '%U' or c /= '%T' or c /= '%N' or not has_next
|
||||
loop
|
||||
next
|
||||
end
|
||||
Result := has_next
|
||||
end
|
||||
|
||||
is_valid_start_symbol : BOOLEAN
|
||||
-- expecting `{' or `[' as start symbol
|
||||
do
|
||||
if attached representation as s and then s.count > 0 then
|
||||
Result := s[1] = '{' or s[1] = '['
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Constants
|
||||
|
||||
false_id: STRING = "false"
|
||||
|
||||
true_id: STRING = "true"
|
||||
|
||||
null_id: STRING = "null"
|
||||
|
||||
|
||||
end
|
||||
@@ -1,118 +0,0 @@
|
||||
note
|
||||
description: "Objects that ..."
|
||||
author: "jvelilla"
|
||||
date: "2008/08/24"
|
||||
revision: "0.1"
|
||||
|
||||
class
|
||||
JSON_READER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_json: STRING)
|
||||
-- Initialize Reader
|
||||
do
|
||||
set_representation (a_json)
|
||||
end
|
||||
|
||||
feature -- Commands
|
||||
|
||||
set_representation (a_json: STRING)
|
||||
-- Set `representation'.
|
||||
do
|
||||
a_json.left_adjust
|
||||
a_json.right_adjust
|
||||
representation := a_json
|
||||
index := 1
|
||||
end
|
||||
|
||||
read: CHARACTER
|
||||
-- Read character
|
||||
do
|
||||
if not representation.is_empty then
|
||||
Result := representation.item (index)
|
||||
end
|
||||
end
|
||||
|
||||
next
|
||||
-- Move to next index
|
||||
require
|
||||
has_more_elements: has_next
|
||||
do
|
||||
index := index + 1
|
||||
ensure
|
||||
incremented: old index + 1 = index
|
||||
end
|
||||
|
||||
previous
|
||||
-- Move to previous index
|
||||
require
|
||||
not_is_first: has_previous
|
||||
do
|
||||
index := index - 1
|
||||
ensure
|
||||
incremented: old index - 1 = index
|
||||
end
|
||||
|
||||
skip_white_spaces
|
||||
-- Remove white spaces
|
||||
local
|
||||
c: like actual
|
||||
do
|
||||
from
|
||||
c := actual
|
||||
until
|
||||
(c /= ' ' and c /= '%N' and c /= '%R' and c /= '%U' and c /= '%T' ) or not has_next
|
||||
loop
|
||||
next
|
||||
c := actual
|
||||
end
|
||||
end
|
||||
|
||||
json_substring (start_index, end_index: INTEGER_32): STRING
|
||||
-- JSON representation between `start_index' and `end_index'
|
||||
do
|
||||
Result := representation.substring (start_index, end_index)
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
has_next: BOOLEAN
|
||||
-- Has a next character?
|
||||
do
|
||||
Result := index <= representation.count
|
||||
end
|
||||
|
||||
has_previous: BOOLEAN
|
||||
-- Has a previous character?
|
||||
do
|
||||
Result := index >= 1
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
representation: STRING
|
||||
-- Serialized representation of the original JSON string
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
actual: CHARACTER
|
||||
-- Current character or '%U' if none
|
||||
do
|
||||
if index > representation.count then
|
||||
Result := '%U'
|
||||
else
|
||||
Result := representation.item (index)
|
||||
end
|
||||
end
|
||||
|
||||
index: INTEGER
|
||||
-- Actual index
|
||||
|
||||
invariant
|
||||
representation_not_void: representation /= Void
|
||||
|
||||
end
|
||||
@@ -1,77 +0,0 @@
|
||||
note
|
||||
description: ""
|
||||
author: "jvelilla"
|
||||
date: "2008/08/24"
|
||||
revision: "0.1"
|
||||
|
||||
class
|
||||
JSON_TOKENS
|
||||
|
||||
feature -- Access
|
||||
|
||||
j_OBJECT_OPEN: CHARACTER = '{'
|
||||
j_ARRAY_OPEN: CHARACTER = '['
|
||||
j_OBJECT_CLOSE: CHARACTER = '}'
|
||||
j_ARRAY_CLOSE: CHARACTER = ']'
|
||||
|
||||
j_STRING: CHARACTER = '"'
|
||||
j_PLUS: CHARACTER = '+'
|
||||
j_MINUS: CHARACTER = '-'
|
||||
j_DOT: CHARACTER = '.'
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_open_token (c: CHARACTER): BOOLEAN
|
||||
-- Characters which open a type
|
||||
do
|
||||
inspect c
|
||||
when j_OBJECT_OPEN, j_ARRAY_OPEN, j_STRING, j_PLUS, j_MINUS, j_DOT then
|
||||
Result := True
|
||||
else
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
is_close_token (c: CHARACTER): BOOLEAN
|
||||
-- Characters which close a type
|
||||
do
|
||||
inspect c
|
||||
when j_OBJECT_CLOSE, j_ARRAY_CLOSE, j_STRING then
|
||||
Result := True
|
||||
else
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
is_special_character (c: CHARACTER): BOOLEAN
|
||||
-- Control Characters
|
||||
-- %F Form feed
|
||||
-- %H backslasH
|
||||
-- %N Newline
|
||||
-- %R carriage Return
|
||||
-- %T horizontal Tab
|
||||
-- %B Backspace
|
||||
-- / Solidus
|
||||
-- " Quotation
|
||||
do
|
||||
inspect c
|
||||
when '%F', '%H', '%N', '%R', '%T', '%B', '/', '"' then
|
||||
Result := True
|
||||
else
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
is_special_control (c: CHARACTER): BOOLEAN
|
||||
--Control Characters
|
||||
-- \b\f\n\r\t
|
||||
do
|
||||
inspect c
|
||||
when 'b', 'f', 'n', 'r', 't' then
|
||||
Result := True
|
||||
else
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,38 +0,0 @@
|
||||
note
|
||||
description: "[
|
||||
Shared factory class for creating JSON objects. Maps JSON
|
||||
objects to ELKS HASH_TABLEs and JSON arrays to ELKS
|
||||
LINKED_LISTs. Use non-conforming inheritance from this
|
||||
class to ensure that your classes share the same
|
||||
JSON_FACTORY instance.
|
||||
]"
|
||||
author: "Paul Cohen"
|
||||
date: "$Date$"
|
||||
revision: "$Revision: 89185 $"
|
||||
file: "$HeadURL: $"
|
||||
|
||||
class SHARED_EJSON
|
||||
|
||||
feature
|
||||
|
||||
json: EJSON
|
||||
-- A shared EJSON instance with default converters for
|
||||
--LINKED_LIST [ANY] and HASH_TABLE [ANY, HASHABLE]
|
||||
local
|
||||
jalc: JSON_ARRAYED_LIST_CONVERTER
|
||||
jllc: JSON_LINKED_LIST_CONVERTER
|
||||
jhtc: JSON_HASH_TABLE_CONVERTER
|
||||
once
|
||||
create Result
|
||||
|
||||
create jalc.make
|
||||
Result.add_converter (jalc)
|
||||
|
||||
create jllc.make
|
||||
Result.add_converter (jllc)
|
||||
|
||||
create jhtc.make
|
||||
Result.add_converter (jhtc)
|
||||
end
|
||||
|
||||
end -- class SHARED_EJSON
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="http" uuid="F8BE3C55-88E8-4103-A936-B1E5CB1D330E" library_target="http">
|
||||
<target name="http">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||
<cluster name="src" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="http" uuid="F8BE3C55-88E8-4103-A936-B1E5CB1D330E" library_target="http">
|
||||
<target name="http">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||
<cluster name="src" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
${NOTE_KEYWORD}
|
||||
copyright: "2011-${YEAR}, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
@@ -0,0 +1,98 @@
|
||||
note
|
||||
description: "Summary description for {HTTP_CONSTANTS}."
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_CONSTANTS
|
||||
|
||||
feature -- Ports
|
||||
|
||||
default_http_port: INTEGER = 80
|
||||
default_https_port: INTEGER = 443
|
||||
|
||||
feature -- Method
|
||||
|
||||
method_get: STRING = "GET"
|
||||
method_post: STRING = "POST"
|
||||
method_put: STRING = "PUT"
|
||||
method_delete: STRING = "DELETE"
|
||||
method_head: STRING = "HEAD"
|
||||
method_download: STRING = "DOWNLOAD"
|
||||
|
||||
feature -- Content type
|
||||
|
||||
octet_stream: STRING = "application/octet-stream"
|
||||
-- Octet stream content-type header
|
||||
multipart_form: STRING = "multipart/form-data"
|
||||
-- Starting chars of multipart form data content-type header
|
||||
form_encoded: STRING = "application/x-www-form-urlencoded"
|
||||
-- Starting chars of form url-encoded data content-type header
|
||||
xml_text: STRING = "text/xml"
|
||||
-- XML text content-type header
|
||||
html_text: STRING = "text/html"
|
||||
-- HTML text content-type header
|
||||
json_text: STRING = "text/json"
|
||||
-- JSON text content-type header
|
||||
json_app: STRING = "application/json"
|
||||
-- JSON application content-type header
|
||||
js_text: STRING = "text/javascript"
|
||||
-- Javascript text content-type header
|
||||
js_app: STRING = "application/javascript"
|
||||
-- JavaScript application content-type header
|
||||
plain_text: STRING = "text/plain"
|
||||
-- Plain text content-type header
|
||||
|
||||
feature -- Server
|
||||
|
||||
http_version_1_0: STRING = "HTTP/1.0"
|
||||
http_version_1_1: STRING = "HTTP/1.1"
|
||||
http_host_header: STRING = "Host"
|
||||
http_authorization_header: STRING = "Authorization: "
|
||||
http_end_of_header_line: STRING = "%R%N"
|
||||
http_end_of_command: STRING = "%R%N%R%N"
|
||||
http_content_length: STRING = "Content-Length: "
|
||||
http_content_type: STRING = "Content-Type: "
|
||||
http_content_location: STRING = "Content-Location: "
|
||||
http_content_disposition: STRING = "Content-Disposition: "
|
||||
http_path_translated: STRING = "Path-Translated: "
|
||||
http_agent: STRING = "User-agent: "
|
||||
http_from: STRING = "From: "
|
||||
|
||||
feature -- Server: header
|
||||
|
||||
header_host: STRING = "Host"
|
||||
header_authorization: STRING = "Authorization"
|
||||
header_content_length: STRING = "Content-Length"
|
||||
header_content_type: STRING = "Content-Type"
|
||||
header_content_location: STRING = "Content-Location"
|
||||
header_content_disposition: STRING = "Content-Disposition"
|
||||
header_cache_control: STRING = "Cache-Control"
|
||||
header_path_translated: STRING = "Path-Translated"
|
||||
header_agent: STRING = "User-Agent"
|
||||
header_referer: STRING = "Referer" -- Officially mispelled in std
|
||||
header_location: STRING = "Location"
|
||||
header_from: STRING = "From"
|
||||
header_status: STRING = "Status"
|
||||
header_multipart_tag_value_separator: CHARACTER = ';'
|
||||
|
||||
feature -- Misc
|
||||
|
||||
http_status_ok: STRING = "200 OK"
|
||||
|
||||
default_bufsize: INTEGER = 16384 --| 16K
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,71 @@
|
||||
note
|
||||
description: "Summary description for {HTTP_DATE_TIME_UTILITIES}."
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_DATE_TIME_UTILITIES
|
||||
|
||||
feature -- Access
|
||||
|
||||
now_utc: DATE_TIME
|
||||
do
|
||||
create Result.make_now_utc
|
||||
end
|
||||
|
||||
epoch: DATE_TIME
|
||||
once ("THREAD")
|
||||
create Result.make_from_epoch (0)
|
||||
end
|
||||
|
||||
feature -- Unix time stamp
|
||||
|
||||
unix_time_stamp (dt: detachable DATE_TIME): INTEGER_64
|
||||
-- Unix time stamp from `dt' if attached or from epoch is detached
|
||||
local
|
||||
l_date_time: DATE_TIME
|
||||
do
|
||||
if dt /= Void then
|
||||
l_date_time := dt
|
||||
else
|
||||
l_date_time := now_utc
|
||||
end
|
||||
Result := l_date_time.definite_duration (epoch).seconds_count
|
||||
end
|
||||
|
||||
fine_unix_time_stamp (dt: detachable DATE_TIME): DOUBLE
|
||||
-- Fine unix time stamp from `dt' if attached or from epoch is detached
|
||||
local
|
||||
l_date_time: DATE_TIME
|
||||
do
|
||||
if dt /= Void then
|
||||
l_date_time := dt
|
||||
else
|
||||
l_date_time := now_utc
|
||||
end
|
||||
Result := l_date_time.definite_duration (epoch).fine_seconds_count
|
||||
end
|
||||
|
||||
feature -- Unix time stamp conversion
|
||||
|
||||
unix_time_stamp_to_date_time (i64: INTEGER_64): DATE_TIME
|
||||
-- Date time related to `i64'
|
||||
do
|
||||
create Result.make_from_epoch (i64.as_integer_32)
|
||||
ensure
|
||||
same_unix_time_stamp: unix_time_stamp (Result) = i64
|
||||
end
|
||||
|
||||
;note
|
||||
copyright: "Copyright (c) 1984-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,86 @@
|
||||
note
|
||||
description: "Summary description for {HTTP_FORMAT_CONSTANTS}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_FORMAT_CONSTANTS
|
||||
|
||||
feature -- Id
|
||||
|
||||
json: INTEGER = 0x1
|
||||
|
||||
xml: INTEGER = 0x2
|
||||
|
||||
text: INTEGER = 0x4
|
||||
|
||||
html: INTEGER = 0x8
|
||||
|
||||
rss: INTEGER = 0x10
|
||||
|
||||
atom: INTEGER = 0x20
|
||||
|
||||
feature -- Name
|
||||
|
||||
json_name: STRING = "json"
|
||||
|
||||
xml_name: STRING = "xml"
|
||||
|
||||
text_name: STRING = "text"
|
||||
|
||||
html_name: STRING = "html"
|
||||
|
||||
rss_name: STRING = "rss"
|
||||
|
||||
atom_name: STRING = "atom"
|
||||
|
||||
empty_name: STRING = ""
|
||||
|
||||
feature -- Query
|
||||
|
||||
format_id (a_id: STRING): INTEGER
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
s := a_id.as_lower
|
||||
if s.same_string (json_name) then
|
||||
Result := json
|
||||
elseif s.same_string (xml_name) then
|
||||
Result := xml
|
||||
elseif s.same_string (text_name) then
|
||||
Result := text
|
||||
elseif s.same_string (html_name) then
|
||||
Result := html
|
||||
elseif s.same_string (rss_name) then
|
||||
Result := rss
|
||||
elseif s.same_string (atom_name) then
|
||||
Result := atom
|
||||
end
|
||||
end
|
||||
|
||||
format_name (a_id: INTEGER): STRING
|
||||
do
|
||||
inspect a_id
|
||||
when json then Result := json_name
|
||||
when xml then Result := xml_name
|
||||
when text then Result := text_name
|
||||
when html then Result := html_name
|
||||
when rss then Result := rss_name
|
||||
when atom then Result := atom_name
|
||||
else Result := empty_name
|
||||
end
|
||||
ensure
|
||||
result_is_lower_case: Result /= Void and then Result.as_lower ~ Result
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "Copyright (c) 1984-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,79 @@
|
||||
note
|
||||
description: "Summary description for {HTTP_REQUEST_METHOD_CONSTANTS}."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_REQUEST_METHOD_CONSTANTS
|
||||
|
||||
feature -- Id
|
||||
|
||||
method_get: INTEGER = 0x1
|
||||
|
||||
method_post: INTEGER = 0x2
|
||||
|
||||
method_put: INTEGER = 0x4
|
||||
|
||||
method_delete: INTEGER = 0x8
|
||||
|
||||
method_head: INTEGER = 0x10
|
||||
|
||||
feature -- Name
|
||||
|
||||
method_get_name: STRING = "GET"
|
||||
|
||||
method_post_name: STRING = "POST"
|
||||
|
||||
method_put_name: STRING = "PUT"
|
||||
|
||||
method_delete_name: STRING = "DELETE"
|
||||
|
||||
method_head_name: STRING = "HEAD"
|
||||
|
||||
method_empty_name: STRING = ""
|
||||
|
||||
feature -- Query
|
||||
|
||||
method_id (a_id: STRING): INTEGER
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
s := a_id.as_lower
|
||||
if s.same_string (method_get_name) then
|
||||
Result := method_get
|
||||
elseif s.same_string (method_post_name) then
|
||||
Result := method_post
|
||||
elseif s.same_string (method_put_name) then
|
||||
Result := method_put
|
||||
elseif s.same_string (method_delete_name) then
|
||||
Result := method_delete
|
||||
elseif s.same_string (method_head_name) then
|
||||
Result := method_head
|
||||
end
|
||||
end
|
||||
|
||||
method_name (a_id: INTEGER): STRING
|
||||
do
|
||||
inspect a_id
|
||||
when method_get then Result := method_get_name
|
||||
when method_post then Result := method_post_name
|
||||
when method_put then Result := method_put_name
|
||||
when method_delete then Result := method_delete_name
|
||||
when method_head then Result := method_head_name
|
||||
else Result := method_empty_name
|
||||
end
|
||||
ensure
|
||||
result_is_upper_case: Result /= Void and then Result.as_upper ~ Result
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "Copyright (c) 1984-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,106 @@
|
||||
note
|
||||
description: "[
|
||||
Status code constants pertaining to the HTTP protocol
|
||||
See http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
|
||||
]"
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_STATUS_CODE
|
||||
|
||||
feature -- 1xx : Informational
|
||||
|
||||
continue: INTEGER = 100
|
||||
switching_protocols: INTEGER = 101
|
||||
processing: INTEGER = 102 -- WebDAV RFC 2518
|
||||
ie7_request_uri_too_long: INTEGER = 122 -- non standard, IE7 only
|
||||
|
||||
feature -- 2xx : Success
|
||||
|
||||
ok: INTEGER = 200
|
||||
created: INTEGER = 201
|
||||
accepted: INTEGER = 202
|
||||
nonauthoritative_info: INTEGER = 203
|
||||
no_content: INTEGER = 204
|
||||
reset_content: INTEGER = 205
|
||||
partial_content: INTEGER = 206
|
||||
multistatus: INTEGER = 207 -- WebDAV RFC 4918
|
||||
im_used: INTEGER = 226 -- RFC 4918
|
||||
|
||||
feature -- 3xx : Redirection
|
||||
|
||||
multiple_choices: INTEGER = 300
|
||||
moved_permanently: INTEGER = 301
|
||||
found: INTEGER = 302
|
||||
see_other: INTEGER = 303
|
||||
not_modified: INTEGER = 304
|
||||
use_proxy: INTEGER = 305
|
||||
switch_proxy: INTEGER = 306
|
||||
temp_redirect: INTEGER = 307
|
||||
|
||||
feature -- 4xx : Client Error
|
||||
|
||||
bad_request: INTEGER = 400
|
||||
unauthorized: INTEGER = 401
|
||||
payment_required: INTEGER = 402
|
||||
forbidden: INTEGER = 403
|
||||
not_found: INTEGER = 404
|
||||
method_not_allowed: INTEGER = 405
|
||||
not_acceptable: INTEGER = 406
|
||||
proxy_auth_required: INTEGER = 407
|
||||
request_timeout: INTEGER = 408
|
||||
conflict: INTEGER = 409
|
||||
gone: INTEGER = 410
|
||||
length_required: INTEGER = 411
|
||||
precondition_failed: INTEGER = 412
|
||||
request_entity_too_large: INTEGER = 413
|
||||
request_uri_too_long: INTEGER = 414
|
||||
unsupported_media_type: INTEGER = 415
|
||||
request_range_not_satisfiable: INTEGER = 416
|
||||
expectation_failed: INTEGER = 417
|
||||
teapot: INTEGER = 418
|
||||
|
||||
feature -- 4xx : Client Error : WebDAV errors
|
||||
|
||||
too_many_connections: INTEGER = 421
|
||||
unprocessable_entity: INTEGER = 422
|
||||
locked: INTEGER = 423
|
||||
failed_dependency: INTEGER = 424
|
||||
unordered_collection: INTEGER = 425
|
||||
|
||||
upgrade_required: INTEGER = 426
|
||||
no_response: INTEGER = 444
|
||||
retry_with: INTEGER = 449
|
||||
blocked_parental: INTEGER = 450
|
||||
client_closed_request: INTEGER = 499
|
||||
|
||||
feature -- 5xx : Server Error
|
||||
|
||||
internal_server_error: INTEGER = 500
|
||||
not_implemented: INTEGER = 501
|
||||
bad_gateway: INTEGER = 502
|
||||
service_unavailable: INTEGER = 503
|
||||
gateway_timeout: INTEGER = 504
|
||||
http_version_not_supported: INTEGER = 505
|
||||
variant_also_negotiates: INTEGER = 506
|
||||
insufficient_storage: INTEGER = 507 -- WebDAV RFC 4918
|
||||
|
||||
bandwidth_limit_exceeded: INTEGER = 509
|
||||
not_extended: INTEGER = 510
|
||||
|
||||
user_access_denied: INTEGER = 530
|
||||
|
||||
note
|
||||
copyright: "Copyright (c) 1984-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,160 @@
|
||||
note
|
||||
description: "[
|
||||
Status code constants pertaining to the HTTP protocol
|
||||
See http://en.wikipedia.org/wiki/List_of_HTTP_status_codes
|
||||
]"
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
HTTP_STATUS_CODE_MESSAGES
|
||||
|
||||
inherit
|
||||
HTTP_STATUS_CODE
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_valid_http_status_code (v: INTEGER): BOOLEAN
|
||||
-- Is the given value a valid http status code?
|
||||
do
|
||||
Result := v >= continue and v <= user_access_denied
|
||||
end
|
||||
|
||||
feature -- Status messages
|
||||
|
||||
http_status_code_message (a_code: INTEGER): detachable STRING
|
||||
-- Header message related to HTTP status code `a_code'
|
||||
do
|
||||
inspect a_code
|
||||
when continue then
|
||||
Result := "Continue"
|
||||
when switching_protocols then
|
||||
Result := "Switching Protocols"
|
||||
when processing then
|
||||
Result := "Processing"
|
||||
when ok then
|
||||
Result := "OK"
|
||||
when created then
|
||||
Result := "Created"
|
||||
when accepted then
|
||||
Result := "Accepted"
|
||||
when nonauthoritative_info then
|
||||
Result := "Non-Authoritative Information"
|
||||
when no_content then
|
||||
Result := "No Content"
|
||||
when reset_content then
|
||||
Result := "Reset Content"
|
||||
when partial_content then
|
||||
Result := "Partial Content"
|
||||
when multistatus then
|
||||
Result := "Multi-Status"
|
||||
when multiple_choices then
|
||||
Result := "Multiple Choices"
|
||||
when moved_permanently then
|
||||
Result := "Moved Permanently"
|
||||
when found then
|
||||
Result := "Found"
|
||||
when see_other then
|
||||
Result := "See Other"
|
||||
when not_modified then
|
||||
Result := "Not Modified"
|
||||
when use_proxy then
|
||||
Result := "Use Proxy"
|
||||
when switch_proxy then
|
||||
Result := "Switch Proxy"
|
||||
when temp_redirect then
|
||||
Result := "Temporary Redirect"
|
||||
when bad_request then
|
||||
Result := "Bad Request"
|
||||
when unauthorized then
|
||||
Result := "Unauthorized"
|
||||
when payment_required then
|
||||
Result := "Payment Required"
|
||||
when forbidden then
|
||||
Result := "Forbidden"
|
||||
when not_found then
|
||||
Result := "Not Found"
|
||||
when method_not_allowed then
|
||||
Result := "Method Not Allowed"
|
||||
when not_acceptable then
|
||||
Result := "Not Acceptable"
|
||||
when proxy_auth_required then
|
||||
Result := "Proxy Authentication Required"
|
||||
when request_timeout then
|
||||
Result := "Request Timeout"
|
||||
when conflict then
|
||||
Result := "Conflict"
|
||||
when gone then
|
||||
Result := "Gone"
|
||||
when length_required then
|
||||
Result := "Length Required"
|
||||
when precondition_failed then
|
||||
Result := "Precondition Failed"
|
||||
when request_entity_too_large then
|
||||
Result := "Request Entity Too Large"
|
||||
when request_uri_too_long then
|
||||
Result := "Request-URI Too Long"
|
||||
when unsupported_media_type then
|
||||
Result := "Unsupported Media Type"
|
||||
when request_range_not_satisfiable then
|
||||
Result := "Requested Range Not Satisfiable"
|
||||
when expectation_failed then
|
||||
Result := "Expectation Failed"
|
||||
when teapot then
|
||||
Result := "I'm a teapot"
|
||||
when too_many_connections then
|
||||
Result := "There are too many connections from your internet address"
|
||||
when unprocessable_entity then
|
||||
Result := "Unprocessable Entity"
|
||||
when locked then
|
||||
Result := "Locked"
|
||||
when failed_dependency then
|
||||
Result := "Failed Dependency"
|
||||
when unordered_collection then
|
||||
Result := "Unordered Collection"
|
||||
when upgrade_required then
|
||||
Result := "Upgrade Required"
|
||||
when retry_with then
|
||||
Result := "Retry With"
|
||||
when blocked_parental then
|
||||
Result := "Blocked by Windows Parental Controls"
|
||||
when internal_server_error then
|
||||
Result := "Internal Server Error"
|
||||
when not_implemented then
|
||||
Result := "Not Implemented"
|
||||
when bad_gateway then
|
||||
Result := "Bad Gateway"
|
||||
when service_unavailable then
|
||||
Result := "Service Unavailable"
|
||||
when gateway_timeout then
|
||||
Result := "Gateway Timeout"
|
||||
when http_version_not_supported then
|
||||
Result := "HTTP Version Not Supported"
|
||||
when variant_also_negotiates then
|
||||
Result := "Variant Also Negotiates"
|
||||
when insufficient_storage then
|
||||
Result := "Insufficient Storage"
|
||||
when bandwidth_limit_exceeded then
|
||||
Result := "Bandwidth Limit Exceeded"
|
||||
when not_extended then
|
||||
Result := "Not Extended"
|
||||
when user_access_denied then
|
||||
Result := "User access denied"
|
||||
else
|
||||
Result := Void
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "Copyright (c) 1984-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,4 @@
|
||||
Most recent spec/draft/in-progress http://code.google.com/p/uri-templates/source/browse/trunk/spec/draft-gregorio-uritemplate.xml
|
||||
|
||||
svn cat http://uri-templates.googlecode.com/svn/trunk/spec/draft-gregorio-uritemplate.xml > uritemplate-draft-gregorio-trunk.xml
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,10 @@
|
||||
${NOTE_KEYWORD}
|
||||
copyright: "2011-${YEAR}, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
@@ -0,0 +1,52 @@
|
||||
note
|
||||
description: "[
|
||||
Summary description for {URI_TEMPLATE_CONSTANTS}.
|
||||
|
||||
see http://tools.ietf.org/html/draft-gregorio-uritemplate-05
|
||||
]"
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
URI_TEMPLATE_CONSTANTS
|
||||
|
||||
feature -- Operator
|
||||
|
||||
Reserved_operator: CHARACTER = '+'
|
||||
|
||||
Form_style_query_operator: CHARACTER = '?'
|
||||
|
||||
Path_style_parameters_operator: CHARACTER = ';'
|
||||
|
||||
Path_segment_operator: CHARACTER = '/'
|
||||
|
||||
Label_operator: CHARACTER = '.'
|
||||
|
||||
feature -- Separator
|
||||
|
||||
Default_delimiter: CHARACTER = '|' --| Draft 05
|
||||
|
||||
feature -- Explode
|
||||
|
||||
Explode_plus: CHARACTER = '+'
|
||||
|
||||
Explode_star: CHARACTER = '*'
|
||||
|
||||
feature -- Modified
|
||||
|
||||
Modifier_substring: CHARACTER = ':'
|
||||
|
||||
Modifier_remainder: CHARACTER = '^'
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,463 @@
|
||||
note
|
||||
description: "[
|
||||
Summary description for {URI_TEMPLATE}.
|
||||
|
||||
See http://tools.ietf.org/html/draft-gregorio-uritemplate-04
|
||||
|
||||
note for draft 05, check {URI_TEMPLATE_CONSTANTS}.Default_delimiter
|
||||
|
||||
]"
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
URI_TEMPLATE
|
||||
|
||||
inherit
|
||||
HASHABLE
|
||||
|
||||
DEBUG_OUTPUT
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (s: STRING)
|
||||
do
|
||||
template := s
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
template: STRING
|
||||
-- URI string representation
|
||||
|
||||
feature -- Status report
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
create Result.make_from_string (template)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
hash_code: INTEGER
|
||||
-- Hash code value
|
||||
do
|
||||
Result := template.hash_code
|
||||
end
|
||||
|
||||
feature -- Structures
|
||||
|
||||
variable_names: LIST [STRING]
|
||||
-- All variable names
|
||||
do
|
||||
analyze
|
||||
if attached expressions as l_expressions then
|
||||
create {ARRAYED_LIST [STRING]} Result.make (l_expressions.count)
|
||||
from
|
||||
l_expressions.start
|
||||
until
|
||||
l_expressions.after
|
||||
loop
|
||||
Result.append (l_expressions.item.variable_names)
|
||||
l_expressions.forth
|
||||
end
|
||||
else
|
||||
create {ARRAYED_LIST [STRING]} Result.make (0)
|
||||
end
|
||||
end
|
||||
|
||||
path_variable_names: LIST [STRING]
|
||||
-- All variable names part of the path
|
||||
do
|
||||
analyze
|
||||
if attached expressions as l_expressions then
|
||||
create {ARRAYED_LIST [STRING]} Result.make (l_expressions.count)
|
||||
from
|
||||
l_expressions.start
|
||||
until
|
||||
l_expressions.after
|
||||
loop
|
||||
if not l_expressions.item.is_query then
|
||||
Result.append (l_expressions.item.variable_names)
|
||||
end
|
||||
l_expressions.forth
|
||||
end
|
||||
else
|
||||
create {ARRAYED_LIST [STRING]} Result.make (0)
|
||||
end
|
||||
end
|
||||
|
||||
query_variable_names: LIST [STRING]
|
||||
-- All variable names part of the query (i.e after '?')
|
||||
do
|
||||
analyze
|
||||
if attached expressions as l_expressions then
|
||||
create {ARRAYED_LIST [STRING]} Result.make (l_expressions.count)
|
||||
from
|
||||
l_expressions.start
|
||||
until
|
||||
l_expressions.after
|
||||
loop
|
||||
if l_expressions.item.is_query then
|
||||
Result.append (l_expressions.item.variable_names)
|
||||
end
|
||||
l_expressions.forth
|
||||
end
|
||||
else
|
||||
create {ARRAYED_LIST [STRING]} Result.make (0)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Builder
|
||||
|
||||
expanded_string (a_ht: HASH_TABLE [detachable ANY, STRING]): STRING
|
||||
-- Expanded template using variable from `a_ht'
|
||||
local
|
||||
tpl: like template
|
||||
exp: URI_TEMPLATE_EXPRESSION
|
||||
p,q: INTEGER
|
||||
do
|
||||
analyze
|
||||
tpl := template
|
||||
if attached expressions as l_expressions then
|
||||
create Result.make (tpl.count)
|
||||
from
|
||||
l_expressions.start
|
||||
p := 1
|
||||
until
|
||||
l_expressions.after
|
||||
loop
|
||||
q := l_expressions.item.position
|
||||
--| Added inter variable text
|
||||
Result.append (tpl.substring (p, q - 1))
|
||||
--| Expand variables ...
|
||||
exp := l_expressions.item
|
||||
exp.append_expanded_to_string (a_ht, Result)
|
||||
|
||||
p := q + l_expressions.item.expression.count + 2
|
||||
|
||||
l_expressions.forth
|
||||
end
|
||||
Result.append (tpl.substring (p, tpl.count))
|
||||
else
|
||||
create Result.make_from_string (tpl)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Match
|
||||
|
||||
match (a_uri: STRING): detachable URI_TEMPLATE_MATCH_RESULT
|
||||
require
|
||||
is_valid: is_valid
|
||||
local
|
||||
b: BOOLEAN
|
||||
tpl: like template
|
||||
l_offset: INTEGER
|
||||
p,q: INTEGER
|
||||
exp: URI_TEMPLATE_EXPRESSION
|
||||
vn, s,t: STRING
|
||||
vv: STRING
|
||||
l_vars, l_path_vars, l_query_vars: HASH_TABLE [STRING, STRING]
|
||||
l_uri_count: INTEGER
|
||||
tpl_count: INTEGER
|
||||
l_next_literal_separator: detachable STRING
|
||||
do
|
||||
--| Extract expansion parts "\\{([^\\}]*)\\}"
|
||||
analyze
|
||||
if attached expressions as l_expressions then
|
||||
create l_path_vars.make (l_expressions.count)
|
||||
create l_query_vars.make (l_expressions.count)
|
||||
l_vars := l_path_vars
|
||||
b := True
|
||||
l_uri_count := a_uri.count
|
||||
tpl := template
|
||||
tpl_count := tpl.count
|
||||
if l_expressions.is_empty then
|
||||
-- b := a_uri.substring (1, tpl_count).same_string (tpl)
|
||||
b := a_uri.same_string (tpl)
|
||||
else
|
||||
from
|
||||
l_expressions.start
|
||||
p := 0
|
||||
l_offset := 0
|
||||
until
|
||||
l_expressions.after or not b
|
||||
loop
|
||||
exp := l_expressions.item
|
||||
vn := exp.expression
|
||||
q := exp.position
|
||||
--| Check text between vars
|
||||
if p = q then
|
||||
--| There should be at least one literal between two expression
|
||||
--| {var}{foobar} is ambigous for matching ...
|
||||
b := False
|
||||
elseif q > p then
|
||||
if p = 0 then
|
||||
p := 1
|
||||
end
|
||||
t := tpl.substring (p, q - 1)
|
||||
s := a_uri.substring (p + l_offset, q + l_offset - 1)
|
||||
b := s.same_string (t)
|
||||
p := exp.end_position
|
||||
end
|
||||
l_expressions.forth --| we forth `l_expressions' so be careful
|
||||
|
||||
--| Check related variable
|
||||
if b and then not vn.is_empty then
|
||||
if exp.is_query then
|
||||
l_vars := l_query_vars
|
||||
else
|
||||
l_vars := l_path_vars
|
||||
end
|
||||
if q + l_offset <= l_uri_count then
|
||||
inspect vn[1]
|
||||
when '?' then
|
||||
import_form_style_parameters_into (a_uri.substring (q + l_offset + 1, l_uri_count), l_vars)
|
||||
p := tpl_count + 1
|
||||
l_offset := l_offset + (l_uri_count - (q + l_offset + 1))
|
||||
when ';' then
|
||||
import_path_style_parameters_into (a_uri.substring (q + l_offset, l_uri_count), l_vars)
|
||||
p := tpl_count + 1
|
||||
else
|
||||
if not l_expressions.after then
|
||||
exp := l_expressions.item --| We change `exp' here
|
||||
l_next_literal_separator := tpl.substring (p, exp.position -1)
|
||||
elseif p < tpl_count then
|
||||
l_next_literal_separator := tpl.substring (p, tpl_count)
|
||||
else
|
||||
l_next_literal_separator := Void
|
||||
end
|
||||
vv := next_path_variable_value (a_uri, q + l_offset, l_next_literal_separator)
|
||||
l_vars.force (vv, vn)
|
||||
l_offset := l_offset + vv.count - (vn.count + 2)
|
||||
end
|
||||
else
|
||||
b := exp.is_query --| query are optional
|
||||
end
|
||||
end
|
||||
if b and l_expressions.after then
|
||||
if
|
||||
(p < tpl_count) or
|
||||
(p + l_offset < l_uri_count)
|
||||
then
|
||||
--| Remaining literal part
|
||||
t := tpl.substring (p, tpl_count)
|
||||
s := a_uri.substring (p + l_offset, l_uri_count)
|
||||
b := s.same_string (t)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if b then
|
||||
create Result.make (l_path_vars, l_query_vars)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Basic operation
|
||||
|
||||
parse
|
||||
-- Parse template
|
||||
do
|
||||
reset
|
||||
analyze
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_valid: BOOLEAN
|
||||
-- Is Current URI template valid?
|
||||
do
|
||||
analyze
|
||||
Result := not has_syntax_error
|
||||
end
|
||||
|
||||
feature {NONE} -- Internal Access
|
||||
|
||||
reset
|
||||
do
|
||||
expressions := Void
|
||||
has_syntax_error := False
|
||||
end
|
||||
|
||||
has_syntax_error: BOOLEAN
|
||||
-- Has syntax error
|
||||
--| Make sense only if `analyze' was processed before
|
||||
|
||||
expressions: detachable LIST [URI_TEMPLATE_EXPRESSION]
|
||||
-- Expansion parts
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
analyze
|
||||
local
|
||||
l_expressions: like expressions
|
||||
c: CHARACTER
|
||||
i,p,n: INTEGER
|
||||
tpl: like template
|
||||
in_x: BOOLEAN
|
||||
in_query: BOOLEAN
|
||||
x: STRING
|
||||
exp: URI_TEMPLATE_EXPRESSION
|
||||
l_has_query_expression: BOOLEAN
|
||||
do
|
||||
l_expressions := expressions
|
||||
if l_expressions = Void then
|
||||
tpl := template
|
||||
|
||||
--| Extract expansion parts "\\{([^\\}]*)\\}"
|
||||
create {ARRAYED_LIST [like expressions.item]} l_expressions.make (tpl.occurrences ('{'))
|
||||
from
|
||||
i := 1
|
||||
n := tpl.count
|
||||
l_has_query_expression := False
|
||||
create x.make_empty
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
c := tpl[i]
|
||||
if in_x then
|
||||
if c = '}' then
|
||||
create exp.make (p, x.twin, in_query)
|
||||
l_expressions.force (exp)
|
||||
x.wipe_out
|
||||
in_x := False
|
||||
if l_has_query_expression and then i < n then
|
||||
--| Remaining text after {?exp}
|
||||
has_syntax_error := True
|
||||
end
|
||||
else
|
||||
x.extend (c)
|
||||
end
|
||||
else
|
||||
inspect c
|
||||
when '{' then
|
||||
check x_is_empty: x.is_empty end
|
||||
p := i
|
||||
in_x := True
|
||||
if not l_has_query_expression then
|
||||
l_has_query_expression := tpl.valid_index (i+1) and then tpl[i+1] = '?'
|
||||
end
|
||||
if not in_query then
|
||||
in_query := l_has_query_expression
|
||||
end
|
||||
when '?' then
|
||||
in_query := True
|
||||
else
|
||||
end
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
expressions := l_expressions
|
||||
end
|
||||
end
|
||||
|
||||
import_path_style_parameters_into (a_content: STRING; res: HASH_TABLE [STRING, STRING])
|
||||
require
|
||||
a_content_attached: a_content /= Void
|
||||
res_attached: res /= Void
|
||||
do
|
||||
import_custom_style_parameters_into (a_content, ';', res)
|
||||
end
|
||||
|
||||
import_form_style_parameters_into (a_content: STRING; res: HASH_TABLE [STRING, STRING])
|
||||
require
|
||||
a_content_attached: a_content /= Void
|
||||
res_attached: res /= Void
|
||||
do
|
||||
import_custom_style_parameters_into (a_content, '&', res)
|
||||
end
|
||||
|
||||
import_custom_style_parameters_into (a_content: STRING; a_separator: CHARACTER; res: HASH_TABLE [STRING, STRING])
|
||||
require
|
||||
a_content_attached: a_content /= Void
|
||||
res_attached: res /= Void
|
||||
local
|
||||
n, p, i, j: INTEGER
|
||||
s: STRING
|
||||
l_name,l_value: STRING
|
||||
do
|
||||
n := a_content.count
|
||||
if n > 0 then
|
||||
from
|
||||
p := 1
|
||||
until
|
||||
p = 0
|
||||
loop
|
||||
i := a_content.index_of (a_separator, p)
|
||||
if i = 0 then
|
||||
s := a_content.substring (p, n)
|
||||
p := 0
|
||||
else
|
||||
s := a_content.substring (p, i - 1)
|
||||
p := i + 1
|
||||
end
|
||||
if not s.is_empty then
|
||||
j := s.index_of ('=', 1)
|
||||
if j > 0 then
|
||||
l_name := s.substring (1, j - 1)
|
||||
l_value := s.substring (j + 1, s.count)
|
||||
res.force (l_value, l_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
next_path_variable_value (a_uri: STRING; a_index: INTEGER; a_end_token: detachable STRING): STRING
|
||||
require
|
||||
valid_index: a_index <= a_uri.count
|
||||
local
|
||||
c: CHARACTER
|
||||
i,n,p: INTEGER
|
||||
l_end_token_first_char: CHARACTER
|
||||
l_end_token_count: INTEGER
|
||||
do
|
||||
from
|
||||
if a_end_token /= Void and then not a_end_token.is_empty then
|
||||
l_end_token_first_char := a_end_token.item (1)
|
||||
l_end_token_count := a_end_token.count
|
||||
end
|
||||
i := a_index
|
||||
n := a_uri.count
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
c := a_uri[i]
|
||||
inspect c
|
||||
when '/', '?' then
|
||||
i := n
|
||||
else
|
||||
if
|
||||
a_end_token /= Void and then
|
||||
c = l_end_token_first_char and then
|
||||
a_uri.substring (i, i + l_end_token_count - 1).same_string (a_end_token)
|
||||
then
|
||||
i := n
|
||||
else
|
||||
p := i
|
||||
end
|
||||
end
|
||||
i := i + 1
|
||||
end
|
||||
Result := a_uri.substring (a_index, p)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,52 @@
|
||||
note
|
||||
description: "[
|
||||
Summary description for {URI_TEMPLATE_CONSTANTS}.
|
||||
|
||||
see http://tools.ietf.org/html/draft-gregorio-uritemplate-04
|
||||
]"
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
URI_TEMPLATE_CONSTANTS
|
||||
|
||||
feature -- Operator
|
||||
|
||||
Reserved_operator: CHARACTER = '+'
|
||||
|
||||
Form_style_query_operator: CHARACTER = '?'
|
||||
|
||||
Path_style_parameters_operator: CHARACTER = ';'
|
||||
|
||||
Path_segment_operator: CHARACTER = '/'
|
||||
|
||||
Label_operator: CHARACTER = '.'
|
||||
|
||||
feature -- Separator
|
||||
|
||||
Default_delimiter: CHARACTER = '=' --| Draft 0.4 , change to '|' for Draft 0.5
|
||||
|
||||
feature -- Explode
|
||||
|
||||
Explode_plus: CHARACTER = '+'
|
||||
|
||||
Explode_star: CHARACTER = '*'
|
||||
|
||||
feature -- Modified
|
||||
|
||||
Modifier_substring: CHARACTER = ':'
|
||||
|
||||
Modifier_remainder: CHARACTER = '^'
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,274 @@
|
||||
note
|
||||
description: "Summary description for {URI_TEMPLATE_EXPRESSION}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
URI_TEMPLATE_EXPRESSION
|
||||
|
||||
inherit
|
||||
ANY
|
||||
|
||||
DEBUG_OUTPUT
|
||||
export {NONE} all end
|
||||
|
||||
URI_TEMPLATE_CONSTANTS
|
||||
export {NONE} all end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_position: INTEGER; a_expression: STRING; a_is_query: BOOLEAN)
|
||||
do
|
||||
position := a_position
|
||||
expression := a_expression
|
||||
is_query := a_is_query
|
||||
operator := '%U'
|
||||
end
|
||||
|
||||
feature -- Processing
|
||||
|
||||
analyze
|
||||
local
|
||||
exp: like expression
|
||||
s: detachable STRING
|
||||
lst: LIST [STRING]
|
||||
p: INTEGER
|
||||
vars: like variables
|
||||
vn: STRING
|
||||
vmodifier: detachable STRING
|
||||
i,n: INTEGER
|
||||
do
|
||||
if not is_analyzed then
|
||||
exp := expression
|
||||
if not exp.is_empty then
|
||||
op_prefix := '%U'
|
||||
op_delimiter := ','
|
||||
inspect exp[1]
|
||||
when Reserved_operator then
|
||||
--| '+'
|
||||
reserved := True
|
||||
operator := '+'
|
||||
when Label_operator then
|
||||
--| '.'
|
||||
operator := '.'
|
||||
op_prefix := '.'
|
||||
op_delimiter := '.'
|
||||
when Path_segment_operator then
|
||||
--| '/'
|
||||
operator := '/'
|
||||
op_prefix := '/'
|
||||
op_delimiter := '/'
|
||||
when Path_style_parameters_operator then
|
||||
--| ';'
|
||||
operator := ';'
|
||||
op_prefix := ';'
|
||||
op_delimiter := ';'
|
||||
when Form_style_query_operator then
|
||||
--| '?'
|
||||
operator := '?'
|
||||
op_prefix := '?'
|
||||
op_delimiter := '&'
|
||||
when '|', '!', '@' then
|
||||
operator := exp[1]
|
||||
else
|
||||
operator := '%U'
|
||||
end
|
||||
if operator /= '%U' then
|
||||
s := exp.substring (2, exp.count)
|
||||
else
|
||||
s := exp
|
||||
end
|
||||
|
||||
lst := s.split (',')
|
||||
from
|
||||
create {ARRAYED_LIST [like variables.item]} vars.make (lst.count)
|
||||
lst.start
|
||||
until
|
||||
lst.after
|
||||
loop
|
||||
s := lst.item
|
||||
vmodifier := Void
|
||||
p := s.index_of (Default_delimiter, 1)
|
||||
if p > 0 then
|
||||
vn := s.substring (1, p - 1)
|
||||
s := s.substring (p + 1, s.count)
|
||||
else
|
||||
vn := s
|
||||
s := Void
|
||||
end
|
||||
from
|
||||
vmodifier := Void
|
||||
i := 1
|
||||
n := vn.count
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
inspect vn[i]
|
||||
when Explode_plus, Explode_star, Modifier_substring, Modifier_remainder then
|
||||
vmodifier := vn.substring (i, n)
|
||||
vn := vn.substring (1, i - 1)
|
||||
i := n + 1 --| exit
|
||||
else
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
vars.force (create {URI_TEMPLATE_EXPRESSION_VARIABLE}.make (Current, vn, s, vmodifier))
|
||||
lst.forth
|
||||
end
|
||||
variables := vars
|
||||
end
|
||||
is_analyzed := True
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
position: INTEGER
|
||||
-- Character position on Current in the template
|
||||
|
||||
end_position: INTEGER
|
||||
do
|
||||
Result := position + expression.count + 2 --| '{' + `expression' + '}'
|
||||
end
|
||||
|
||||
expression: STRING
|
||||
-- Operator? + VariableName + modifier
|
||||
|
||||
is_query: BOOLEAN
|
||||
-- Is in the query part (i.e: after '?' ?)
|
||||
|
||||
feature -- Status
|
||||
|
||||
operator: CHARACTER
|
||||
-- First character of `expression' if among
|
||||
|
||||
reserved: BOOLEAN
|
||||
-- Is reserved
|
||||
-- i.e: do not url-encode the reserved character
|
||||
|
||||
op_prefix: CHARACTER
|
||||
-- When expanding list of table, first character to use
|
||||
--| ex: '?' for {?var}
|
||||
|
||||
op_delimiter: CHARACTER
|
||||
-- When expanding list of table, delimiter character to use
|
||||
--| ex: ',' for {?var}
|
||||
|
||||
variables: detachable LIST [URI_TEMPLATE_EXPRESSION_VARIABLE]
|
||||
-- List of variables declared in `expression'
|
||||
--| ex: "foo", "bar" for {?foo,bar}
|
||||
|
||||
variable_names: LIST [STRING]
|
||||
do
|
||||
analyze
|
||||
if attached variables as vars then
|
||||
create {ARRAYED_LIST [STRING]} Result.make (vars.count)
|
||||
from
|
||||
vars.start
|
||||
until
|
||||
vars.after
|
||||
loop
|
||||
Result.force (vars.item.name)
|
||||
vars.forth
|
||||
end
|
||||
else
|
||||
create {ARRAYED_LIST [STRING]} Result.make (0)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
is_analyzed: BOOLEAN
|
||||
|
||||
feature -- Report
|
||||
|
||||
append_expanded_to_string (a_ht: HASH_TABLE [detachable ANY, STRING]; a_buffer: STRING)
|
||||
do
|
||||
analyze
|
||||
if attached variables as vars then
|
||||
append_custom_variables_to_string (a_ht, vars, op_prefix, op_delimiter, True, a_buffer)
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
append_custom_variables_to_string (a_ht: HASH_TABLE [detachable ANY, STRING]; vars: like variables; prefix_char, delimiter_char: CHARACTER; a_include_name: BOOLEAN; a_buffer: STRING)
|
||||
-- If `first_char' is '%U' do not print any first character
|
||||
local
|
||||
vi: like variables.item
|
||||
l_is_first: BOOLEAN
|
||||
vdata: detachable ANY
|
||||
vstr: detachable STRING
|
||||
l_use_default: BOOLEAN
|
||||
do
|
||||
if vars /= Void then
|
||||
from
|
||||
vars.start
|
||||
l_is_first := True
|
||||
until
|
||||
vars.after
|
||||
loop
|
||||
vi := vars.item
|
||||
vdata := a_ht.item (vi.name)
|
||||
vstr := Void
|
||||
if vdata /= Void then
|
||||
vstr := vi.expanded_string (vdata)
|
||||
if vstr = Void and vi.has_explode then
|
||||
--| Missing or list empty
|
||||
vstr := vi.default_value
|
||||
l_use_default := True
|
||||
else
|
||||
l_use_default := False
|
||||
end
|
||||
else
|
||||
--| Missing
|
||||
vstr := vi.default_value
|
||||
l_use_default := True
|
||||
end
|
||||
if vstr /= Void then
|
||||
if l_is_first then
|
||||
if prefix_char /= '%U' then
|
||||
a_buffer.append_character (prefix_char)
|
||||
end
|
||||
l_is_first := False
|
||||
else
|
||||
a_buffer.append_character (delimiter_char)
|
||||
end
|
||||
if l_use_default and (operator = Form_style_query_operator) and not vi.has_explode_star then
|
||||
a_buffer.append (vi.name)
|
||||
if vi.has_explode_plus then
|
||||
a_buffer.append_character ('.')
|
||||
else
|
||||
a_buffer.append_character ('=')
|
||||
end
|
||||
end
|
||||
a_buffer.append (vstr)
|
||||
end
|
||||
vars.forth
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Status report
|
||||
|
||||
debug_output: STRING
|
||||
-- String that should be displayed in debugger to represent `Current'.
|
||||
do
|
||||
Result := expression
|
||||
end
|
||||
|
||||
;note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,412 @@
|
||||
note
|
||||
description: "Summary description for {URI_TEMPLATE_EXPRESSION_VARIABLE}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
URI_TEMPLATE_EXPRESSION_VARIABLE
|
||||
|
||||
inherit
|
||||
ANY
|
||||
|
||||
URI_TEMPLATE_CONSTANTS
|
||||
export {NONE} all end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (exp: like expression; n: like name; d: like default_value; em: detachable STRING)
|
||||
-- Create based on expression `exp', variable name `n', default value `d' if any
|
||||
-- and explode or modifier string `em'
|
||||
do
|
||||
expression := exp
|
||||
operator := exp.operator
|
||||
name := n
|
||||
default_value := d
|
||||
if em /= Void and then em.count > 0 then
|
||||
inspect em[1]
|
||||
when Explode_star, Explode_plus then
|
||||
explode := em[1]
|
||||
when Modifier_substring, Modifier_remainder then
|
||||
modifier := em
|
||||
else
|
||||
end
|
||||
end
|
||||
|
||||
op_prefix := '%U'
|
||||
op_separator := ','
|
||||
|
||||
inspect operator
|
||||
when Reserved_operator then --| '+'
|
||||
reserved := True
|
||||
when Form_style_query_operator then --| '?'
|
||||
op_prefix := '?'
|
||||
op_separator := '&'
|
||||
when Path_style_parameters_operator then --| ';'
|
||||
op_prefix := ';'
|
||||
op_separator := ';'
|
||||
when Path_segment_operator then --| '/'
|
||||
op_prefix := '/'
|
||||
op_separator := '/'
|
||||
when Label_operator then --| '.'
|
||||
op_prefix := '.'
|
||||
op_separator := '.'
|
||||
else
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
expression: URI_TEMPLATE_EXPRESSION
|
||||
-- Parent expression
|
||||
|
||||
operator: CHARACTER
|
||||
-- First character of related `expression'
|
||||
|
||||
name: STRING
|
||||
-- variable name
|
||||
|
||||
default_value: detachable STRING
|
||||
-- default value if any
|
||||
|
||||
reserved: BOOLEAN
|
||||
-- Is reserved?
|
||||
-- i.e: do not url-encode the reserved character
|
||||
|
||||
op_prefix: CHARACTER
|
||||
-- When expanding list of table, first character to use
|
||||
--| ex: '?' for {?var}
|
||||
|
||||
op_separator: CHARACTER
|
||||
-- When expanding list of table, delimiter character to use
|
||||
--| ex: ',' for {?var}
|
||||
|
||||
explode: CHARACTER
|
||||
-- Explode character , '*' or '+'
|
||||
|
||||
modifier: detachable STRING
|
||||
-- Modifier expression, starting by ':' or '^'
|
||||
--| ":3" , "-3", "^4", ...
|
||||
|
||||
modified_string (s: READABLE_STRING_GENERAL): READABLE_STRING_GENERAL
|
||||
local
|
||||
t: STRING
|
||||
i,n: INTEGER
|
||||
do
|
||||
Result := s
|
||||
if attached modifier as m and then m.count > 1 then
|
||||
n := s.count
|
||||
t := m.substring (2, m.count)
|
||||
if t.is_integer then
|
||||
i := t.to_integer
|
||||
inspect m[1]
|
||||
when Modifier_substring then
|
||||
if i > 0 then
|
||||
if i < n then
|
||||
Result := s.substring (1, i)
|
||||
end
|
||||
elseif i < 0 then
|
||||
Result := s.substring (n - i, n)
|
||||
end
|
||||
when Modifier_remainder then
|
||||
if i > 0 then
|
||||
if i < n then
|
||||
Result := s.substring (i + 1, n)
|
||||
end
|
||||
elseif i < 0 then
|
||||
Result := s.substring (1, n + i) --| n + i = n - (-i)
|
||||
end
|
||||
else
|
||||
check Known_modified: False end
|
||||
-- Unchanged
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
has_explode: BOOLEAN
|
||||
do
|
||||
Result := explode = Explode_plus or explode = Explode_star
|
||||
end
|
||||
|
||||
has_explode_plus: BOOLEAN
|
||||
do
|
||||
Result := explode = Explode_plus
|
||||
end
|
||||
|
||||
has_explode_star: BOOLEAN
|
||||
do
|
||||
Result := explode = Explode_star
|
||||
end
|
||||
|
||||
feature -- Report
|
||||
|
||||
expanded_string (d: detachable ANY): detachable STRING
|
||||
local
|
||||
l_delimiter: CHARACTER
|
||||
v_enc: detachable STRING
|
||||
k_enc: STRING
|
||||
l_obj: detachable ANY
|
||||
i,n: INTEGER
|
||||
explode_is_plus: BOOLEAN
|
||||
explode_is_star: BOOLEAN
|
||||
l_has_explode: BOOLEAN
|
||||
dft: detachable ANY
|
||||
has_list_op: BOOLEAN
|
||||
op: like operator
|
||||
do
|
||||
l_has_explode := has_explode
|
||||
if l_has_explode then
|
||||
explode_is_plus := has_explode_plus
|
||||
explode_is_star := has_explode_star
|
||||
end
|
||||
op := operator
|
||||
has_list_op := op /= '%U' and op /= Reserved_operator
|
||||
dft := default_value
|
||||
create Result.make (20)
|
||||
if attached {READABLE_STRING_GENERAL} d as l_string then
|
||||
v_enc := url_encoded_string (modified_string (l_string), not reserved)
|
||||
if op = Form_style_query_operator then
|
||||
Result.append (name)
|
||||
Result.append_character ('=')
|
||||
elseif op = Path_style_parameters_operator then
|
||||
Result.append (name)
|
||||
if not v_enc.is_empty then
|
||||
Result.append_character ('=')
|
||||
end
|
||||
end
|
||||
Result.append (v_enc)
|
||||
elseif attached {ARRAY [detachable ANY]} d as l_array then
|
||||
if l_array.is_empty then
|
||||
if dft /= Void then
|
||||
inspect op
|
||||
when Form_style_query_operator, Path_style_parameters_operator then
|
||||
if not l_has_explode then
|
||||
Result.append (name)
|
||||
Result.append_character ('=')
|
||||
Result.append (dft.out)
|
||||
else
|
||||
if explode_is_plus then
|
||||
Result.append (name)
|
||||
Result.append_character ('.')
|
||||
end
|
||||
Result.append (dft.out)
|
||||
end
|
||||
when Path_segment_operator then
|
||||
if explode_is_plus then
|
||||
Result.append (name)
|
||||
Result.append_character ('.')
|
||||
end
|
||||
Result.append (dft.out)
|
||||
when Label_operator then
|
||||
else
|
||||
if l_has_explode then
|
||||
if explode_is_plus then
|
||||
Result.append (name)
|
||||
Result.append_character ('.')
|
||||
end
|
||||
Result.append (dft.out)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- nothing ...
|
||||
end
|
||||
else
|
||||
if l_has_explode then
|
||||
l_delimiter := op_separator
|
||||
else
|
||||
l_delimiter := ','
|
||||
inspect op
|
||||
when Form_style_query_operator then
|
||||
Result.append (name)
|
||||
Result.append_character ('=')
|
||||
else
|
||||
end
|
||||
end
|
||||
|
||||
from
|
||||
i := l_array.lower
|
||||
n := l_array.upper
|
||||
until
|
||||
i > n
|
||||
loop
|
||||
l_obj := l_array[i]
|
||||
if l_obj /= Void then
|
||||
v_enc := url_encoded_string (l_obj.out, not reserved)
|
||||
else
|
||||
v_enc := ""
|
||||
end
|
||||
if explode_is_plus then
|
||||
if
|
||||
(op = Form_style_query_operator and explode_is_plus) or
|
||||
(op = Path_style_parameters_operator and l_has_explode)
|
||||
then
|
||||
Result.append (name)
|
||||
Result.append_character ('=')
|
||||
else
|
||||
Result.append (name)
|
||||
Result.append_character ('.')
|
||||
end
|
||||
elseif explode_is_star and op = Form_style_query_operator then
|
||||
Result.append (name)
|
||||
Result.append_character ('=')
|
||||
end
|
||||
Result.append (v_enc)
|
||||
if i < n then
|
||||
Result.append_character (l_delimiter)
|
||||
end
|
||||
|
||||
i := i + 1
|
||||
end
|
||||
end
|
||||
if Result.is_empty then
|
||||
Result := Void
|
||||
end
|
||||
elseif attached {HASH_TABLE [detachable ANY, STRING]} d as l_table then
|
||||
if l_table.is_empty then
|
||||
if dft /= Void then
|
||||
inspect op
|
||||
when Form_style_query_operator, Path_style_parameters_operator then
|
||||
if not l_has_explode then
|
||||
Result.append (name)
|
||||
Result.append_character ('=')
|
||||
Result.append (dft.out)
|
||||
else
|
||||
if explode_is_plus then
|
||||
Result.append (name)
|
||||
Result.append_character ('.')
|
||||
end
|
||||
Result.append (dft.out)
|
||||
end
|
||||
when Path_segment_operator then
|
||||
if explode_is_plus then
|
||||
Result.append (name)
|
||||
Result.append_character ('.')
|
||||
end
|
||||
Result.append (dft.out)
|
||||
when Label_operator then
|
||||
else
|
||||
if l_has_explode then
|
||||
if explode_is_plus then
|
||||
Result.append (name)
|
||||
Result.append_character ('.')
|
||||
end
|
||||
Result.append (dft.out)
|
||||
end
|
||||
end
|
||||
else
|
||||
-- nothing ...
|
||||
end
|
||||
else
|
||||
if l_has_explode then
|
||||
l_delimiter := op_separator
|
||||
else
|
||||
l_delimiter := ','
|
||||
inspect op
|
||||
when Form_style_query_operator then
|
||||
Result.append (name)
|
||||
Result.append_character ('=')
|
||||
else
|
||||
end
|
||||
end
|
||||
|
||||
from
|
||||
l_table.start
|
||||
until
|
||||
l_table.after
|
||||
loop
|
||||
k_enc := url_encoded_string (l_table.key_for_iteration, not reserved)
|
||||
l_obj := l_table.item_for_iteration
|
||||
if l_obj /= Void then
|
||||
v_enc := url_encoded_string (l_obj.out, not reserved)
|
||||
else
|
||||
v_enc := ""
|
||||
end
|
||||
|
||||
if explode_is_plus then
|
||||
Result.append (name)
|
||||
Result.append_character ('.')
|
||||
end
|
||||
if
|
||||
l_has_explode
|
||||
then
|
||||
Result.append (k_enc)
|
||||
Result.append_character ('=')
|
||||
else
|
||||
Result.append (k_enc)
|
||||
Result.append_character (l_delimiter)
|
||||
end
|
||||
Result.append (v_enc)
|
||||
|
||||
l_table.forth
|
||||
if not l_table.after then
|
||||
Result.append_character (l_delimiter)
|
||||
end
|
||||
end
|
||||
end
|
||||
if Result.is_empty then
|
||||
Result := Void
|
||||
end
|
||||
else
|
||||
if d /= Void then
|
||||
v_enc := url_encoded_string (d.out, not reserved)
|
||||
elseif dft /= Void then
|
||||
v_enc := url_encoded_string (dft.out, not reserved)
|
||||
else
|
||||
v_enc := default_value
|
||||
end
|
||||
if op = Form_style_query_operator then
|
||||
Result.append (name)
|
||||
if v_enc /= Void then
|
||||
Result.append_character ('=')
|
||||
end
|
||||
elseif op = Path_style_parameters_operator then
|
||||
Result.append (name)
|
||||
if v_enc /= Void and then not v_enc.is_empty then
|
||||
Result.append_character ('=')
|
||||
end
|
||||
end
|
||||
if v_enc /= Void then
|
||||
Result.append (v_enc)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
url_encoded_string (s: READABLE_STRING_GENERAL; a_encoded: BOOLEAN): STRING
|
||||
do
|
||||
if a_encoded then
|
||||
Result := url_encoder.encoded_string (s.as_string_32)
|
||||
else
|
||||
Result := url_encoder.partial_encoded_string (s.as_string_32, <<
|
||||
':', ',',
|
||||
Reserved_operator,
|
||||
Label_operator,
|
||||
Path_segment_operator,
|
||||
Path_style_parameters_operator,
|
||||
Form_style_query_operator,
|
||||
'|', '!', '@'
|
||||
>>)
|
||||
end
|
||||
end
|
||||
|
||||
url_encoder: URL_ENCODER
|
||||
once
|
||||
create Result
|
||||
end
|
||||
|
||||
;note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,25 @@
|
||||
note
|
||||
description: "Summary description for {URI_TEMPLATE_HANDLER}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
deferred class
|
||||
URI_TEMPLATE_HANDLER
|
||||
|
||||
feature -- Events
|
||||
|
||||
|
||||
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,108 @@
|
||||
note
|
||||
description: "Summary description for {URI_TEMPLATE_MATCH_RESULT}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
URI_TEMPLATE_MATCH_RESULT
|
||||
|
||||
create
|
||||
make,
|
||||
make_empty
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (p: like path_variables; q: like query_variables)
|
||||
do
|
||||
path_variables := p
|
||||
query_variables := q
|
||||
end
|
||||
|
||||
make_empty
|
||||
do
|
||||
make (create {like path_variables}.make (0), create {like query_variables}.make (0))
|
||||
end
|
||||
|
||||
|
||||
feature -- Access
|
||||
|
||||
path_variables: HASH_TABLE [STRING, STRING]
|
||||
-- Variables being part of the path segments
|
||||
|
||||
query_variables: HASH_TABLE [STRING, STRING]
|
||||
-- Variables being part of the query segments (i.e: after the ?)
|
||||
|
||||
feature -- Query
|
||||
|
||||
path_variable (n: STRING): detachable STRING
|
||||
-- Value related to query variable name `n'
|
||||
do
|
||||
Result := path_variables.item (n)
|
||||
end
|
||||
|
||||
query_variable (n: STRING): detachable STRING
|
||||
-- Value related to path variable name `n'
|
||||
do
|
||||
Result := query_variables.item (n)
|
||||
end
|
||||
|
||||
variable (n: STRING): detachable STRING
|
||||
-- Value related to variable name `n'
|
||||
do
|
||||
Result := query_variable (n)
|
||||
if Result = Void then
|
||||
Result := path_variable (n)
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Query: url-decoded
|
||||
|
||||
url_decoded_query_variable (n: STRING): detachable STRING_32
|
||||
-- Unencoded value related to variable name `n'
|
||||
do
|
||||
if attached query_variable (n) as v then
|
||||
Result := url_decoded_string (v)
|
||||
end
|
||||
end
|
||||
|
||||
url_decoded_path_variable (n: STRING): detachable STRING_32
|
||||
-- Unencoded value related to variable name `n'
|
||||
do
|
||||
if attached path_variable (n) as v then
|
||||
Result := url_decoded_string (v)
|
||||
end
|
||||
end
|
||||
|
||||
url_decoded_variable (n: STRING): detachable STRING_32
|
||||
-- Unencoded value related to variable name `n'
|
||||
do
|
||||
if attached variable (n) as v then
|
||||
Result := url_decoded_string (v)
|
||||
end
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
url_decoded_string (s: READABLE_STRING_GENERAL): STRING_32
|
||||
do
|
||||
Result := url_encoder.decoded_string (s.as_string_8)
|
||||
end
|
||||
|
||||
url_encoder: URL_ENCODER
|
||||
once
|
||||
create Result
|
||||
end
|
||||
|
||||
;note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="test_uri_template_draft_05" uuid="1B44EA8C-7D88-4823-B16A-C9FA8DA3ACFF" library_target="test_uri_template_draft_05">
|
||||
<target name="test_uri_template_draft_05">
|
||||
<root class="ANY" feature="default_create"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<variable name="uri_template" value="draft_05"/>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
|
||||
<library name="uri_template" location="uri_template-safe.ecf"/>
|
||||
<tests name="tests" location=".\tests\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/test_uri_template.e$</exclude>
|
||||
</file_rule>
|
||||
</tests>
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,638 @@
|
||||
note
|
||||
description: "[
|
||||
Eiffel tests that can be executed by testing tool.
|
||||
]"
|
||||
author: "EiffelStudio test wizard"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
testing: "type/manual"
|
||||
|
||||
class
|
||||
TEST_URI_TEMPLATE_DRAFT_05
|
||||
|
||||
inherit
|
||||
EQA_TEST_SET
|
||||
|
||||
feature -- Test routines
|
||||
|
||||
test_uri_template_parser
|
||||
note
|
||||
testing: "uri-template-05"
|
||||
do
|
||||
uri_template_parse ("api/foo/{foo_id}/{?id,extra}", <<"foo_id">>, <<"id", "extra">>)
|
||||
uri_template_parse ("weather/{state}/{city}?forecast={day}", <<"state", "city">>, <<"day">>)
|
||||
end
|
||||
|
||||
test_uri_template_matcher
|
||||
note
|
||||
testing: "uri-template-05"
|
||||
local
|
||||
tpl: URI_TEMPLATE
|
||||
do
|
||||
create tpl.make ("{version}/{id}")
|
||||
uri_template_match (tpl, "v2/123", <<["version", "v2"], ["id" , "123"]>>, <<>>)
|
||||
|
||||
create tpl.make ("api/{foo}{bar}/id/{id}")
|
||||
uri_template_mismatch (tpl, "api/foobar/id/123")
|
||||
|
||||
create tpl.make ("api/foo/{foo_id}/{?id,extra}")
|
||||
uri_template_match (tpl, "api/foo/bar/", <<["foo_id", "bar"]>>, <<>>)
|
||||
uri_template_match (tpl, "api/foo/bar/?id=123", <<["foo_id", "bar"]>>, <<["id", "123"]>>)
|
||||
uri_template_match (tpl, "api/foo/bar/?id=123&extra=test", <<["foo_id", "bar"]>>, <<["id", "123"], ["extra", "test"]>>)
|
||||
uri_template_match (tpl, "api/foo/bar/?id=123&extra=test&one=more", <<["foo_id", "bar"]>>, <<["id", "123"], ["extra", "test"]>>)
|
||||
uri_template_mismatch (tpl, "")
|
||||
uri_template_mismatch (tpl, "/")
|
||||
uri_template_mismatch (tpl, "foo/bar/?id=123")
|
||||
uri_template_mismatch (tpl, "/api/foo/bar/")
|
||||
uri_template_mismatch (tpl, "api/foo/bar")
|
||||
|
||||
create tpl.make ("weather/{state}/{city}?forecast={day}")
|
||||
uri_template_match (tpl, "weather/California/Goleta?forecast=today", <<["state", "California"], ["city", "Goleta"]>>, <<["day", "today"]>>)
|
||||
end
|
||||
|
||||
uri_template_string_errors: detachable LIST [STRING]
|
||||
|
||||
test_uri_template_string_builder
|
||||
note
|
||||
testing: "uri-template-05"
|
||||
local
|
||||
ht: HASH_TABLE [detachable ANY, STRING]
|
||||
empty_keys: HASH_TABLE [STRING, STRING]
|
||||
empty_list: ARRAY [STRING]
|
||||
favs: HASH_TABLE [detachable ANY, STRING]
|
||||
keys: HASH_TABLE [STRING, STRING]
|
||||
colors: ARRAY [STRING]
|
||||
names: ARRAY [STRING]
|
||||
semi_dot: HASH_TABLE [STRING, STRING]
|
||||
vals: ARRAY [STRING]
|
||||
do
|
||||
create ht.make (3)
|
||||
ht.force ("FooBar", "foo_id")
|
||||
ht.force ("That's right!", "extra")
|
||||
ht.force ("123", "id")
|
||||
ht.force ("California", "state")
|
||||
ht.force ("Goleta", "city")
|
||||
ht.force ("today", "day")
|
||||
|
||||
ht.force ("value", "var")
|
||||
ht.force ("Hello World!", "hello")
|
||||
ht.force ("", "empty")
|
||||
ht.force ("/foo/bar", "path")
|
||||
ht.force ("1024", "x")
|
||||
ht.force ("768", "y")
|
||||
ht.force ("fred", "foo")
|
||||
ht.force ("That's right!", "foo2")
|
||||
ht.force ("http://example.com/home/", "base")
|
||||
|
||||
names := <<"Fred", "Wilma", "Pebbles">>
|
||||
ht.force (names, "name")
|
||||
create favs.make (2)
|
||||
favs.force ("red", "color")
|
||||
favs.force ("high", "volume")
|
||||
ht.force (favs, "favs")
|
||||
|
||||
create empty_list.make_empty
|
||||
ht.force (empty_list,"empty_list")
|
||||
|
||||
create empty_keys.make (0)
|
||||
ht.force (empty_keys,"empty_keys")
|
||||
|
||||
vals := <<"val1", "val2", "val3">>
|
||||
ht.force (vals, "list")
|
||||
create keys.make (2)
|
||||
keys.force ("val1", "key1")
|
||||
keys.force ("val2", "key2")
|
||||
ht.force (keys, "keys")
|
||||
|
||||
colors := <<"red", "green", "blue">>
|
||||
create semi_dot.make (3)
|
||||
semi_dot.force (";", "semi")
|
||||
semi_dot.force (".", "dot")
|
||||
semi_dot.force (",", "comma")
|
||||
|
||||
create {ARRAYED_LIST [STRING]} uri_template_string_errors.make (10)
|
||||
|
||||
|
||||
--| Simple string expansion
|
||||
uri_template_string (ht, "{var}", "value")
|
||||
uri_template_string (ht, "{hello}", "Hello+World%%21")
|
||||
uri_template_string (ht, "O{empty}X", "OX")
|
||||
uri_template_string (ht, "O{undef}X", "OX")
|
||||
|
||||
--| String expansion with defaults
|
||||
uri_template_string (ht, "{var|default}", "value")
|
||||
uri_template_string (ht, "O{empty|default}X", "OX")
|
||||
uri_template_string (ht, "O{undef|default}X", "OdefaultX")
|
||||
|
||||
--| Reserved expansion with defaults
|
||||
uri_template_string (ht, "{+var}", "value")
|
||||
uri_template_string (ht, "{+hello}", "Hello+World!")
|
||||
uri_template_string (ht, "{+path}/here", "/foo/bar/here")
|
||||
uri_template_string (ht, "here?ref={+path}", "here?ref=/foo/bar")
|
||||
uri_template_string (ht, "up{+path}{x}/here", "up/foo/bar1024/here")
|
||||
uri_template_string (ht, "up{+empty|/1}/here", "up/here")
|
||||
uri_template_string (ht, "up{+undef|/1}/here", "up/1/here")
|
||||
|
||||
--| String expansion with multiple variables
|
||||
uri_template_string (ht, "{x,y}", "1024,768")
|
||||
uri_template_string (ht, "{x,hello,y}", "1024,Hello+World%%21,768")
|
||||
uri_template_string (ht, "?{x,empty}", "?1024,")
|
||||
uri_template_string (ht, "?{x,undef}", "?1024")
|
||||
uri_template_string (ht, "?{undef,y}", "?768")
|
||||
uri_template_string (ht, "?{x,undef|0}", "?1024,0")
|
||||
|
||||
--| Reserved expansion with multiple variables
|
||||
uri_template_string (ht, "{+x,hello,y}", "1024,Hello+World!,768")
|
||||
uri_template_string (ht, "{+path,x}/here", "/foo/bar,1024/here")
|
||||
--| Label expansion, dot-prefixed
|
||||
uri_template_string (ht, "X{.var}", "X.value")
|
||||
uri_template_string (ht, "X{.empty}", "X.")
|
||||
uri_template_string (ht, "X{.undef}", "X")
|
||||
|
||||
--| Path segments, slash-prefixed
|
||||
uri_template_string (ht, "{/var}", "/value")
|
||||
uri_template_string (ht, "{/var,empty}", "/value/")
|
||||
uri_template_string (ht, "{/var,undef}", "/value")
|
||||
|
||||
--| Path-style parameters, semicolon-prefixed
|
||||
uri_template_string (ht, "{;x,y}", ";x=1024;y=768")
|
||||
uri_template_string (ht, "{;x,y,empty}", ";x=1024;y=768;empty")
|
||||
uri_template_string (ht, "{;x,y,undef}", ";x=1024;y=768")
|
||||
|
||||
--| Form-style query, ampersand-separated
|
||||
uri_template_string (ht, "{?x,y}", "?x=1024&y=768")
|
||||
uri_template_string (ht, "{?x,y,empty}", "?x=1024&y=768&empty=")
|
||||
uri_template_string (ht, "{?x,y,undef}", "?x=1024&y=768")
|
||||
|
||||
|
||||
ht.force (colors, "list")
|
||||
ht.force (semi_dot, "keys")
|
||||
--| String expansion with value modifiers
|
||||
uri_template_string (ht, "{var:3}", "val")
|
||||
uri_template_string (ht, "{var:30}", "value")
|
||||
uri_template_string (ht, "{var^3}", "ue")
|
||||
uri_template_string (ht, "{var^-3}", "va")
|
||||
|
||||
uri_template_string (ht, "{list}", "red,green,blue")
|
||||
uri_template_string (ht, "{list*}", "red,green,blue")
|
||||
uri_template_string (ht, "{keys}", "semi,%%3B,dot,.,comma,%%2C")
|
||||
uri_template_string (ht, "{keys*}", "semi=%%3B,dot=.,comma=%%2C")
|
||||
--| Reserved expansion with value modifiers
|
||||
uri_template_string (ht, "{+path:6}/here", "/foo/b/here")
|
||||
uri_template_string (ht, "{+list}", "red,green,blue")
|
||||
uri_template_string (ht, "{+list*}", "red,green,blue")
|
||||
uri_template_string (ht, "{+keys}", "semi,;,dot,.,comma,,")
|
||||
uri_template_string (ht, "{+keys*}", "semi=;,dot=.,comma=,")
|
||||
--| Label expansion, dot-prefixed
|
||||
uri_template_string (ht, "X{.var:3}", "X.val")
|
||||
uri_template_string (ht, "X{.list}", "X.red,green,blue")
|
||||
uri_template_string (ht, "X{.list*}", "X.red.green.blue")
|
||||
uri_template_string (ht, "X{.keys}", "X.semi,%%3B,dot,.,comma,%%2C")
|
||||
uri_template_string (ht, "X{.keys*}", "X.semi=%%3B.dot=..comma=%%2C")
|
||||
|
||||
--| Path segments, slash-prefixed
|
||||
uri_template_string (ht, "{/var:1,var}", "/v/value")
|
||||
uri_template_string (ht, "{/list}", "/red,green,blue")
|
||||
uri_template_string (ht, "{/list*}", "/red/green/blue")
|
||||
uri_template_string (ht, "{/list*,path:4}", "/red/green/blue/%%2Ffoo")
|
||||
uri_template_string (ht, "{/keys}", "/semi,%%3B,dot,.,comma,%%2C")
|
||||
uri_template_string (ht, "{/keys*}", "/semi=%%3B/dot=./comma=%%2C")
|
||||
|
||||
--| Path-style parameters, semicolon-prefixed
|
||||
uri_template_string (ht, "{;hello:5}", ";hello=Hello")
|
||||
uri_template_string (ht, "{;list}", ";red,green,blue")
|
||||
uri_template_string (ht, "{;list*}", ";red;green;blue")
|
||||
uri_template_string (ht, "{;keys}", ";semi,%%3B,dot,.,comma,%%2C")
|
||||
uri_template_string (ht, "{;keys*}", ";semi=%%3B;dot=.;comma=%%2C")
|
||||
|
||||
--| Form-style query, ampersand-separated
|
||||
uri_template_string (ht, "{?var:3}", "?var=val")
|
||||
uri_template_string (ht, "{?list}", "?list=red,green,blue")
|
||||
uri_template_string (ht, "{?list*}", "?list=red&list=green&list=blue")
|
||||
uri_template_string (ht, "{?keys}", "?keys=semi,%%3B,dot,.,comma,%%2C")
|
||||
uri_template_string (ht, "{?keys*}", "?semi=%%3B&dot=.&comma=%%2C")
|
||||
|
||||
assert ("all strings built", uri_template_string_errors = Void or (attached uri_template_string_errors as err and then err.is_empty))
|
||||
end
|
||||
|
||||
|
||||
test_uri_template_string_builder_extra
|
||||
note
|
||||
testing: "uri-template-05"
|
||||
local
|
||||
ht: HASH_TABLE [detachable ANY, STRING]
|
||||
empty_keys: HASH_TABLE [STRING, STRING]
|
||||
empty_list: ARRAY [STRING]
|
||||
favs: HASH_TABLE [detachable ANY, STRING]
|
||||
keys: HASH_TABLE [STRING, STRING]
|
||||
colors: ARRAY [STRING]
|
||||
names: ARRAY [STRING]
|
||||
semi_dot: HASH_TABLE [STRING, STRING]
|
||||
vals: ARRAY [STRING]
|
||||
do
|
||||
create ht.make (3)
|
||||
ht.force ("FooBar", "foo_id")
|
||||
ht.force ("That's right!", "extra")
|
||||
ht.force ("123", "id")
|
||||
ht.force ("California", "state")
|
||||
ht.force ("Goleta", "city")
|
||||
ht.force ("today", "day")
|
||||
|
||||
ht.force ("value", "var")
|
||||
ht.force ("Hello World!", "hello")
|
||||
ht.force ("", "empty")
|
||||
ht.force ("/foo/bar", "path")
|
||||
ht.force ("1024", "x")
|
||||
ht.force ("768", "y")
|
||||
ht.force ("fred", "foo")
|
||||
ht.force ("That's right!", "foo2")
|
||||
ht.force ("http://example.com/home/", "base")
|
||||
|
||||
names := <<"Fred", "Wilma", "Pebbles">>
|
||||
ht.force (names, "name")
|
||||
create favs.make (2)
|
||||
favs.force ("red", "color")
|
||||
favs.force ("high", "volume")
|
||||
ht.force (favs, "favs")
|
||||
|
||||
create empty_list.make_empty
|
||||
ht.force (empty_list,"empty_list")
|
||||
|
||||
create empty_keys.make (0)
|
||||
ht.force (empty_keys,"empty_keys")
|
||||
|
||||
vals := <<"val1", "val2", "val3">>
|
||||
ht.force (vals, "list")
|
||||
create keys.make (2)
|
||||
keys.force ("val1", "key1")
|
||||
keys.force ("val2", "key2")
|
||||
ht.force (keys, "keys")
|
||||
|
||||
colors := <<"red", "green", "blue">>
|
||||
create semi_dot.make (3)
|
||||
semi_dot.force (";", "semi")
|
||||
semi_dot.force (".", "dot")
|
||||
semi_dot.force (",", "comma")
|
||||
|
||||
create {ARRAYED_LIST [STRING]} uri_template_string_errors.make (10)
|
||||
|
||||
--| Addition to the spec
|
||||
uri_template_string (ht, "api/foo/{foo_id}/{?id,extra}",
|
||||
"api/foo/FooBar/?id=123&extra=That%%27s+right%%21")
|
||||
|
||||
uri_template_string (ht, "api/foo/{foo_id}/{?id,empty,undef,extra}",
|
||||
"api/foo/FooBar/?id=123&empty=&extra=That%%27s+right%%21")
|
||||
|
||||
uri_template_string (ht, "weather/{state}/{city}?forecast={day}",
|
||||
"weather/California/Goleta?forecast=today")
|
||||
|
||||
|
||||
uri_template_string (ht, "{var|default}", "value")
|
||||
uri_template_string (ht, "{undef|default}", "default")
|
||||
uri_template_string (ht, "{undef:3|default}", "default")
|
||||
|
||||
uri_template_string (ht, "x{empty}y", "xy")
|
||||
uri_template_string (ht, "x{empty|_}y", "xy")
|
||||
uri_template_string (ht, "x{undef}y", "xy")
|
||||
uri_template_string (ht, "x{undef|_}y", "x_y")
|
||||
|
||||
uri_template_string (ht, "x{.name|none}", "x.Fred,Wilma,Pebbles")
|
||||
uri_template_string (ht, "x{.name*|none}", "x.Fred.Wilma.Pebbles")
|
||||
uri_template_string (ht, "x{.empty}", "x.")
|
||||
uri_template_string (ht, "x{.empty|none}", "x.")
|
||||
uri_template_string (ht, "x{.undef}", "x")
|
||||
uri_template_string (ht, "x{.undef|none}", "x.none")
|
||||
|
||||
uri_template_string (ht, "x{/name|none}", "x/Fred,Wilma,Pebbles")
|
||||
uri_template_string (ht, "x{/name*|none}", "x/Fred/Wilma/Pebbles")
|
||||
uri_template_string (ht, "x{/undef}", "x")
|
||||
uri_template_string (ht, "x{/undef|none}", "x/none")
|
||||
uri_template_string (ht, "x{/empty}", "x/")
|
||||
uri_template_string (ht, "x{/empty|none}", "x/")
|
||||
uri_template_string (ht, "x{/empty_keys}", "x")
|
||||
uri_template_string (ht, "x{/empty_keys|none}", "x/none")
|
||||
uri_template_string (ht, "x{/empty_keys*}", "x")
|
||||
uri_template_string (ht, "x{/empty_keys*|none}", "x/none")
|
||||
|
||||
uri_template_string (ht, "x{;name|none}", "x;name=Fred,Wilma,Pebbles")
|
||||
uri_template_string (ht, "x{;favs|none}", "x;favs=color,red,volume,high")
|
||||
uri_template_string (ht, "x{;favs*|none}", "x;color=red;volume=high")
|
||||
uri_template_string (ht, "x{;empty}", "x;empty")
|
||||
uri_template_string (ht, "x{;empty|none}", "x;empty")
|
||||
|
||||
uri_template_string (ht, "x{;undef}", "x")
|
||||
uri_template_string (ht, "x{;undef|none}", "x;none")
|
||||
uri_template_string (ht, "x{;undef|foo=y}", "x;foo=y")
|
||||
|
||||
uri_template_string (ht, "x{?var|none}", "x?var=value")
|
||||
uri_template_string (ht, "x{?favs|none}", "x?favs=color,red,volume,high")
|
||||
uri_template_string (ht, "x{?favs*|none}", "x?color=red&volume=high")
|
||||
uri_template_string (ht, "x{?empty}", "x?empty=")
|
||||
uri_template_string (ht, "x{?empty|foo=none}", "x?empty=")
|
||||
uri_template_string (ht, "x{?undef}", "x")
|
||||
uri_template_string (ht, "x{?undef|foo=none}", "x?foo=none")
|
||||
uri_template_string (ht, "x{?empty_keys}", "x")
|
||||
uri_template_string (ht, "x{?empty_keys|none}", "x?none")
|
||||
uri_template_string (ht, "x{?empty_keys|y=z}", "x?y=z")
|
||||
uri_template_string (ht, "x{?empty_keys*|y=z}", "x?y=z")
|
||||
|
||||
|
||||
------
|
||||
|
||||
uri_template_string (ht, "x{empty_list}y", "xy")
|
||||
uri_template_string (ht, "x{empty_list|_}y", "xy")
|
||||
uri_template_string (ht, "x{empty_list*}y", "xy")
|
||||
uri_template_string (ht, "x{empty_list*|_}y", "x_y")
|
||||
uri_template_string (ht, "x{empty_list+}y", "xy")
|
||||
uri_template_string (ht, "x{empty_list+|_}y", "xempty_list._y")
|
||||
|
||||
uri_template_string (ht, "x{empty_keys}y", "xy")
|
||||
uri_template_string (ht, "x{empty_keys|_}y", "xy")
|
||||
uri_template_string (ht, "x{empty_keys*}y", "xy")
|
||||
uri_template_string (ht, "x{empty_keys*|_}y", "x_y")
|
||||
uri_template_string (ht, "x{empty_keys+}y", "xy")
|
||||
uri_template_string (ht, "x{empty_keys+|_}y", "xempty_keys._y")
|
||||
|
||||
uri_template_string (ht, "x{?name|none}", "x?name=Fred,Wilma,Pebbles")
|
||||
uri_template_string (ht, "x{?favs|none}", "x?favs=color,red,volume,high")
|
||||
uri_template_string (ht, "x{?favs*|none}", "x?color=red&volume=high")
|
||||
uri_template_string (ht, "x{?favs+|none}", "x?favs.color=red&favs.volume=high")
|
||||
|
||||
uri_template_string (ht, "x{?undef}", "x")
|
||||
uri_template_string (ht, "x{?undef|none}", "x?undef=none")
|
||||
uri_template_string (ht, "x{?empty}", "x?empty=")
|
||||
uri_template_string (ht, "x{?empty|none}", "x?empty=")
|
||||
|
||||
uri_template_string (ht, "x{?empty_list}", "x")
|
||||
uri_template_string (ht, "x{?empty_list|none}", "x?empty_list=none")
|
||||
uri_template_string (ht, "x{?empty_list*}", "x")
|
||||
uri_template_string (ht, "x{?empty_list*|none}", "x?none")
|
||||
uri_template_string (ht, "x{?empty_list+}", "x")
|
||||
uri_template_string (ht, "x{?empty_list+|none}", "x?empty_list.none")
|
||||
|
||||
uri_template_string (ht, "x{?empty_keys}", "x")
|
||||
uri_template_string (ht, "x{?empty_keys|none}", "x?empty_keys=none")
|
||||
uri_template_string (ht, "x{?empty_keys*}", "x")
|
||||
uri_template_string (ht, "x{?empty_keys*|none}", "x?none")
|
||||
uri_template_string (ht, "x{?empty_keys+}", "x")
|
||||
uri_template_string (ht, "x{?empty_keys+|none}", "x?empty_keys.none")
|
||||
|
||||
uri_template_string (ht, "x{;name|none}", "x;name=Fred,Wilma,Pebbles")
|
||||
uri_template_string (ht, "x{;favs|none}", "x;favs=color,red,volume,high")
|
||||
uri_template_string (ht, "x{;favs*|none}", "x;color=red;volume=high")
|
||||
uri_template_string (ht, "x{;favs+|none}", "x;favs.color=red;favs.volume=high")
|
||||
uri_template_string (ht, "x{;undef}", "x")
|
||||
uri_template_string (ht, "x{;undef|none}", "x;undef=none")
|
||||
uri_template_string (ht, "x{;undef|none}", "x;none")
|
||||
uri_template_string (ht, "x{;empty}", "x;empty")
|
||||
uri_template_string (ht, "x{;empty|none}", "x;empty")
|
||||
|
||||
uri_template_string (ht, "x{;empty_list}", "x")
|
||||
uri_template_string (ht, "x{;empty_list|none}", "x;empty_list=none")
|
||||
uri_template_string (ht, "x{;empty_list*}", "x")
|
||||
uri_template_string (ht, "x{;empty_list*|none}", "x;none")
|
||||
uri_template_string (ht, "x{;empty_list+}", "x")
|
||||
uri_template_string (ht, "x{;empty_list+|none}", "x;empty_list.none")
|
||||
|
||||
uri_template_string (ht, "x{;empty_keys}", "x")
|
||||
uri_template_string (ht, "x{;empty_keys|none}", "x;empty_keys=none")
|
||||
uri_template_string (ht, "x{;empty_keys*}", "x")
|
||||
uri_template_string (ht, "x{;empty_keys*|none}", "x;none")
|
||||
uri_template_string (ht, "x{;empty_keys+}", "x")
|
||||
uri_template_string (ht, "x{;empty_keys+|none}", "x;empty_keys.none")
|
||||
|
||||
uri_template_string (ht, "x{/name|none}", "x/Fred,Wilma,Pebbles")
|
||||
uri_template_string (ht, "x{/name*|none}", "x/Fred/Wilma/Pebbles")
|
||||
uri_template_string (ht, "x{/name+|none}", "x/name.Fred/name.Wilma/name.Pebbles")
|
||||
uri_template_string (ht, "x{/favs|none}", "x/color,red,volume,high")
|
||||
uri_template_string (ht, "x{/favs*|none}", "x/color/red/volume/high")
|
||||
uri_template_string (ht, "x{/favs+|none}", "x/favs.color/red/favs.volume/high")
|
||||
|
||||
uri_template_string (ht, "x{/undef}", "x")
|
||||
uri_template_string (ht, "x{/undef|none}", "x/none")
|
||||
uri_template_string (ht, "x{/empty}", "x/")
|
||||
uri_template_string (ht, "x{/empty|none}", "x/")
|
||||
|
||||
uri_template_string (ht, "x{/empty_list}", "x")
|
||||
uri_template_string (ht, "x{/empty_list|none}", "x/none")
|
||||
uri_template_string (ht, "x{/empty_list*}", "x")
|
||||
uri_template_string (ht, "x{/empty_list*|none}", "x/none")
|
||||
uri_template_string (ht, "x{/empty_list+}", "x")
|
||||
uri_template_string (ht, "x{/empty_list+|none}", "x/empty_list.none")
|
||||
|
||||
uri_template_string (ht, "x{/empty_keys}", "x")
|
||||
uri_template_string (ht, "x{/empty_keys|none}", "x/none")
|
||||
uri_template_string (ht, "x{/empty_keys*}", "x")
|
||||
uri_template_string (ht, "x{/empty_keys*|none}", "x/none")
|
||||
uri_template_string (ht, "x{/empty_keys+}", "x")
|
||||
uri_template_string (ht, "x{/empty_keys+|none}", "x/empty_keys.none")
|
||||
|
||||
--| Simple expansion with comma-separated values
|
||||
uri_template_string (ht, "{var}", "value")
|
||||
uri_template_string (ht, "{hello}", "Hello+World%%21")
|
||||
uri_template_string (ht, "{path}/here", "%%2Ffoo%%2Fbar/here")
|
||||
uri_template_string (ht, "{x,y}", "1024,768")
|
||||
uri_template_string (ht, "{var|default}", "value")
|
||||
uri_template_string (ht, "{undef|default}", "default")
|
||||
uri_template_string (ht, "{list}", "val1,val2,val3")
|
||||
uri_template_string (ht, "{list*}", "val1,val2,val3")
|
||||
uri_template_string (ht, "{list+}", "list.val1,list.val2,list.val3")
|
||||
uri_template_string (ht, "{keys}", "key1,val1,key2,val2")
|
||||
uri_template_string (ht, "{keys*}", "key1,val1,key2,val2")
|
||||
uri_template_string (ht, "{keys+}", "keys.key1,val1,keys.key2,val2")
|
||||
|
||||
--| Reserved expansion with comma-separated values
|
||||
uri_template_string (ht, "{+var}", "value")
|
||||
uri_template_string (ht, "{+hello}", "Hello+World!")
|
||||
uri_template_string (ht, "{+path}/here", "/foo/bar/here")
|
||||
uri_template_string (ht, "{+path,x}/here", "/foo/bar,1024/here")
|
||||
uri_template_string (ht, "{+path}{x}/here", "/foo/bar1024/here")
|
||||
uri_template_string (ht, "{+empty}/here", "/here")
|
||||
uri_template_string (ht, "{+undef}/here", "/here")
|
||||
uri_template_string (ht, "{+list}", "val1,val2,val3")
|
||||
uri_template_string (ht, "{+list*}", "val1,val2,val3")
|
||||
uri_template_string (ht, "{+list+}", "list.val1,list.val2,list.val3")
|
||||
uri_template_string (ht, "{+keys}", "key1,val1,key2,val2")
|
||||
uri_template_string (ht, "{+keys*}", "key1,val1,key2,val2")
|
||||
uri_template_string (ht, "{+keys+}", "keys.key1,val1,keys.key2,val2")
|
||||
|
||||
--| Path-style parameters, semicolon-prefixed
|
||||
uri_template_string (ht, "{;x,y}", ";x=1024;y=768")
|
||||
uri_template_string (ht, "{;x,y,empty}", ";x=1024;y=768;empty")
|
||||
uri_template_string (ht, "{;x,y,undef}", ";x=1024;y=768")
|
||||
uri_template_string (ht, "{;list}", ";list=val1,val2,val3") -- DIFF
|
||||
uri_template_string (ht, "{;list*}", ";val1;val2;val3")
|
||||
uri_template_string (ht, "{;list+}", ";list=val1;list=val2;list=val3")
|
||||
uri_template_string (ht, "{;keys}", ";key1,val1,key2,val2")
|
||||
uri_template_string (ht, "{;keys*}", ";key1=val1;key2=val2")
|
||||
uri_template_string (ht, "{;keys+}", ";keys.key1=val1;keys.key2=val2")
|
||||
|
||||
--| Form-style parameters, ampersand-separated
|
||||
uri_template_string (ht, "{?x,y}", "?x=1024&y=768")
|
||||
uri_template_string (ht, "{?x,y,empty}", "?x=1024&y=768&empty=")
|
||||
uri_template_string (ht, "{?x,y,undef}", "?x=1024&y=768")
|
||||
uri_template_string (ht, "{?list}", "?list=val1,val2,val3")
|
||||
uri_template_string (ht, "{?list*}", "?val1&val2&val3")
|
||||
uri_template_string (ht, "{?list+}", "?list=val1&list=val2&list=val3")
|
||||
uri_template_string (ht, "{?keys}", "?keys=key1,val1,key2,val2")
|
||||
uri_template_string (ht, "{?keys*}", "?key1=val1&key2=val2")
|
||||
uri_template_string (ht, "{?keys+}", "?keys.key1=val1&keys.key2=val2")
|
||||
|
||||
--| Hierarchical path segments, slash-separated
|
||||
uri_template_string (ht, "{/var}", "/value")
|
||||
uri_template_string (ht, "{/var,empty}", "/value/")
|
||||
uri_template_string (ht, "{/var,undef}", "/value")
|
||||
uri_template_string (ht, "{/list}", "/val1,val2,val3")
|
||||
uri_template_string (ht, "{/list*}", "/val1/val2/val3")
|
||||
uri_template_string (ht, "{/list*,x}", "/val1/val2/val3/1024")
|
||||
uri_template_string (ht, "{/list+}", "/list.val1/list.val2/list.val3")
|
||||
uri_template_string (ht, "{/keys}", "/key1,val1,key2,val2")
|
||||
uri_template_string (ht, "{/keys*}", "/key1/val1/key2/val2")
|
||||
uri_template_string (ht, "{/keys+}", "/keys.key1/val1/keys.key2/val2")
|
||||
|
||||
--| Label expansion, dot-prefixed
|
||||
uri_template_string (ht, "X{.var}", "X.value")
|
||||
uri_template_string (ht, "X{.empty}", "X.")
|
||||
uri_template_string (ht, "X{.undef}", "X")
|
||||
uri_template_string (ht, "X{.list}", "X.val1,val2,val3")
|
||||
uri_template_string (ht, "X{.list*}", "X.val1.val2.val3")
|
||||
uri_template_string (ht, "X{.list*,x}", "X.val1.val2.val3.1024")
|
||||
uri_template_string (ht, "X{.list+}", "X.list.val1.list.val2.list.val3")
|
||||
uri_template_string (ht, "X{.keys}", "X.key1,val1,key2,val2")
|
||||
uri_template_string (ht, "X{.keys*}", "X.key1.val1.key2.val2")
|
||||
uri_template_string (ht, "X{.keys+}", "X.keys.key1.val1.keys.key2.val2")
|
||||
|
||||
--| Simple Expansion
|
||||
uri_template_string (ht, "{foo}", "fred")
|
||||
uri_template_string (ht, "{foo,foo}", "fred,fred")
|
||||
uri_template_string (ht, "{bar,foo}", "fred")
|
||||
uri_template_string (ht, "{bar|wilma}", "wilma")
|
||||
|
||||
--| Reserved Expansion
|
||||
uri_template_string (ht, "{foo2}", "That%%27s+right%%21")
|
||||
uri_template_string (ht, "{+foo2}", "That%%27s+right!")
|
||||
uri_template_string (ht, "{base}index", "http%%3A%%2F%%2Fexample.com%%2Fhome%%2Findex")
|
||||
uri_template_string (ht, "{+base}index", "http://example.com/home/index")
|
||||
|
||||
assert ("all strings built", uri_template_string_errors = Void or (attached uri_template_string_errors as err and then err.is_empty))
|
||||
end
|
||||
|
||||
uri_template_string (a_ht: HASH_TABLE [detachable ANY, STRING]; a_expression: STRING; a_expected: STRING)
|
||||
local
|
||||
tpl: URI_TEMPLATE
|
||||
s: STRING
|
||||
m: STRING
|
||||
do
|
||||
create tpl.make (a_expression)
|
||||
s := tpl.expanded_string (a_ht)
|
||||
if not s.same_string (a_expected) then
|
||||
m := "Expected string for %"" + a_expression + "%" expected=%""+ a_expected +"%" but got %"" + s + "%"%N"
|
||||
if attached uri_template_string_errors as err then
|
||||
print (m)
|
||||
err.force (m)
|
||||
else
|
||||
assert (m, False)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
uri_template_parse (s: STRING_8; path_vars: ARRAY [STRING]; query_vars: ARRAY [STRING])
|
||||
local
|
||||
u: URI_TEMPLATE
|
||||
matched: BOOLEAN
|
||||
i: INTEGER
|
||||
do
|
||||
create u.make (s)
|
||||
if attached u.path_variable_names as vars then
|
||||
matched := vars.count = path_vars.count
|
||||
from
|
||||
i := path_vars.lower
|
||||
vars.start
|
||||
until
|
||||
not matched or i > path_vars.upper
|
||||
loop
|
||||
matched := vars.item.same_string (path_vars[i])
|
||||
vars.forth
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
matched := path_vars.is_empty
|
||||
end
|
||||
assert ("path variables matched", matched)
|
||||
|
||||
if attached u.query_variable_names as vars then
|
||||
matched := vars.count = query_vars.count
|
||||
from
|
||||
i := query_vars.lower
|
||||
vars.start
|
||||
until
|
||||
not matched or i > query_vars.upper
|
||||
loop
|
||||
matched := vars.item.same_string (query_vars[i])
|
||||
vars.forth
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
matched := query_vars.is_empty
|
||||
end
|
||||
assert ("query variables matched", matched)
|
||||
end
|
||||
|
||||
uri_template_mismatch (a_uri_template: URI_TEMPLATE; a_uri: STRING)
|
||||
local
|
||||
l_match: detachable URI_TEMPLATE_MATCH_RESULT
|
||||
do
|
||||
l_match := a_uri_template.match (a_uri)
|
||||
assert ("uri %"" + a_uri + "%" does not match template %"" + a_uri_template.template + "%"", l_match = Void)
|
||||
end
|
||||
|
||||
uri_template_match (a_uri_template: URI_TEMPLATE; a_uri: STRING; path_res: ARRAY [TUPLE [name: STRING; value: STRING]]; query_res: ARRAY [TUPLE [name: STRING; value: STRING]])
|
||||
local
|
||||
b: BOOLEAN
|
||||
i: INTEGER
|
||||
l_match: detachable URI_TEMPLATE_MATCH_RESULT
|
||||
do
|
||||
l_match := a_uri_template.match (a_uri)
|
||||
if l_match /= Void then
|
||||
if attached l_match.path_variables as path_ht then
|
||||
b := path_ht.count = path_res.count
|
||||
from
|
||||
i := path_res.lower
|
||||
until
|
||||
not b or i > path_res.upper
|
||||
loop
|
||||
b := attached path_ht.item (path_res[i].name) as s and then s.same_string (path_res[i].value)
|
||||
i := i + 1
|
||||
end
|
||||
assert ("uri matched path variables", b)
|
||||
end
|
||||
if attached l_match.query_variables as query_ht then
|
||||
b := query_ht.count >= query_res.count
|
||||
from
|
||||
i := query_res.lower
|
||||
until
|
||||
not b or i > query_res.upper
|
||||
loop
|
||||
b := attached query_ht.item (query_res[i].name) as s and then s.same_string (query_res[i].value)
|
||||
i := i + 1
|
||||
end
|
||||
assert ("uri matched query variables", b)
|
||||
end
|
||||
else
|
||||
assert ("uri matched", False)
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,670 @@
|
||||
note
|
||||
description: "[
|
||||
Eiffel tests that can be executed by testing tool.
|
||||
]"
|
||||
author: "EiffelStudio test wizard"
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
testing: "type/manual"
|
||||
|
||||
class
|
||||
TEST_URI_TEMPLATE
|
||||
|
||||
inherit
|
||||
EQA_TEST_SET
|
||||
|
||||
feature -- Test routines
|
||||
|
||||
test_uri_template_parser
|
||||
note
|
||||
testing: "uri-template"
|
||||
do
|
||||
uri_template_parse ("api/foo/{foo_id}/{?id,extra}", <<"foo_id">>, <<"id", "extra">>)
|
||||
uri_template_parse ("weather/{state}/{city}?forecast={day}", <<"state", "city">>, <<"day">>)
|
||||
uri_template_parse ("/hello/{name}.{format}", <<"name", "format">>, <<>>)
|
||||
uri_template_parse ("/hello.{format}/{name}", <<"format", "name">>, <<>>)
|
||||
uri_template_parse ("/hello/Joce.{format}/foo{?foobar};crazy=IDEA", <<"name">>, <<"foobar">>)
|
||||
end
|
||||
|
||||
test_uri_template_matcher
|
||||
note
|
||||
testing: "uri-template"
|
||||
local
|
||||
tpl: URI_TEMPLATE
|
||||
do
|
||||
create tpl.make ("{version}/{id}")
|
||||
uri_template_match (tpl, "v2/123", <<["version", "v2"], ["id" , "123"]>>, <<>>)
|
||||
|
||||
create tpl.make ("api/{foo}{bar}/id/{id}")
|
||||
uri_template_mismatch (tpl, "api/foobar/id/123")
|
||||
|
||||
|
||||
create tpl.make ("api/foo/{foo_id}/{?id,extra}")
|
||||
uri_template_match (tpl, "api/foo/bar/", <<["foo_id", "bar"]>>, <<>>)
|
||||
uri_template_match (tpl, "api/foo/bar/?id=123", <<["foo_id", "bar"]>>, <<["id", "123"]>>)
|
||||
uri_template_match (tpl, "api/foo/bar/?id=123&extra=test", <<["foo_id", "bar"]>>, <<["id", "123"], ["extra", "test"]>>)
|
||||
uri_template_match (tpl, "api/foo/bar/?id=123&extra=test&one=more", <<["foo_id", "bar"]>>, <<["id", "123"], ["extra", "test"]>>)
|
||||
uri_template_mismatch (tpl, "")
|
||||
uri_template_mismatch (tpl, "/")
|
||||
uri_template_mismatch (tpl, "foo/bar/?id=123")
|
||||
uri_template_mismatch (tpl, "/api/foo/bar/")
|
||||
uri_template_mismatch (tpl, "api/foo/bar")
|
||||
|
||||
create tpl.make ("weather/{state}/{city}?forecast={day}")
|
||||
uri_template_match (tpl, "weather/California/Goleta?forecast=today", <<["state", "California"], ["city", "Goleta"]>>, <<["day", "today"]>>)
|
||||
|
||||
create tpl.make ("/hello")
|
||||
uri_template_match (tpl, "/hello", <<>>, <<>>)
|
||||
uri_template_mismatch (tpl, "/hello/Foo2") -- longer
|
||||
uri_template_mismatch (tpl, "/hell") -- shorter
|
||||
|
||||
create tpl.make ("/hello.{format}")
|
||||
uri_template_match (tpl, "/hello.xml", <<["format", "xml"]>>, <<>>)
|
||||
uri_template_mismatch (tpl, "/hello.xml/Bar")
|
||||
|
||||
|
||||
create tpl.make ("/hello.{format}/{name}")
|
||||
uri_template_match (tpl, "/hello.xml/Joce", <<["format", "xml"], ["name", "Joce"]>>, <<>>)
|
||||
|
||||
create tpl.make ("/hello/{name}.{format}")
|
||||
uri_template_match (tpl, "/hello/Joce.json", <<["name", "Joce"], ["format", "json"]>>, <<>>)
|
||||
|
||||
create tpl.make ("/hello/{name}.{format}/foo")
|
||||
uri_template_match (tpl, "/hello/Joce.xml/foo", <<["name", "Joce"], ["format", "xml"]>>, <<>>)
|
||||
uri_template_mismatch (tpl, "/hello/Joce.xml/fooBAR")
|
||||
|
||||
create tpl.make ("/hello/{name}.{format}/foo{?foo};crazy={idea}")
|
||||
-- uri_template_match (tpl, "/hello/Joce.xml/foo", <<["name", "Joce"], ["format", "xml"]>>, <<>>)
|
||||
uri_template_match (tpl, "/hello/Joce.xml/foo?foo=FOO", <<["name", "Joce"], ["format", "xml"]>>, <<["foo", "FOO"]>>)
|
||||
uri_template_match (tpl, "/hello/Joce.xml/foo;crazy=IDEA", <<["name", "Joce"], ["format", "xml"]>>, <<["idea", "IDEA"], ["crazy", "IDEA"]>>)
|
||||
|
||||
end
|
||||
|
||||
uri_template_string_errors: detachable LIST [STRING]
|
||||
|
||||
test_uri_template_string_builder
|
||||
note
|
||||
testing: "uri-template"
|
||||
local
|
||||
ht: HASH_TABLE [detachable ANY, STRING]
|
||||
empty_keys: HASH_TABLE [STRING, STRING]
|
||||
empty_list: ARRAY [STRING]
|
||||
favs: HASH_TABLE [detachable ANY, STRING]
|
||||
keys: HASH_TABLE [STRING, STRING]
|
||||
colors: ARRAY [STRING]
|
||||
names: ARRAY [STRING]
|
||||
semi_dot: HASH_TABLE [STRING, STRING]
|
||||
vals: ARRAY [STRING]
|
||||
do
|
||||
create ht.make (3)
|
||||
ht.force ("FooBar", "foo_id")
|
||||
ht.force ("That's right!", "extra")
|
||||
ht.force ("123", "id")
|
||||
ht.force ("California", "state")
|
||||
ht.force ("Goleta", "city")
|
||||
ht.force ("today", "day")
|
||||
|
||||
ht.force ("value", "var")
|
||||
ht.force ("Hello World!", "hello")
|
||||
ht.force ("", "empty")
|
||||
ht.force ("/foo/bar", "path")
|
||||
ht.force ("1024", "x")
|
||||
ht.force ("768", "y")
|
||||
ht.force ("fred", "foo")
|
||||
ht.force ("That's right!", "foo2")
|
||||
ht.force ("http://example.com/home/", "base")
|
||||
|
||||
names := <<"Fred", "Wilma", "Pebbles">>
|
||||
ht.force (names, "name")
|
||||
create favs.make (2)
|
||||
favs.force ("red", "color")
|
||||
favs.force ("high", "volume")
|
||||
ht.force (favs, "favs")
|
||||
|
||||
create empty_list.make_empty
|
||||
ht.force (empty_list,"empty_list")
|
||||
|
||||
create empty_keys.make (0)
|
||||
ht.force (empty_keys,"empty_keys")
|
||||
|
||||
vals := <<"val1", "val2", "val3">>
|
||||
ht.force (vals, "list")
|
||||
create keys.make (2)
|
||||
keys.force ("val1", "key1")
|
||||
keys.force ("val2", "key2")
|
||||
ht.force (keys, "keys")
|
||||
|
||||
colors := <<"red", "green", "blue">>
|
||||
create semi_dot.make (3)
|
||||
semi_dot.force (";", "semi")
|
||||
semi_dot.force (".", "dot")
|
||||
semi_dot.force (",", "comma")
|
||||
|
||||
create {ARRAYED_LIST [STRING]} uri_template_string_errors.make (10)
|
||||
|
||||
|
||||
--| Simple string expansion
|
||||
uri_template_string (ht, "{var}", "value")
|
||||
uri_template_string (ht, "{hello}", "Hello+World%%21")
|
||||
uri_template_string (ht, "O{empty}X", "OX")
|
||||
uri_template_string (ht, "O{undef}X", "OX")
|
||||
|
||||
--| String expansion with defaults
|
||||
uri_template_string (ht, "{var=default}", "value")
|
||||
uri_template_string (ht, "O{empty=default}X", "OX")
|
||||
uri_template_string (ht, "O{undef=default}X", "OdefaultX")
|
||||
|
||||
--| Reserved expansion with defaults
|
||||
uri_template_string (ht, "{+var}", "value")
|
||||
uri_template_string (ht, "{+hello}", "Hello+World!")
|
||||
uri_template_string (ht, "{+path}/here", "/foo/bar/here")
|
||||
uri_template_string (ht, "here?ref={+path}", "here?ref=/foo/bar")
|
||||
uri_template_string (ht, "up{+path}{x}/here", "up/foo/bar1024/here")
|
||||
uri_template_string (ht, "up{+empty=/1}/here", "up/here")
|
||||
uri_template_string (ht, "up{+undef=/1}/here", "up/1/here")
|
||||
|
||||
--| String expansion with multiple variables
|
||||
uri_template_string (ht, "{x,y}", "1024,768")
|
||||
uri_template_string (ht, "{x,hello,y}", "1024,Hello+World%%21,768")
|
||||
uri_template_string (ht, "?{x,empty}", "?1024,")
|
||||
uri_template_string (ht, "?{x,undef}", "?1024")
|
||||
uri_template_string (ht, "?{undef,y}", "?768")
|
||||
uri_template_string (ht, "?{x,undef=0}", "?1024,0")
|
||||
|
||||
--| Reserved expansion with multiple variables
|
||||
uri_template_string (ht, "{+x,hello,y}", "1024,Hello+World!,768")
|
||||
uri_template_string (ht, "{+path,x}/here", "/foo/bar,1024/here")
|
||||
--| Label expansion, dot-prefixed
|
||||
uri_template_string (ht, "X{.var}", "X.value")
|
||||
uri_template_string (ht, "X{.empty}", "X.")
|
||||
uri_template_string (ht, "X{.undef}", "X")
|
||||
|
||||
--| Path segments, slash-prefixed
|
||||
uri_template_string (ht, "{/var}", "/value")
|
||||
uri_template_string (ht, "{/var,empty}", "/value/")
|
||||
uri_template_string (ht, "{/var,undef}", "/value")
|
||||
|
||||
--| Path-style parameters, semicolon-prefixed
|
||||
uri_template_string (ht, "{;x,y}", ";x=1024;y=768")
|
||||
uri_template_string (ht, "{;x,y,empty}", ";x=1024;y=768;empty")
|
||||
uri_template_string (ht, "{;x,y,undef}", ";x=1024;y=768")
|
||||
|
||||
--| Form-style query, ampersand-separated
|
||||
uri_template_string (ht, "{?x,y}", "?x=1024&y=768")
|
||||
uri_template_string (ht, "{?x,y,empty}", "?x=1024&y=768&empty=")
|
||||
uri_template_string (ht, "{?x,y,undef}", "?x=1024&y=768")
|
||||
|
||||
|
||||
ht.force (colors, "list")
|
||||
ht.force (semi_dot, "keys")
|
||||
--| String expansion with value modifiers
|
||||
uri_template_string (ht, "{var:3}", "val")
|
||||
uri_template_string (ht, "{var:30}", "value")
|
||||
uri_template_string (ht, "{var^3}", "ue")
|
||||
uri_template_string (ht, "{var^-3}", "va")
|
||||
|
||||
uri_template_string (ht, "{list}", "red,green,blue")
|
||||
uri_template_string (ht, "{list*}", "red,green,blue")
|
||||
uri_template_string (ht, "{keys}", "semi,%%3B,dot,.,comma,%%2C")
|
||||
uri_template_string (ht, "{keys*}", "semi=%%3B,dot=.,comma=%%2C")
|
||||
--| Reserved expansion with value modifiers
|
||||
uri_template_string (ht, "{+path:6}/here", "/foo/b/here")
|
||||
uri_template_string (ht, "{+list}", "red,green,blue")
|
||||
uri_template_string (ht, "{+list*}", "red,green,blue")
|
||||
uri_template_string (ht, "{+keys}", "semi,;,dot,.,comma,,")
|
||||
uri_template_string (ht, "{+keys*}", "semi=;,dot=.,comma=,")
|
||||
--| Label expansion, dot-prefixed
|
||||
uri_template_string (ht, "X{.var:3}", "X.val")
|
||||
uri_template_string (ht, "X{.list}", "X.red,green,blue")
|
||||
uri_template_string (ht, "X{.list*}", "X.red.green.blue")
|
||||
uri_template_string (ht, "X{.keys}", "X.semi,%%3B,dot,.,comma,%%2C")
|
||||
uri_template_string (ht, "X{.keys*}", "X.semi=%%3B.dot=..comma=%%2C")
|
||||
|
||||
--| Path segments, slash-prefixed
|
||||
uri_template_string (ht, "{/var:1,var}", "/v/value")
|
||||
uri_template_string (ht, "{/list}", "/red,green,blue")
|
||||
uri_template_string (ht, "{/list*}", "/red/green/blue")
|
||||
uri_template_string (ht, "{/list*,path:4}", "/red/green/blue/%%2Ffoo")
|
||||
uri_template_string (ht, "{/keys}", "/semi,%%3B,dot,.,comma,%%2C")
|
||||
uri_template_string (ht, "{/keys*}", "/semi=%%3B/dot=./comma=%%2C")
|
||||
|
||||
--| Path-style parameters, semicolon-prefixed
|
||||
uri_template_string (ht, "{;hello:5}", ";hello=Hello")
|
||||
uri_template_string (ht, "{;list}", ";red,green,blue")
|
||||
uri_template_string (ht, "{;list*}", ";red;green;blue")
|
||||
uri_template_string (ht, "{;keys}", ";semi,%%3B,dot,.,comma,%%2C")
|
||||
uri_template_string (ht, "{;keys*}", ";semi=%%3B;dot=.;comma=%%2C")
|
||||
|
||||
--| Form-style query, ampersand-separated
|
||||
uri_template_string (ht, "{?var:3}", "?var=val")
|
||||
uri_template_string (ht, "{?list}", "?list=red,green,blue")
|
||||
uri_template_string (ht, "{?list*}", "?list=red&list=green&list=blue")
|
||||
uri_template_string (ht, "{?keys}", "?keys=semi,%%3B,dot,.,comma,%%2C")
|
||||
uri_template_string (ht, "{?keys*}", "?semi=%%3B&dot=.&comma=%%2C")
|
||||
|
||||
assert ("all strings built", uri_template_string_errors = Void or (attached uri_template_string_errors as err and then err.is_empty))
|
||||
end
|
||||
|
||||
|
||||
test_uri_template_string_builder_extra
|
||||
note
|
||||
testing: "uri-template"
|
||||
local
|
||||
ht: HASH_TABLE [detachable ANY, STRING]
|
||||
empty_keys: HASH_TABLE [STRING, STRING]
|
||||
empty_list: ARRAY [STRING]
|
||||
favs: HASH_TABLE [detachable ANY, STRING]
|
||||
keys: HASH_TABLE [STRING, STRING]
|
||||
colors: ARRAY [STRING]
|
||||
names: ARRAY [STRING]
|
||||
semi_dot: HASH_TABLE [STRING, STRING]
|
||||
vals: ARRAY [STRING]
|
||||
do
|
||||
create ht.make (3)
|
||||
ht.force ("FooBar", "foo_id")
|
||||
ht.force ("That's right!", "extra")
|
||||
ht.force ("123", "id")
|
||||
ht.force ("California", "state")
|
||||
ht.force ("Goleta", "city")
|
||||
ht.force ("today", "day")
|
||||
|
||||
ht.force ("value", "var")
|
||||
ht.force ("Hello World!", "hello")
|
||||
ht.force ("", "empty")
|
||||
ht.force ("/foo/bar", "path")
|
||||
ht.force ("1024", "x")
|
||||
ht.force ("768", "y")
|
||||
ht.force ("fred", "foo")
|
||||
ht.force ("That's right!", "foo2")
|
||||
ht.force ("http://example.com/home/", "base")
|
||||
|
||||
names := <<"Fred", "Wilma", "Pebbles">>
|
||||
ht.force (names, "name")
|
||||
create favs.make (2)
|
||||
favs.force ("red", "color")
|
||||
favs.force ("high", "volume")
|
||||
ht.force (favs, "favs")
|
||||
|
||||
create empty_list.make_empty
|
||||
ht.force (empty_list,"empty_list")
|
||||
|
||||
create empty_keys.make (0)
|
||||
ht.force (empty_keys,"empty_keys")
|
||||
|
||||
vals := <<"val1", "val2", "val3">>
|
||||
ht.force (vals, "list")
|
||||
create keys.make (2)
|
||||
keys.force ("val1", "key1")
|
||||
keys.force ("val2", "key2")
|
||||
ht.force (keys, "keys")
|
||||
|
||||
colors := <<"red", "green", "blue">>
|
||||
create semi_dot.make (3)
|
||||
semi_dot.force (";", "semi")
|
||||
semi_dot.force (".", "dot")
|
||||
semi_dot.force (",", "comma")
|
||||
|
||||
create {ARRAYED_LIST [STRING]} uri_template_string_errors.make (10)
|
||||
|
||||
--| Addition to the spec
|
||||
uri_template_string (ht, "api/foo/{foo_id}/{?id,extra}",
|
||||
"api/foo/FooBar/?id=123&extra=That%%27s+right%%21")
|
||||
|
||||
uri_template_string (ht, "api/foo/{foo_id}/{?id,empty,undef,extra}",
|
||||
"api/foo/FooBar/?id=123&empty=&extra=That%%27s+right%%21")
|
||||
|
||||
uri_template_string (ht, "weather/{state}/{city}?forecast={day}",
|
||||
"weather/California/Goleta?forecast=today")
|
||||
|
||||
|
||||
uri_template_string (ht, "{var=default}", "value")
|
||||
uri_template_string (ht, "{undef=default}", "default")
|
||||
uri_template_string (ht, "{undef:3=default}", "default")
|
||||
|
||||
uri_template_string (ht, "x{empty}y", "xy")
|
||||
uri_template_string (ht, "x{empty=_}y", "xy")
|
||||
uri_template_string (ht, "x{undef}y", "xy")
|
||||
uri_template_string (ht, "x{undef=_}y", "x_y")
|
||||
|
||||
uri_template_string (ht, "x{.name=none}", "x.Fred,Wilma,Pebbles")
|
||||
uri_template_string (ht, "x{.name*=none}", "x.Fred.Wilma.Pebbles")
|
||||
uri_template_string (ht, "x{.empty}", "x.")
|
||||
uri_template_string (ht, "x{.empty=none}", "x.")
|
||||
uri_template_string (ht, "x{.undef}", "x")
|
||||
uri_template_string (ht, "x{.undef=none}", "x.none")
|
||||
|
||||
uri_template_string (ht, "x{/name=none}", "x/Fred,Wilma,Pebbles")
|
||||
uri_template_string (ht, "x{/name*=none}", "x/Fred/Wilma/Pebbles")
|
||||
uri_template_string (ht, "x{/undef}", "x")
|
||||
uri_template_string (ht, "x{/undef=none}", "x/none")
|
||||
uri_template_string (ht, "x{/empty}", "x/")
|
||||
uri_template_string (ht, "x{/empty=none}", "x/")
|
||||
uri_template_string (ht, "x{/empty_keys}", "x")
|
||||
uri_template_string (ht, "x{/empty_keys=none}", "x/none")
|
||||
uri_template_string (ht, "x{/empty_keys*}", "x")
|
||||
uri_template_string (ht, "x{/empty_keys*=none}", "x/none")
|
||||
|
||||
uri_template_string (ht, "x{;name=none}", "x;name=Fred,Wilma,Pebbles")
|
||||
uri_template_string (ht, "x{;favs=none}", "x;favs=color,red,volume,high")
|
||||
uri_template_string (ht, "x{;favs*=none}", "x;color=red;volume=high")
|
||||
uri_template_string (ht, "x{;empty}", "x;empty")
|
||||
uri_template_string (ht, "x{;empty=none}", "x;empty")
|
||||
|
||||
uri_template_string (ht, "x{;undef}", "x")
|
||||
uri_template_string (ht, "x{;undef=none}", "x;none")
|
||||
uri_template_string (ht, "x{;undef=foo=y}", "x;foo=y")
|
||||
|
||||
uri_template_string (ht, "x{?var=none}", "x?var=value")
|
||||
uri_template_string (ht, "x{?favs=none}", "x?favs=color,red,volume,high")
|
||||
uri_template_string (ht, "x{?favs*=none}", "x?color=red&volume=high")
|
||||
uri_template_string (ht, "x{?empty}", "x?empty=")
|
||||
uri_template_string (ht, "x{?empty=foo=none}", "x?empty=")
|
||||
uri_template_string (ht, "x{?undef}", "x")
|
||||
uri_template_string (ht, "x{?undef=foo=none}", "x?foo=none")
|
||||
uri_template_string (ht, "x{?empty_keys}", "x")
|
||||
uri_template_string (ht, "x{?empty_keys=none}", "x?none")
|
||||
uri_template_string (ht, "x{?empty_keys=y=z}", "x?y=z")
|
||||
uri_template_string (ht, "x{?empty_keys*=y=z}", "x?y=z")
|
||||
|
||||
|
||||
------
|
||||
|
||||
uri_template_string (ht, "x{empty_list}y", "xy")
|
||||
uri_template_string (ht, "x{empty_list=_}y", "xy")
|
||||
uri_template_string (ht, "x{empty_list*}y", "xy")
|
||||
uri_template_string (ht, "x{empty_list*=_}y", "x_y")
|
||||
uri_template_string (ht, "x{empty_list+}y", "xy")
|
||||
uri_template_string (ht, "x{empty_list+=_}y", "xempty_list._y")
|
||||
|
||||
uri_template_string (ht, "x{empty_keys}y", "xy")
|
||||
uri_template_string (ht, "x{empty_keys=_}y", "xy")
|
||||
uri_template_string (ht, "x{empty_keys*}y", "xy")
|
||||
uri_template_string (ht, "x{empty_keys*=_}y", "x_y")
|
||||
uri_template_string (ht, "x{empty_keys+}y", "xy")
|
||||
uri_template_string (ht, "x{empty_keys+=_}y", "xempty_keys._y")
|
||||
|
||||
uri_template_string (ht, "x{?name=none}", "x?name=Fred,Wilma,Pebbles")
|
||||
uri_template_string (ht, "x{?favs=none}", "x?favs=color,red,volume,high")
|
||||
uri_template_string (ht, "x{?favs*=none}", "x?color=red&volume=high")
|
||||
uri_template_string (ht, "x{?favs+=none}", "x?favs.color=red&favs.volume=high")
|
||||
|
||||
uri_template_string (ht, "x{?undef}", "x")
|
||||
uri_template_string (ht, "x{?undef=none}", "x?undef=none")
|
||||
uri_template_string (ht, "x{?empty}", "x?empty=")
|
||||
uri_template_string (ht, "x{?empty=none}", "x?empty=")
|
||||
|
||||
uri_template_string (ht, "x{?empty_list}", "x")
|
||||
uri_template_string (ht, "x{?empty_list=none}", "x?empty_list=none")
|
||||
uri_template_string (ht, "x{?empty_list*}", "x")
|
||||
uri_template_string (ht, "x{?empty_list*=none}", "x?none")
|
||||
uri_template_string (ht, "x{?empty_list+}", "x")
|
||||
uri_template_string (ht, "x{?empty_list+=none}", "x?empty_list.none")
|
||||
|
||||
uri_template_string (ht, "x{?empty_keys}", "x")
|
||||
uri_template_string (ht, "x{?empty_keys=none}", "x?empty_keys=none")
|
||||
uri_template_string (ht, "x{?empty_keys*}", "x")
|
||||
uri_template_string (ht, "x{?empty_keys*=none}", "x?none")
|
||||
uri_template_string (ht, "x{?empty_keys+}", "x")
|
||||
uri_template_string (ht, "x{?empty_keys+=none}", "x?empty_keys.none")
|
||||
|
||||
uri_template_string (ht, "x{;name=none}", "x;name=Fred,Wilma,Pebbles")
|
||||
uri_template_string (ht, "x{;favs=none}", "x;favs=color,red,volume,high")
|
||||
uri_template_string (ht, "x{;favs*=none}", "x;color=red;volume=high")
|
||||
uri_template_string (ht, "x{;favs+=none}", "x;favs.color=red;favs.volume=high")
|
||||
uri_template_string (ht, "x{;undef}", "x")
|
||||
uri_template_string (ht, "x{;undef=none}", "x;undef=none")
|
||||
uri_template_string (ht, "x{;undef=none}", "x;none")
|
||||
uri_template_string (ht, "x{;empty}", "x;empty")
|
||||
uri_template_string (ht, "x{;empty=none}", "x;empty")
|
||||
|
||||
uri_template_string (ht, "x{;empty_list}", "x")
|
||||
uri_template_string (ht, "x{;empty_list=none}", "x;empty_list=none")
|
||||
uri_template_string (ht, "x{;empty_list*}", "x")
|
||||
uri_template_string (ht, "x{;empty_list*=none}", "x;none")
|
||||
uri_template_string (ht, "x{;empty_list+}", "x")
|
||||
uri_template_string (ht, "x{;empty_list+=none}", "x;empty_list.none")
|
||||
|
||||
uri_template_string (ht, "x{;empty_keys}", "x")
|
||||
uri_template_string (ht, "x{;empty_keys=none}", "x;empty_keys=none")
|
||||
uri_template_string (ht, "x{;empty_keys*}", "x")
|
||||
uri_template_string (ht, "x{;empty_keys*=none}", "x;none")
|
||||
uri_template_string (ht, "x{;empty_keys+}", "x")
|
||||
uri_template_string (ht, "x{;empty_keys+=none}", "x;empty_keys.none")
|
||||
|
||||
uri_template_string (ht, "x{/name=none}", "x/Fred,Wilma,Pebbles")
|
||||
uri_template_string (ht, "x{/name*=none}", "x/Fred/Wilma/Pebbles")
|
||||
uri_template_string (ht, "x{/name+=none}", "x/name.Fred/name.Wilma/name.Pebbles")
|
||||
uri_template_string (ht, "x{/favs=none}", "x/color,red,volume,high")
|
||||
uri_template_string (ht, "x{/favs*=none}", "x/color/red/volume/high")
|
||||
uri_template_string (ht, "x{/favs+=none}", "x/favs.color/red/favs.volume/high")
|
||||
|
||||
uri_template_string (ht, "x{/undef}", "x")
|
||||
uri_template_string (ht, "x{/undef=none}", "x/none")
|
||||
uri_template_string (ht, "x{/empty}", "x/")
|
||||
uri_template_string (ht, "x{/empty=none}", "x/")
|
||||
|
||||
uri_template_string (ht, "x{/empty_list}", "x")
|
||||
uri_template_string (ht, "x{/empty_list=none}", "x/none")
|
||||
uri_template_string (ht, "x{/empty_list*}", "x")
|
||||
uri_template_string (ht, "x{/empty_list*=none}", "x/none")
|
||||
uri_template_string (ht, "x{/empty_list+}", "x")
|
||||
uri_template_string (ht, "x{/empty_list+=none}", "x/empty_list.none")
|
||||
|
||||
uri_template_string (ht, "x{/empty_keys}", "x")
|
||||
uri_template_string (ht, "x{/empty_keys=none}", "x/none")
|
||||
uri_template_string (ht, "x{/empty_keys*}", "x")
|
||||
uri_template_string (ht, "x{/empty_keys*=none}", "x/none")
|
||||
uri_template_string (ht, "x{/empty_keys+}", "x")
|
||||
uri_template_string (ht, "x{/empty_keys+=none}", "x/empty_keys.none")
|
||||
|
||||
--| Simple expansion with comma-separated values
|
||||
uri_template_string (ht, "{var}", "value")
|
||||
uri_template_string (ht, "{hello}", "Hello+World%%21")
|
||||
uri_template_string (ht, "{path}/here", "%%2Ffoo%%2Fbar/here")
|
||||
uri_template_string (ht, "{x,y}", "1024,768")
|
||||
uri_template_string (ht, "{var=default}", "value")
|
||||
uri_template_string (ht, "{undef=default}", "default")
|
||||
uri_template_string (ht, "{list}", "val1,val2,val3")
|
||||
uri_template_string (ht, "{list*}", "val1,val2,val3")
|
||||
uri_template_string (ht, "{list+}", "list.val1,list.val2,list.val3")
|
||||
uri_template_string (ht, "{keys}", "key1,val1,key2,val2")
|
||||
uri_template_string (ht, "{keys*}", "key1,val1,key2,val2")
|
||||
uri_template_string (ht, "{keys+}", "keys.key1,val1,keys.key2,val2")
|
||||
|
||||
--| Reserved expansion with comma-separated values
|
||||
uri_template_string (ht, "{+var}", "value")
|
||||
uri_template_string (ht, "{+hello}", "Hello+World!")
|
||||
uri_template_string (ht, "{+path}/here", "/foo/bar/here")
|
||||
uri_template_string (ht, "{+path,x}/here", "/foo/bar,1024/here")
|
||||
uri_template_string (ht, "{+path}{x}/here", "/foo/bar1024/here")
|
||||
uri_template_string (ht, "{+empty}/here", "/here")
|
||||
uri_template_string (ht, "{+undef}/here", "/here")
|
||||
uri_template_string (ht, "{+list}", "val1,val2,val3")
|
||||
uri_template_string (ht, "{+list*}", "val1,val2,val3")
|
||||
uri_template_string (ht, "{+list+}", "list.val1,list.val2,list.val3")
|
||||
uri_template_string (ht, "{+keys}", "key1,val1,key2,val2")
|
||||
uri_template_string (ht, "{+keys*}", "key1,val1,key2,val2")
|
||||
uri_template_string (ht, "{+keys+}", "keys.key1,val1,keys.key2,val2")
|
||||
|
||||
--| Path-style parameters, semicolon-prefixed
|
||||
uri_template_string (ht, "{;x,y}", ";x=1024;y=768")
|
||||
uri_template_string (ht, "{;x,y,empty}", ";x=1024;y=768;empty")
|
||||
uri_template_string (ht, "{;x,y,undef}", ";x=1024;y=768")
|
||||
uri_template_string (ht, "{;list}", ";list=val1,val2,val3") -- DIFF
|
||||
uri_template_string (ht, "{;list*}", ";val1;val2;val3")
|
||||
uri_template_string (ht, "{;list+}", ";list=val1;list=val2;list=val3")
|
||||
uri_template_string (ht, "{;keys}", ";key1,val1,key2,val2")
|
||||
uri_template_string (ht, "{;keys*}", ";key1=val1;key2=val2")
|
||||
uri_template_string (ht, "{;keys+}", ";keys.key1=val1;keys.key2=val2")
|
||||
|
||||
--| Form-style parameters, ampersand-separated
|
||||
uri_template_string (ht, "{?x,y}", "?x=1024&y=768")
|
||||
uri_template_string (ht, "{?x,y,empty}", "?x=1024&y=768&empty=")
|
||||
uri_template_string (ht, "{?x,y,undef}", "?x=1024&y=768")
|
||||
uri_template_string (ht, "{?list}", "?list=val1,val2,val3")
|
||||
uri_template_string (ht, "{?list*}", "?val1&val2&val3")
|
||||
uri_template_string (ht, "{?list+}", "?list=val1&list=val2&list=val3")
|
||||
uri_template_string (ht, "{?keys}", "?keys=key1,val1,key2,val2")
|
||||
uri_template_string (ht, "{?keys*}", "?key1=val1&key2=val2")
|
||||
uri_template_string (ht, "{?keys+}", "?keys.key1=val1&keys.key2=val2")
|
||||
|
||||
--| Hierarchical path segments, slash-separated
|
||||
uri_template_string (ht, "{/var}", "/value")
|
||||
uri_template_string (ht, "{/var,empty}", "/value/")
|
||||
uri_template_string (ht, "{/var,undef}", "/value")
|
||||
uri_template_string (ht, "{/list}", "/val1,val2,val3")
|
||||
uri_template_string (ht, "{/list*}", "/val1/val2/val3")
|
||||
uri_template_string (ht, "{/list*,x}", "/val1/val2/val3/1024")
|
||||
uri_template_string (ht, "{/list+}", "/list.val1/list.val2/list.val3")
|
||||
uri_template_string (ht, "{/keys}", "/key1,val1,key2,val2")
|
||||
uri_template_string (ht, "{/keys*}", "/key1/val1/key2/val2")
|
||||
uri_template_string (ht, "{/keys+}", "/keys.key1/val1/keys.key2/val2")
|
||||
|
||||
--| Label expansion, dot-prefixed
|
||||
uri_template_string (ht, "X{.var}", "X.value")
|
||||
uri_template_string (ht, "X{.empty}", "X.")
|
||||
uri_template_string (ht, "X{.undef}", "X")
|
||||
uri_template_string (ht, "X{.list}", "X.val1,val2,val3")
|
||||
uri_template_string (ht, "X{.list*}", "X.val1.val2.val3")
|
||||
uri_template_string (ht, "X{.list*,x}", "X.val1.val2.val3.1024")
|
||||
uri_template_string (ht, "X{.list+}", "X.list.val1.list.val2.list.val3")
|
||||
uri_template_string (ht, "X{.keys}", "X.key1,val1,key2,val2")
|
||||
uri_template_string (ht, "X{.keys*}", "X.key1.val1.key2.val2")
|
||||
uri_template_string (ht, "X{.keys+}", "X.keys.key1.val1.keys.key2.val2")
|
||||
|
||||
--| Simple Expansion
|
||||
uri_template_string (ht, "{foo}", "fred")
|
||||
uri_template_string (ht, "{foo,foo}", "fred,fred")
|
||||
uri_template_string (ht, "{bar,foo}", "fred")
|
||||
uri_template_string (ht, "{bar=wilma}", "wilma")
|
||||
|
||||
--| Reserved Expansion
|
||||
uri_template_string (ht, "{foo2}", "That%%27s+right%%21")
|
||||
uri_template_string (ht, "{+foo2}", "That%%27s+right!")
|
||||
uri_template_string (ht, "{base}index", "http%%3A%%2F%%2Fexample.com%%2Fhome%%2Findex")
|
||||
uri_template_string (ht, "{+base}index", "http://example.com/home/index")
|
||||
|
||||
assert ("all strings built", uri_template_string_errors = Void or (attached uri_template_string_errors as err and then err.is_empty))
|
||||
end
|
||||
|
||||
uri_template_string (a_ht: HASH_TABLE [detachable ANY, STRING]; a_expression: STRING; a_expected: STRING)
|
||||
local
|
||||
tpl: URI_TEMPLATE
|
||||
s: STRING
|
||||
m: STRING
|
||||
do
|
||||
create tpl.make (a_expression)
|
||||
s := tpl.expanded_string (a_ht)
|
||||
if not s.same_string (a_expected) then
|
||||
m := "Expected string for %"" + a_expression + "%" expected=%""+ a_expected +"%" but got %"" + s + "%"%N"
|
||||
if attached uri_template_string_errors as err then
|
||||
print (m)
|
||||
err.force (m)
|
||||
else
|
||||
assert (m, False)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
uri_template_parse (s: STRING_8; path_vars: ARRAY [STRING]; query_vars: ARRAY [STRING])
|
||||
local
|
||||
u: URI_TEMPLATE
|
||||
matched: BOOLEAN
|
||||
i: INTEGER
|
||||
do
|
||||
create u.make (s)
|
||||
u.parse
|
||||
assert ("Template %""+ s +"%" is valid", u.is_valid)
|
||||
if attached u.path_variable_names as vars then
|
||||
matched := vars.count = path_vars.count
|
||||
from
|
||||
i := path_vars.lower
|
||||
vars.start
|
||||
until
|
||||
not matched or i > path_vars.upper
|
||||
loop
|
||||
matched := vars.item.same_string (path_vars[i])
|
||||
vars.forth
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
matched := path_vars.is_empty
|
||||
end
|
||||
assert ("path variables matched for %""+ s +"%"", matched)
|
||||
|
||||
if attached u.query_variable_names as vars then
|
||||
matched := vars.count = query_vars.count
|
||||
from
|
||||
i := query_vars.lower
|
||||
vars.start
|
||||
until
|
||||
not matched or i > query_vars.upper
|
||||
loop
|
||||
matched := vars.item.same_string (query_vars[i])
|
||||
vars.forth
|
||||
i := i + 1
|
||||
end
|
||||
else
|
||||
matched := query_vars.is_empty
|
||||
end
|
||||
assert ("query variables matched %""+ s +"%"", matched)
|
||||
end
|
||||
|
||||
uri_template_mismatch (a_uri_template: URI_TEMPLATE; a_uri: STRING)
|
||||
local
|
||||
l_match: detachable URI_TEMPLATE_MATCH_RESULT
|
||||
do
|
||||
l_match := a_uri_template.match (a_uri)
|
||||
assert ("uri %"" + a_uri + "%" does not match template %"" + a_uri_template.template + "%"", l_match = Void)
|
||||
end
|
||||
|
||||
uri_template_match (a_uri_template: URI_TEMPLATE; a_uri: STRING; path_res: ARRAY [TUPLE [name: STRING; value: STRING]]; query_res: ARRAY [TUPLE [name: STRING; value: STRING]])
|
||||
local
|
||||
b: BOOLEAN
|
||||
i: INTEGER
|
||||
l_match: detachable URI_TEMPLATE_MATCH_RESULT
|
||||
do
|
||||
l_match := a_uri_template.match (a_uri)
|
||||
if l_match /= Void then
|
||||
if attached l_match.path_variables as path_ht then
|
||||
b := path_ht.count = path_res.count
|
||||
from
|
||||
i := path_res.lower
|
||||
until
|
||||
not b or i > path_res.upper
|
||||
loop
|
||||
b := attached path_ht.item (path_res[i].name) as s and then s.same_string (path_res[i].value)
|
||||
i := i + 1
|
||||
end
|
||||
assert ("uri %"" + a_uri + "%" matched path variables", b)
|
||||
end
|
||||
if attached l_match.query_variables as query_ht then
|
||||
b := query_ht.count >= query_res.count
|
||||
from
|
||||
i := query_res.lower
|
||||
until
|
||||
not b or i > query_res.upper
|
||||
loop
|
||||
b := attached query_ht.item (query_res[i].name) as s and then s.same_string (query_res[i].value)
|
||||
i := i + 1
|
||||
end
|
||||
assert ("uri %"" + a_uri + "%" matched query variables", b)
|
||||
end
|
||||
else
|
||||
assert ("uri %"" + a_uri + "%" matched", False)
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="uri_template" uuid="64B64199-7F12-4A33-A962-4AD314E9593D" library_target="uri_template">
|
||||
<target name="uri_template">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="encoder" location="..\..\text\encoder\encoder-safe.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time-safe.ecf"/>
|
||||
<cluster name="src" location="src\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/draft_05$</exclude>
|
||||
</file_rule>
|
||||
<file_rule>
|
||||
<exclude>/uri_template_constants.e</exclude>
|
||||
<condition>
|
||||
<custom name="uri_template" value="draft_05"/>
|
||||
</condition>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
<cluster name="draft_05" location=".\src\draft_05\">
|
||||
<condition>
|
||||
<custom name="uri_template" value="draft_05"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</target>
|
||||
<target name="tests" extends="uri_template">
|
||||
<root class="ANY" feature="default_create"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing-safe.ecf"/>
|
||||
<tests name="tests" location=".\tests\"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="uri_template" uuid="64B64199-7F12-4A33-A962-4AD314E9593D" library_target="uri_template">
|
||||
<target name="uri_template">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<option warning="true" full_class_checking="true">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="encoder" location="..\..\text\encoder\encoder.ecf"/>
|
||||
<library name="time" location="$ISE_LIBRARY\library\time\time.ecf"/>
|
||||
<cluster name="src" location="src\" recursive="true">
|
||||
<file_rule>
|
||||
<exclude>/draft_05$</exclude>
|
||||
</file_rule>
|
||||
<file_rule>
|
||||
<exclude>/uri_template_constants.e</exclude>
|
||||
<condition>
|
||||
<custom name="uri_template" value="draft_05"/>
|
||||
</condition>
|
||||
</file_rule>
|
||||
</cluster>
|
||||
<cluster name="draft_05" location=".\src\draft_05\">
|
||||
<condition>
|
||||
<custom name="uri_template" value="draft_05"/>
|
||||
</condition>
|
||||
</cluster>
|
||||
</target>
|
||||
<target name="tests" extends="uri_template">
|
||||
<root class="ANY" feature="default_create"/>
|
||||
<file_rule>
|
||||
<exclude>/.git$</exclude>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true">
|
||||
<assertions precondition="true"/>
|
||||
</option>
|
||||
<setting name="console_application" value="false"/>
|
||||
<variable name="uri_template" value="draft_05"/>
|
||||
<library name="testing" location="$ISE_LIBRARY\library\testing\testing.ecf"/>
|
||||
<tests name="tests" location=".\tests\"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,9 @@
|
||||
Copyright: 2011-2011, Eiffel Software and others
|
||||
License: Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)
|
||||
Source:
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="connector_cgi" uuid="3BCBC1C5-9D99-45BB-B15D-B03D2C069CED" library_target="connector_cgi">
|
||||
<target name="connector_cgi">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/\.git$</exclude>
|
||||
<exclude>/\.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/>
|
||||
<library name="http" location="..\..\..\..\protocol\http\http-safe.ecf"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="connector_cgi" uuid="3BCBC1C5-9D99-45BB-B15D-B03D2C069CED" library_target="connector_cgi">
|
||||
<target name="connector_cgi">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/\.git$</exclude>
|
||||
<exclude>/\.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="ewsgi" location="..\..\ewsgi.ecf" readonly="false"/>
|
||||
<library name="http" location="..\..\..\..\protocol\http\http.ecf"/>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,10 @@
|
||||
${NOTE_KEYWORD}
|
||||
copyright: "2011-${YEAR}, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
@@ -0,0 +1,36 @@
|
||||
note
|
||||
description: "Summary description for {GW_CGI_CONNECTOR}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
GW_CGI_CONNECTOR
|
||||
|
||||
inherit
|
||||
EWSGI_CONNECTOR
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature -- Execution
|
||||
|
||||
launch
|
||||
local
|
||||
env: EWSGI_ENVIRONMENT_VARIABLES
|
||||
do
|
||||
create env.make_with_variables ((create {EXECUTION_ENVIRONMENT}).starting_environment_variables)
|
||||
application.process (env, create {GW_CGI_INPUT_STREAM}.make, create {GW_CGI_OUTPUT_STREAM}.make)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,39 @@
|
||||
note
|
||||
description: "Summary description for GW_CGI_INPUT_STREAM."
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
GW_CGI_INPUT_STREAM
|
||||
|
||||
inherit
|
||||
EWSGI_INPUT_STREAM
|
||||
|
||||
CONSOLE
|
||||
rename
|
||||
make as console_make
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
make_open_stdin ("stdin")
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,67 @@
|
||||
note
|
||||
description: "Summary description for GW_CGI_OUTPUT_STREAM."
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
GW_CGI_OUTPUT_STREAM
|
||||
|
||||
inherit
|
||||
EWSGI_OUTPUT_STREAM
|
||||
undefine
|
||||
flush
|
||||
end
|
||||
|
||||
CONSOLE
|
||||
rename
|
||||
make as console_make
|
||||
end
|
||||
|
||||
HTTP_STATUS_CODE_MESSAGES
|
||||
export
|
||||
{NONE} all
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make
|
||||
do
|
||||
make_open_stdout ("stdout")
|
||||
end
|
||||
|
||||
feature -- Status writing
|
||||
|
||||
put_status_line (a_code: INTEGER)
|
||||
-- Put status code line for `a_code'
|
||||
--| Note this is a default implementation, and could be redefined
|
||||
--| for instance in relation to NPH CGI script
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
create s.make (16)
|
||||
s.append ({HTTP_CONSTANTS}.http_version_1_1)
|
||||
s.append_character (' ')
|
||||
s.append_integer (a_code)
|
||||
if attached http_status_code_message (a_code) as l_status_message then
|
||||
s.append_character (' ')
|
||||
s.append_string (l_status_message)
|
||||
end
|
||||
put_header_line (s)
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="connector_libfcgi" uuid="59C57E56-3EE6-4EF7-873F-7ED084B0EB22" library_target="connector_libfcgi">
|
||||
<target name="connector_libfcgi">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/\.git$</exclude>
|
||||
<exclude>/\.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/>
|
||||
<library name="libfcgi" location="..\..\..\libfcgi\libfcgi-safe.ecf" />
|
||||
<library name="http" location="..\..\..\..\protocol\http\http-safe.ecf"/>
|
||||
<cluster name="src" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="connector_libfcgi" uuid="59C57E56-3EE6-4EF7-873F-7ED084B0EB22" library_target="connector_libfcgi">
|
||||
<target name="connector_libfcgi">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/\.git$</exclude>
|
||||
<exclude>/\.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="ewsgi" location="..\..\ewsgi.ecf" readonly="false"/>
|
||||
<library name="libfcgi" location="..\..\..\libfcgi\libfcgi.ecf" />
|
||||
<library name="http" location="..\..\..\..\protocol\http\http.ecf"/>
|
||||
<cluster name="src" location=".\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,10 @@
|
||||
${NOTE_KEYWORD}
|
||||
copyright: "2011-${YEAR}, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
@@ -0,0 +1,81 @@
|
||||
note
|
||||
description: "Summary description for {GW_LIBFCGI_CONNECTOR}."
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
GW_LIBFCGI_CONNECTOR
|
||||
|
||||
inherit
|
||||
EWSGI_CONNECTOR
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
do
|
||||
create fcgi.make
|
||||
create {GW_LIBFCGI_INPUT_STREAM} input.make (fcgi)
|
||||
create {GW_LIBFCGI_OUTPUT_STREAM} output.make (fcgi)
|
||||
end
|
||||
|
||||
feature -- Server
|
||||
|
||||
launch
|
||||
local
|
||||
res: INTEGER
|
||||
do
|
||||
from
|
||||
res := fcgi.fcgi_listen
|
||||
until
|
||||
res < 0
|
||||
loop
|
||||
process_fcgi_request (fcgi.updated_environ_variables, input, output)
|
||||
res := fcgi.fcgi_listen
|
||||
end
|
||||
end
|
||||
|
||||
feature -- Execution
|
||||
|
||||
process_fcgi_request (vars: HASH_TABLE [STRING, STRING]; a_input: like input; a_output: like output)
|
||||
local
|
||||
gw_env: EWSGI_ENVIRONMENT_VARIABLES
|
||||
do
|
||||
create gw_env.make_with_variables (vars)
|
||||
application.process (gw_env, a_input, a_output)
|
||||
end
|
||||
|
||||
feature -- Input/Output
|
||||
|
||||
input: EWSGI_INPUT_STREAM
|
||||
-- Input from client (from httpd server via FCGI)
|
||||
|
||||
output: EWSGI_OUTPUT_STREAM
|
||||
-- Output to client (via httpd server/fcgi)
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
fcgi: FCGI
|
||||
|
||||
invariant
|
||||
fcgi_attached: fcgi /= Void
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,65 @@
|
||||
note
|
||||
description: "Summary description for GW_LIBFCGI_INPUT_STREAM."
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
GW_LIBFCGI_INPUT_STREAM
|
||||
|
||||
inherit
|
||||
EWSGI_INPUT_STREAM
|
||||
|
||||
STRING_HANDLER
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_fcgi: like fcgi)
|
||||
require
|
||||
valid_fcgi: a_fcgi /= Void
|
||||
do
|
||||
fcgi := a_fcgi
|
||||
initialize
|
||||
end
|
||||
|
||||
initialize
|
||||
-- Initialize Current
|
||||
do
|
||||
create last_string.make_empty
|
||||
end
|
||||
|
||||
feature -- Basic operation
|
||||
|
||||
read_stream (nb_char: INTEGER)
|
||||
-- Read a string of at most `nb_char' bound characters
|
||||
-- or until end of file.
|
||||
-- Make result available in `last_string'.
|
||||
do
|
||||
fcgi.fill_string_from_stdin (last_string, nb_char)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
last_string: STRING
|
||||
-- Last string read
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
fcgi: FCGI;
|
||||
-- Bridge to FCGI world
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,78 @@
|
||||
note
|
||||
description: "Summary description for {GW_LIBFCGI_OUTPUT_STREAM}."
|
||||
legal: "See notice at end of class."
|
||||
status: "See notice at end of class."
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
GW_LIBFCGI_OUTPUT_STREAM
|
||||
|
||||
inherit
|
||||
EWSGI_OUTPUT_STREAM
|
||||
|
||||
HTTP_STATUS_CODE_MESSAGES
|
||||
export
|
||||
{NONE} all
|
||||
end
|
||||
|
||||
create
|
||||
make
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make (a_fcgi: like fcgi)
|
||||
require
|
||||
valid_fcgi: a_fcgi /= Void
|
||||
do
|
||||
fcgi := a_fcgi
|
||||
end
|
||||
|
||||
feature -- Status writing
|
||||
|
||||
put_status_line (a_code: INTEGER)
|
||||
-- Put status code line for `a_code'
|
||||
--| Note this is a default implementation, and could be redefined
|
||||
--| for instance in relation to NPH CGI script
|
||||
local
|
||||
s: STRING
|
||||
do
|
||||
create s.make (16)
|
||||
s.append ({HTTP_CONSTANTS}.http_version_1_1)
|
||||
s.append_character (' ')
|
||||
s.append_integer (a_code)
|
||||
if attached http_status_code_message (a_code) as l_status_message then
|
||||
s.append_character (' ')
|
||||
s.append_string (l_status_message)
|
||||
end
|
||||
put_header_line (s)
|
||||
end
|
||||
|
||||
feature -- Basic operation
|
||||
|
||||
put_string (s: STRING)
|
||||
-- Send `s' to http client
|
||||
do
|
||||
fcgi.put_string (s)
|
||||
end
|
||||
|
||||
feature {NONE} -- Implementation
|
||||
|
||||
fcgi: FCGI
|
||||
-- Bridge to FCGI world
|
||||
|
||||
invariant
|
||||
fcgi_attached: fcgi /= Void
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
${NOTE_KEYWORD}
|
||||
copyright: "2011-${YEAR}, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="connector_nino" uuid="F91861FB-4FEA-455F-9570-828D7903DC64" library_target="connector_nino">
|
||||
<target name="connector_nino">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/\.git$</exclude>
|
||||
<exclude>/\.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true" is_attached_by_default="true" void_safety="all">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base-safe.ecf"/>
|
||||
<library name="ewsgi" location="..\..\ewsgi-safe.ecf" readonly="false"/>
|
||||
<library name="http" location="..\..\..\..\protocol\http\http-safe.ecf"/>
|
||||
<library name="nino" location="..\..\..\..\..\ext\server\nino\nino-safe.ecf" readonly="false">
|
||||
<renaming old_name="HTTP_CONSTANTS" new_name="NINO_HTTP_CONSTANTS"/>
|
||||
</library>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<system xmlns="http://www.eiffel.com/developers/xml/configuration-1-8-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.eiffel.com/developers/xml/configuration-1-8-0 http://www.eiffel.com/developers/xml/configuration-1-8-0.xsd" name="connector_nino" uuid="F91861FB-4FEA-455F-9570-828D7903DC64" library_target="connector_nino">
|
||||
<target name="connector_nino">
|
||||
<root all_classes="true"/>
|
||||
<file_rule>
|
||||
<exclude>/EIFGENs$</exclude>
|
||||
<exclude>/\.git$</exclude>
|
||||
<exclude>/\.svn$</exclude>
|
||||
</file_rule>
|
||||
<option warning="true" full_class_checking="true">
|
||||
</option>
|
||||
<library name="base" location="$ISE_LIBRARY\library\base\base.ecf"/>
|
||||
<library name="ewsgi" location="..\..\ewsgi.ecf" readonly="false"/>
|
||||
<library name="http" location="..\..\..\..\protocol\http\http.ecf"/>
|
||||
<library name="nino" location="..\..\..\..\..\ext\server\nino\nino.ecf" readonly="false">
|
||||
<renaming old_name="HTTP_CONSTANTS" new_name="NINO_HTTP_CONSTANTS"/>
|
||||
</library>
|
||||
<cluster name="src" location=".\src\" recursive="true"/>
|
||||
</target>
|
||||
</system>
|
||||
@@ -0,0 +1,93 @@
|
||||
note
|
||||
description: "Summary description for {GW_NINO_CONNECTOR}."
|
||||
author: ""
|
||||
date: "$Date$"
|
||||
revision: "$Revision$"
|
||||
|
||||
class
|
||||
GW_NINO_CONNECTOR
|
||||
|
||||
inherit
|
||||
EWSGI_CONNECTOR
|
||||
redefine
|
||||
initialize
|
||||
end
|
||||
|
||||
create
|
||||
make,
|
||||
make_with_base
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_with_base (a_app: like application; a_base: like base)
|
||||
do
|
||||
make (a_app)
|
||||
base := a_base
|
||||
end
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
initialize
|
||||
local
|
||||
cfg: HTTP_SERVER_CONFIGURATION
|
||||
do
|
||||
create cfg.make
|
||||
create server.make (cfg)
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
server: HTTP_SERVER
|
||||
|
||||
configuration: HTTP_SERVER_CONFIGURATION
|
||||
do
|
||||
Result := server.configuration
|
||||
end
|
||||
|
||||
feature -- Access
|
||||
|
||||
base: detachable STRING
|
||||
-- Root url base
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_base (b: like base)
|
||||
do
|
||||
base := b
|
||||
end
|
||||
|
||||
feature -- Server
|
||||
|
||||
launch
|
||||
local
|
||||
l_http_handler : HTTP_HANDLER
|
||||
do
|
||||
create {GW_NINO_HANDLER} l_http_handler.make_with_callback (server, "NINO_HANDLER", Current)
|
||||
debug ("nino")
|
||||
if attached base as l_base then
|
||||
print ("Base=" + l_base + "%N")
|
||||
end
|
||||
end
|
||||
server.setup (l_http_handler)
|
||||
end
|
||||
|
||||
process_request (env: HASH_TABLE [STRING, STRING]; a_headers_text: STRING; a_input: HTTP_INPUT_STREAM; a_output: HTTP_OUTPUT_STREAM)
|
||||
local
|
||||
gw_env: EWSGI_ENVIRONMENT_VARIABLES
|
||||
do
|
||||
create gw_env.make_with_variables (env)
|
||||
gw_env.set_variable ("RAW_HEADER_DATA", a_headers_text)
|
||||
application.process (gw_env, create {GW_NINO_INPUT_STREAM}.make (a_input), create {GW_NINO_OUTPUT_STREAM}.make (a_output))
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
@@ -0,0 +1,157 @@
|
||||
note
|
||||
description : "Objects that ..."
|
||||
author : "$Author$"
|
||||
date : "$Date$"
|
||||
revision : "$Revision$"
|
||||
|
||||
class
|
||||
GW_NINO_HANDLER
|
||||
|
||||
inherit
|
||||
HTTP_CONNECTION_HANDLER
|
||||
|
||||
create
|
||||
make_with_callback
|
||||
|
||||
feature {NONE} -- Initialization
|
||||
|
||||
make_with_callback (a_main_server: like main_server; a_name: STRING; a_callback: like callback)
|
||||
-- Initialize `Current'.
|
||||
do
|
||||
base := a_callback.base
|
||||
make (a_main_server, a_name)
|
||||
callback := a_callback
|
||||
end
|
||||
|
||||
callback: GW_NINO_CONNECTOR
|
||||
|
||||
feature -- Access
|
||||
|
||||
base: detachable STRING
|
||||
-- Root url base
|
||||
|
||||
feature -- Element change
|
||||
|
||||
set_base (a_uri: like base)
|
||||
-- Set `base' to `a_uri'
|
||||
do
|
||||
base := a_uri
|
||||
end
|
||||
|
||||
feature -- Request processing
|
||||
|
||||
process_request (a_handler: HTTP_CONNECTION_HANDLER; a_input: HTTP_INPUT_STREAM; a_output: HTTP_OUTPUT_STREAM)
|
||||
-- Process request ...
|
||||
local
|
||||
env, vars: HASH_TABLE [STRING, STRING]
|
||||
p: INTEGER
|
||||
l_request_uri, l_script_name, l_query_string, l_path_info: STRING
|
||||
l_server_name, l_server_port: detachable STRING
|
||||
a_headers_map: HASH_TABLE [STRING, STRING]
|
||||
vn: STRING
|
||||
|
||||
e: EXECUTION_ENVIRONMENT
|
||||
do
|
||||
l_request_uri := a_handler.uri
|
||||
a_headers_map := a_handler.request_header_map
|
||||
create e
|
||||
vars := e.starting_environment_variables
|
||||
env := vars.twin
|
||||
|
||||
--| for Any Abc-Def-Ghi add (or replace) the HTTP_ABC_DEF_GHI variable to `env'
|
||||
from
|
||||
a_headers_map.start
|
||||
until
|
||||
a_headers_map.after
|
||||
loop
|
||||
vn := a_headers_map.key_for_iteration.as_upper
|
||||
vn.replace_substring_all ("-", "_")
|
||||
add_environment_variable (a_headers_map.item_for_iteration, vn, env)
|
||||
a_headers_map.forth
|
||||
end
|
||||
|
||||
--| Specific cases
|
||||
|
||||
p := l_request_uri.index_of ('?', 1)
|
||||
if p > 0 then
|
||||
l_script_name := l_request_uri.substring (1, p - 1)
|
||||
l_query_string := l_request_uri.substring (p + 1, l_request_uri.count)
|
||||
else
|
||||
l_script_name := l_request_uri.string
|
||||
l_query_string := ""
|
||||
end
|
||||
if attached a_headers_map.item ("Host") as l_host then
|
||||
add_environment_variable (l_host, "HTTP_HOST", env)
|
||||
p := l_host.index_of (':', 1)
|
||||
if p > 0 then
|
||||
l_server_name := l_host.substring (1, p - 1)
|
||||
l_server_port := l_host.substring (p+1, l_host.count)
|
||||
else
|
||||
l_server_name := l_host
|
||||
l_server_port := "80" -- Default
|
||||
end
|
||||
end
|
||||
|
||||
if attached a_headers_map.item ("Authorization") as l_authorization then
|
||||
add_environment_variable (l_authorization, "HTTP_AUTHORIZATION", env)
|
||||
p := l_authorization.index_of (' ', 1)
|
||||
if p > 0 then
|
||||
add_environment_variable (l_authorization.substring (1, p - 1), "AUTH_TYPE", env)
|
||||
end
|
||||
end
|
||||
|
||||
add_environment_variable ("CGI/1.1", "GATEWAY_INTERFACE", env)
|
||||
add_environment_variable (l_query_string, "QUERY_STRING", env)
|
||||
|
||||
if attached a_handler.remote_info as l_remote_info then
|
||||
add_environment_variable (l_remote_info.addr, "REMOTE_ADDR", env)
|
||||
add_environment_variable (l_remote_info.hostname, "REMOTE_HOST", env)
|
||||
add_environment_variable (l_remote_info.port.out, "REMOTE_PORT", env)
|
||||
-- add_environment_variable (Void, "REMOTE_IDENT", env)
|
||||
-- add_environment_variable (Void, "REMOTE_USER", env)
|
||||
end
|
||||
|
||||
add_environment_variable (l_request_uri, "REQUEST_URI", env)
|
||||
add_environment_variable (a_handler.method, "REQUEST_METHOD", env)
|
||||
|
||||
add_environment_variable (l_script_name, "SCRIPT_NAME", env)
|
||||
add_environment_variable (l_server_name, "SERVER_NAME", env)
|
||||
add_environment_variable (l_server_port, "SERVER_PORT", env)
|
||||
add_environment_variable (a_handler.version, "SERVER_PROTOCOL", env)
|
||||
add_environment_variable ({HTTP_SERVER_CONFIGURATION}.Server_details, "SERVER_SOFTWARE", env)
|
||||
|
||||
--| Apply `base' value
|
||||
if attached base as l_base and then l_request_uri /= Void then
|
||||
if l_request_uri.starts_with (l_base) then
|
||||
l_path_info := l_request_uri.substring (l_base.count + 1, l_request_uri.count)
|
||||
p := l_path_info.index_of ('?', 1)
|
||||
if p > 0 then
|
||||
l_path_info.keep_head (p - 1)
|
||||
end
|
||||
env.force (l_path_info, "PATH_INFO")
|
||||
env.force (l_base, "SCRIPT_NAME")
|
||||
end
|
||||
end
|
||||
|
||||
callback.process_request (env, a_handler.request_header, a_input, a_output)
|
||||
end
|
||||
|
||||
add_environment_variable (a_value: detachable STRING; a_var_name: STRING; env: HASH_TABLE [STRING, STRING])
|
||||
-- Add variable `a_var_name => a_value' to `env'
|
||||
do
|
||||
if a_value /= Void then
|
||||
env.force (a_value, a_var_name)
|
||||
end
|
||||
end
|
||||
|
||||
note
|
||||
copyright: "2011-2011, Eiffel Software and others"
|
||||
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
|
||||
source: "[
|
||||
Eiffel Software
|
||||
5949 Hollister Ave., Goleta, CA 93117 USA
|
||||
Telephone 805-685-1006, Fax 805-685-6869
|
||||
Website http://www.eiffel.com
|
||||
Customer support http://support.eiffel.com
|
||||
]"
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user