Signed-off-by: Rob Hoes <[email protected]>

 ocaml/doc/OMakefile   |    4 +
 ocaml/doc/apidoc.html |    3 +
 ocaml/doc/apidoc.js   |  137 ++++++++++++++++++++++++++++++++++++++++++++++---
 ocaml/doc/jsapi.ml    |   63 +++++++++++++++++++++-
 ocaml/doc/style.css   |   13 +++-
 5 files changed, 203 insertions(+), 17 deletions(-)


# HG changeset patch
# User Rob Hoes <[email protected]>
# Date 1274793987 -3600
# Node ID fbf3b7184a3fd1adabb500c2261659dc8a1bb9ee
# Parent  277057c7e27ade920540db458a0c1b2c07eab17d
Add lifecycle info and release notes to HTML API docs

Signed-off-by: Rob Hoes <[email protected]>

diff -r 277057c7e27a -r fbf3b7184a3f ocaml/doc/OMakefile
--- a/ocaml/doc/OMakefile
+++ b/ocaml/doc/OMakefile
@@ -4,6 +4,10 @@
 
 OCAML_LIBS += ../idl/datamodel
 OCAMLINCLUDES += ../idl
+
+CAMLP4_FILES = jsapi
+UseCamlp4(rpc-light.syntax, $(CAMLP4_FILES))
+
 OCamlProgram(jsapi, jsapi)
 
 .PHONY: doc
diff -r 277057c7e27a -r fbf3b7184a3f ocaml/doc/apidoc.html
--- a/ocaml/doc/apidoc.html
+++ b/ocaml/doc/apidoc.html
@@ -6,10 +6,13 @@
 		<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
 		<script type="text/javascript" src="main.js"></script>
 		<script type="text/javascript" src="api/index.json"></script>
+		<script type="text/javascript" src="api/releases.json"></script>
 		<script type="text/javascript" src="apidoc.js"></script>
 		<script>
 			if (cls != "")
 				document.write('<script src="api/', cls, '.json" type="text/JavaScript"><\/script>');
+			if (rel != "")
+				document.write('<script src="api/', rel, '.json" type="text/JavaScript"><\/script>');
 		</script>
 		<link rel="stylesheet" href="style.css" />
 	</head>
diff -r 277057c7e27a -r fbf3b7184a3f ocaml/doc/apidoc.js
--- a/ocaml/doc/apidoc.js
+++ b/ocaml/doc/apidoc.js
@@ -15,6 +15,7 @@
 // global variables
 
 var cls = getQuerystring('c');
+var rel = getQuerystring('r');
 
 function qualifier(q)
 {
@@ -74,6 +75,29 @@
 	return 'Unknown[' + t + ']';
 }
 
+function current_lifecycle_stage(s)
+{
+	if (s.length == 0)
+		return 'Prototype';
+	else {
+		last_transition = s[s.length-1][0];
+		switch (last_transition) {
+		case 'Deprecated':
+			return 'Deprecated';
+			break;
+		case 'Removed':
+			return 'Removed';
+			break;
+		case 'Published':
+		case 'Changed':
+		case 'Extended':
+		default:
+			return '';
+			break;
+		}
+	}
+}
+
 function make_field(fld, n)
 {
 	name = fld.field_name;
@@ -81,20 +105,22 @@
 	html = "";	
 	html = '<div class="field' + toggle(n) + '">';
 	html += '<input type="button" class="small-button" value="details" onclick="showhide(document.getElementById(\'' + name + '_details\'))" />';
+	html += '<div class="lifecycle">' + current_lifecycle_stage(fld.lifecycle) + '</div>';
 	html += '<div><span class="inline-type">' + transform_type(fld.ty) + '</span> <span class="field-name">' + name + '</span> <span class="inline-qualifier">[' + qualifier(fld.qualifier) + ']</span></div>';
 	
 	html += '<div  id="' + name + '_details" style="display: none">';
 	html += '<div class="field-description">' + fld.field_description + '</div>';
 	
 	html += '<table class="field-table">';
-	if (fld.release != undefined) {
-		html += '<tr><td width="130px"><span class="field-head">Introduced in:</span></td><td>' + fld.release.internal[1] + '</td></tr>';
-		if (fld.release.internal_deprecated_since != undefined)
-			html += '<tr><td width="130px"><span class="field-head">Deprecated since:</span></td><td>' + fld.release.internal_deprecated_since + '</td></tr>';
-	}
 	if (fld.default_value != undefined)
 		html += '<tr><td width="130px"><span class="field-head">Default value:</span></td><td>' + transform_default(fld.default_value) + '</td></tr>';
+	html += '</table>';
 	
+	html += '<table class="field-table">';
+	for (i in fld.lifecycle) {
+		l = fld.lifecycle[i];
+		html += '<tr><td width="130px"><span class="field-head">' + l[0] + ' in:</span></td><td width="130px">' + l[1] + '</td><td>' + l[2] + '</td></tr>';
+	}
 	html += '</table>';
 	html += '</div></div>';
 	
@@ -109,6 +135,7 @@
 	
 	html += '<div class="field' + toggle(n) + '">';
 	html += '<input type="button" class="small-button" value="details" onclick="showhide(document.getElementById(\'' + name + '_details\'))" />';
+	html += '<div class="lifecycle">' + current_lifecycle_stage(msg.msg_lifecycle) + '</div>';
 	html += '<div><span class="inline-type">' + 
 		(msg.msg_result != undefined ? transform_type(msg.msg_result[0]) : 'void') + 
 		'</span> <span class="field-name">' + name + '</span> <span class="inline-params">(' +
@@ -142,10 +169,12 @@
 		}
 		html += '</table></td></tr>';
 	}
-	if (msg.msg_release != undefined) {
-		html += '<tr><td><span class="field-head">Introduced in:</span></td><td>' + msg.msg_release.internal[1] + '</td></tr>';
-		if (msg.msg_release.internal_deprecated_since != undefined)
-			html += '<tr><td><span class="field-head">Deprecated since:</span></td><td>' + msg.msg_release.internal_deprecated_since + '</td></tr>';
+	html += '</table>';
+	
+	html += '<table class="field-table">';
+	for (i in msg.msg_lifecycle) {
+		l = msg.msg_lifecycle[i];
+		html += '<tr><td width="130px"><span class="field-head">' + l[0] + ' in:</span></td><td width="130px">' + l[1] + '</td><td>' + l[2] + '</td></tr>';
 	}
 	html += '</table>';
 	
@@ -164,10 +193,21 @@
 	messages.sort(function(a, b){return a.msg_name.toLowerCase().charCodeAt(0) - b.msg_name.toLowerCase().charCodeAt(0)});
 
 	html = "";
+	html += '<input type="button" class="small-button" value="details" onclick="showhide(document.getElementById(\'class_details\'))" />';
+	html += '<div class="lifecycle">' + current_lifecycle_stage(clsdoc.obj_lifecycle) + '</div>';
 	html += '<h1 class="title">Class: ' + cls + '</h1>\n';
 	
 	html += '<div class="description">' + clsdoc.description + '</div>';
 	
+	html += '<div  id="class_details" style="display: none">';
+	html += '<table class="field-table">';
+	for (i in clsdoc.obj_lifecycle) {
+		l = clsdoc.obj_lifecycle[i];
+		html += '<tr><td width="130px"><span class="field-head">' + l[0] + ' in:</span></td><td width="130px">' + l[1] + '</td><td>' + l[2] + '</td></tr>';
+	}
+	html += '</table>';
+	html += '</div>';
+	
 	html += '<h2>Fields</h2>';
 	if (fields.length > 0) {
 		for (i in fields)
@@ -187,9 +227,60 @@
 	set_content(html);
 }
 
-function build()
+function compare_release_notes(a, b)
 {
+	function change_to_num(x) {
+		if (x.indexOf('Published') > -1) return '0';
+		else if (x.indexOf('Extended') > -1) return '1';
+		else if (x.indexOf('Changed') > -1) return '2';
+		else if (x.indexOf('Deprecated') > -1) return '3';
+		else if (x.indexOf('Removed') > -1) return '4';
+		else return '5';
+	}
+	function element_to_num(x) {
+		if (x.indexOf('object') > -1) return '0';
+		else if (x.indexOf('field') > -1) return '1';
+		else if (x.indexOf('message') > -1) return '2';
+		else return '3';
+	}
+	a = change_to_num(a[0]) + element_to_num(a[0]) + (a[1]+a[2]).toLowerCase();
+	b = change_to_num(b[0]) + element_to_num(b[0]) + (b[1]+b[2]).toLowerCase();
+	return a > b;
+}
+
+function release_doc()
+{	
+	changes = [];
+	
+	for (i in release_info) {
+		c = release_info[i];
+		for (j in c.obj_changes)
+			changes.push([c.obj_changes[j][0] + ' object', c.cls, '', c.obj_changes[j][2]]);
+		for (j in c.field_changes)
+			changes.push([c.field_changes[j][0] + ' field', c.cls, c.field_changes[j][1], c.field_changes[j][2]]);
+		for (j in c.msg_changes)
+			changes.push([c.msg_changes[j][0] + ' message', c.cls, c.msg_changes[j][1], c.msg_changes[j][2]]);
+	}
+	
+	changes.sort(compare_release_notes);
+	
 	html = "";
+	html += '<h1 class="title">Release notes: ' + rel + '</h1>\n';
+
+	html += '<table><tr><th style="width: 12em">Change</th><th>Element</th><th>Description</th></tr>';
+		
+	for (i in changes) {
+		html += '<tr><td>' + changes[i][0] + '</td><td><a href="?c=' + changes[i][1] + (changes[i][2] != '' ? '#' + changes[i][2] : '') + '">' +
+			changes[i][1] + (changes[i][2] != '' ? '.' + changes[i][2] : '') + '</a></td><td>' + changes[i][3] + '</td></tr>';
+	}
+	
+	html += '</table>';
+
+	set_content(html);
+}
+
+function class_list()
+{
 	html = '<h2 class="title">Classes</h2>';
 	
 	classes.sort(function(a, b){return a.toLowerCase().charCodeAt(0) - b.toLowerCase().charCodeAt(0)});
@@ -199,9 +290,35 @@
 	}
 	
 	append_sidebar(html);
+}
+
+function release_list()
+{
+	html = '<h2>Release notes</h2>';
 	
+	for (i in releases) {
+		r = releases[i];
+		html += '<a href="?r=' + r + '">' + r + '</a><br>';
+	}
+	
+	append_sidebar(html);
+}
+
+function build()
+{
 	if (cls != "") {
+		class_list();
+		release_list();
 		class_doc();
 	}
+	else if (rel != "") {
+		class_list();
+		release_list();
+		release_doc();
+	}
+	else {
+		class_list();
+		release_list();
+	}
 }
 
diff -r 277057c7e27a -r fbf3b7184a3f ocaml/doc/jsapi.ml
--- a/ocaml/doc/jsapi.ml
+++ b/ocaml/doc/jsapi.ml
@@ -11,17 +11,72 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU Lesser General Public License for more details.
  *)
+ 
+open Datamodel_types
+open Stringext
 
+type change_t = lifecycle_change * string * string
+and changes_t = change_t list
+with rpc
+
+let obj_change_in_release rel o =
+	let rec find_rel rel = function
+		| [] -> None
+		| (transition, release, doc) :: tl when release = rel -> Some (transition, o.name, doc)
+		| _ :: tl -> find_rel rel tl
+	in
+	find_rel rel o.obj_lifecycle
+	
+let msg_change_in_release rel m =
+	let rec find_rel rel = function
+		| [] -> None
+		| (transition, release, doc) :: tl when release = rel -> Some (transition, m.msg_name, doc)
+		| _ :: tl -> find_rel rel tl
+	in
+	find_rel rel m.msg_lifecycle
+
+let field_change_in_release rel f =
+	let rec find_rel rel = function
+		| [] -> None
+		| (transition, release, doc) :: tl when release = rel -> Some (transition, f.field_name, doc)
+		| _ :: tl -> find_rel rel tl
+	in
+	find_rel rel f.lifecycle
+	
 let _ =
-	let api = (* Datamodel_utils.add_implicit_messages *) (Datamodel.all_api) in
+	let api = (Datamodel.all_api) in
 	let objs = Dm_api.objects_of_api api in
 	let create_json obj =
-		let name = obj.Datamodel_types.name in
-		let s = Jsonrpc.to_string (Datamodel_types.rpc_of_obj obj) in
+		let name = obj.name in
+		let s = Jsonrpc.to_string (rpc_of_obj obj) in
 		Unixext.write_string_to_file ("api/" ^ name ^ ".json") ("clsdoc = " ^ s);
 		name
 	in
 	let names = List.map create_json objs in
 	let class_list = String.concat ", " (List.map (fun s -> "'" ^ s ^ "'") names) in
-	Unixext.write_string_to_file "api/index.json" ("classes = [" ^ class_list ^ "]")
+	Unixext.write_string_to_file "api/index.json" ("classes = [" ^ class_list ^ "]");
 	
+	let new_in_release rel =
+		let search_obj obj =
+			let obj_changes : changes_t = 
+				match obj_change_in_release rel obj with
+				| None -> []
+				| Some x -> [x]
+			in
+				
+			let msgs = List.filter (fun m -> not m.msg_hide_from_docs) obj.messages in
+			let msg_changes : changes_t = List.fold_left
+				(fun l m -> match msg_change_in_release rel m with None -> l | Some x -> x :: l) [] msgs in
+				
+			let flds = List.filter (function Field f -> true | _ -> false) obj.contents in
+			let field_changes : changes_t = List.fold_left
+				(fun l (Field f) -> match field_change_in_release rel f with None -> l | Some x -> x :: l) [] flds in
+				
+			"{'cls': '" ^ obj.name ^ "', 'obj_changes': " ^ Jsonrpc.to_string (rpc_of_changes_t obj_changes) ^ ", 'field_changes': " ^ Jsonrpc.to_string (rpc_of_changes_t field_changes) ^ ", 'msg_changes': " ^ Jsonrpc.to_string (rpc_of_changes_t msg_changes) ^ "}"
+		in
+		let release_info = String.concat ", " (List.map search_obj objs) in
+		Unixext.write_string_to_file ("api/" ^ rel ^ ".json") ("release_info = [" ^ release_info ^ "]")
+	in
+	List.iter new_in_release release_order;
+	let release_list = String.concat ", " (List.map (fun s -> "'" ^ s ^ "'") release_order) in
+	Unixext.write_string_to_file "api/releases.json" ("releases = [" ^ release_list ^ "]");
diff -r 277057c7e27a -r fbf3b7184a3f ocaml/doc/style.css
--- a/ocaml/doc/style.css
+++ b/ocaml/doc/style.css
@@ -188,7 +188,7 @@
 
 td {
 	vertical-align: top;
-	padding: 3px;
+	padding: 3px 1em 3px 0;
 }
 
 th {
@@ -274,11 +274,18 @@
 	margin-left: .5em;
 }
 
+.lifecycle {
+	font-size: 90%;
+	float: right;
+	font-variant:small-caps;
+	color: #900;
+	margin: auto 0.5em;
+}
+
 .small-button {
 	font-size: 70%;
-//	text-align: right;
 	float: right;
-	margin: .5em 0;
+	margin: 0.2em 0 0.2em 0.5em;
 }
 
 .stat {
_______________________________________________
xen-api mailing list
[email protected]
http://lists.xensource.com/mailman/listinfo/xen-api

Reply via email to