4 * https://master-api.flutter.dev/offline/flutter.xml
7 -- contructor with generic eg. const AlwaysStoppedAnimation<T>(
8 -- classes - we need to parse the right hand column to determine which properties/ methods are static..
9 -- enums have methods !!
10 -- types on topLevelconstants are broken (might be able to extract it from the implementation.)
11 -- parse packages on left, and create fake libraries for them...
16 function __construct()
22 $this->pdo = new PDO("sqlite:". TDIR . "doc.db");
23 $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
28 $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
31 CREATE TABLE IF NOT EXISTS node (
32 id INTEGER PRIMARY KEY AUTOINCREMENT,
34 href VARCHAR (255) NOT NULL DEFAULT '',
35 name VARCHAR (255) NOT NULL DEFAULT '',
36 type VARCHAR (32) NOT NULL DEFAULT '',
37 overriddenDepth INTEGER NOT NULL DEFAULT '',
38 qualifiedName VARCHAR (255) NOT NULL DEFAULT '',
39 enclosedBy_name VARCHAR (255) NOT NULL DEFAULT '',
40 enclosedBy_type VARCHAR (16) NOT NULL DEFAULT '',
41 -- derived data / html extracted...
43 memberOf VARCHAR (255) NOT NULL DEFAULT '',
44 is_constructor INTEGER NOT NULL DEFAULT 0,
45 is_static INTEGER NOT NULL DEFAULT 0,
50 is_fake_namespace INTEGER NOT NULL DEFAULT 0,
51 is_mixin INTEGER NOT NULL DEFAULT 0,
52 is_enum INTEGER NOT NULL DEFAULT 0,
53 is_typedef INTEGER NOT NULL DEFAULT 0,
54 is_constant INTEGER NOT NULL DEFAULT 0,
55 is_abstract INTEGER NOT NULL DEFAULT 0,
56 parent_id INTEGER NOT NULL DEFAULT 0,
58 extends VARCHAR(255) NOT NULL DEFAULT ''
64 $this->pdo->exec("ALTER TABLE node ADD COLUMN is_deprecated INTEGER NOT NULL DEFAULT 0");
65 // deals with param type or return type.
66 $this->pdo->exec("ALTER TABLE node ADD COLUMN value_type VARCHAR (255) NOT NULL DEFAULT ''");
68 $this->pdo->exec("ALTER TABLE node ADD COLUMN sequence_no INTEGER NOT NULL DEFAULT 0");
70 $this->pdo->exec("create index lookup on node(name,qualifiedName,parent_id,type)");
73 CREATE TABLE IF NOT EXISTS extends (
74 id INTEGER PRIMARY KEY AUTOINCREMENT,
75 class_id INTEGER NOT NULL DEFAULT 0,
76 extends_id INTEGER NOT NULL DEFAULT 0
80 $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
88 echo "Grouping properties/methods with parents\n";
93 parent_id = coalesce((
99 pn.qualifiedName = substr(cn.qualifiedName, 0, length(cn.qualifiedName) - length(cn.name))
103 enclosedBy_type ='class'
110 //print_R(array($k,$v));
111 $s = $this->pdo->prepare("SELECT * FROM node where $k=?");
112 $s->execute(array($v));
113 $r = $s->fetchAll(PDO::FETCH_ASSOC);
115 if (count($r) != 1) {
116 print_R(array($k,$v,$r));
117 throw new Exception("not 1 record when searching");
121 function lookup($k,$v)
123 //print_R(array($k,$v));
124 $s = $this->pdo->prepare("SELECT id FROM node where $k=?");
125 $s->execute(array($v));
126 $r = $s->fetchAll(PDO::FETCH_ASSOC);
129 print_R(array($k,$v,$r));
130 die("more than one record when calling lookup");
132 return $r ? $r[0]['id'] : 0;
135 function addExtends($class_id , $extends_id)
137 $s = $this->pdo->prepare("SELECT id FROM extends where class_id=? AND extends_id=?");
138 $s->execute(array($class_id , $extends_id));
139 if ($s->fetchAll(PDO::FETCH_ASSOC)) {
142 $s = $this->pdo->prepare("INSERT INTO extends (class_id, extends_id) VALUES (?,?)");
143 $s->execute(array($class_id , $extends_id));
148 function update($id, $o)
153 //echo "UPDATE";print_r($o);
154 foreach((array) $o as $k=>$v) {
155 if (is_a($v,'stdClass')) {
156 foreach((array)$v as $ik => $iv) {
157 $kk[] = $k . '_' . $ik;
160 $kv[]="{$k}_{$ik}=?";
170 $s = $this->pdo->prepare("INSERT INTO node (".
171 implode(',',$kk) . ") VALUES (".
172 implode(',',$vv) . ")");
177 $s = $this->pdo->prepare("UPDATE node SET ".
178 implode(',',$kv) . " WHERE id = $id");
185 function fromIndex($o)
187 $id = $this->lookup('href', $o->href);
188 $this->update($id, $o);
191 function parseIndex()
195 if (file_exists(TDIR. 'docs.db')) {
196 unlink(TDIR.'docs.db');
199 $js = json_decode(file_get_contents(FDIR.'index.json'));
201 $this->fromIndex($o);
205 function readDom($url)
207 $dom = new DomDocument();
208 libxml_use_internal_errors(true);
209 echo "Reading : {$url}\n";
210 $dom->loadHTMLFile(FDIR . $url);
211 libxml_clear_errors();
212 $xp = new DomXPath($dom);
216 function getElementsByClassName($dom, $class, $node = null)
218 $xp = new DomXPath($dom);
219 return $xp->query("//*[contains(concat(' ', @class, ' '), ' ".$class." ')]", $node);
221 function innerHTML($node)
226 $dom= $node->ownerDocument;
228 foreach ($node->childNodes as $child)
230 $ret.= $dom->saveHTML($child);
235 function readDesc($dom, $id)
238 $desc = $this->getElementsByClassName($dom, 'desc');
240 $array['desc'] = $this->innerHTML($desc->item(0));
242 $sc = $this->getElementsByClassName($dom, 'source-code');
244 $array['example'] = $this->innerHTML($sc->item(0));
246 $this->update($id, $array);
249 function readClassData($dom, $id)
252 $sc = $this->getElementsByClassName($dom,'self-crumb');
255 // abstracts actually impletment stuff in flutter...
256 if (preg_match('/abstract class/', $this->innerHTML($sc->item(0)))) {
257 $ar['is_abstract'] = 1;
260 $this->update($id, $ar);
261 $dl = $dom->getElementsByTagName('dl')->item(0);
262 if ($dl->getAttribute('class') != 'dl-horizontal') {
265 if (strpos($this->innerHTML($dl), '@deprecated')) {
266 $ar['is_deprecated'] = 1;
267 $this->update($id, $ar);
269 $dt = $dl->getElementsByTagName('dt');
270 if (!$dt->length || $this->innerHTML($dt->item(0)) != 'Inheritance') {
273 $dd = $dl->getElementsByTagName('dd');
278 $as = $dd->item(0)->getElementsByTagName('a');
280 for($i = $as->length-1;$i > -1; $i--) {
282 $ex = $this->get('href', $as->item($i)->getAttribute('href'));
284 die("could not find " . $as->item($i)->getAttribute('href') . " when parsing" . $id);
286 if (empty($ex['qualifiedName'])) {
288 print_r($ex);die("missing qualifiedName");
290 $this->addExtends($id, $ex['id']);
291 $extends[] = $ex['qualifiedName'];
294 $ar['extends'] = implode(',', $extends);
295 $this->update($id, $ar);
296 //print_r(array($extends, $id));exit;
300 function readReturnType($dom, $id)
303 $sp = $this->getElementsByClassName($dom,'returntype')->item(0);
304 $vt = $this->readTypeToString($sp);
305 $this->update($id, array('value_type' => $vt));
309 function readSignature($dom, $id)
314 // List<Widget> ... --- List -> generic type = Widget
315 // let's start by just storing the values.
325 $parent = $this->get('id',$id);
326 $ar = $this->getElementsByClassName($dom,'parameter');
327 for($i =0;$i<$ar->length;$i++) {
328 // paramenters are used where methods return callbacks with their own params..
329 if ($ar->item($i)->parentNode->getAttribute('class') == 'signature') {
332 $this->readParam( $id, $ar->item($i) , $parent['qualifiedName'] .'.param');
338 function readParam($id, $node, $prefix)
340 $ar = $node->getElementsByTagName('span');
342 echo $this->innerHTML($node);
343 die("mssing paramter info");
353 for($i = 0; $i < $ar->length; $i++) {
355 switch($ar->item($i)->getAttribute('class')) {
357 case 'parameter-name':
358 $o['name'] = $ar->item($i)->textContent;
359 $o['sequence_no'] = $seq++;
360 $o['qualifiedName' ] = $prefix . '.'. $o['name'] ;
363 case 'type-annotation':
365 $o['value_type'] = $this->readTypeToString($ar->item($i) );
370 if (empty($o['qualifiedName' ])) {
371 $out = $this->get('id', $id);
373 die("missing paramenter name onn this page:" .$out['href']);
377 $id = $this->lookup('qualifiedName',$o['qualifiedName' ] );
378 $this->update($id,$o);
382 function readTypeToString($sp)
386 die("readTypeToString got invalid value");
390 $ar = $sp->getElementsByTagName('a');
392 return '<'. $sp->textContent .'>'; // guessing these are generics..
394 if ($ar->length == 1) {
395 $rt = $this->get('href',$ar->item(0)->getAttribute('href'));
396 return $rt['qualifiedName'];
400 for($i =0;$i<$ar->length;$i++) {
401 $rt = $this->get('href',$ar->item($i)->getAttribute('href'));
403 $types[] = $rt['qualifiedName'];
405 //$t .= ($i == 0) ? $rt['qualifiedName'];: ('<'. $rt['qualifiedName'];);
408 //for($i =0;$i<$ar->length-1;$i++) {
411 return implode(',', $types);
417 var $blacklist = array(
418 'dart-io/HttpOverrides/runZoned.html', // very complex method call - object with callbacks..
419 'dart-io/IOOverrides/runZoned.html',// insanly complex method call - object with callbacks..
420 'dart-async/StreamTransformer/StreamTransformer.fromBind.html', // complex ctor
423 function parse($type)
425 echo "Parse $type\n";
426 $s = $this->pdo->prepare("SELECT * FROM node WHERE type = ?");
427 $s->execute(array($type));
428 $res = $s->fetchAll(PDO::FETCH_ASSOC);
429 foreach($res as $r) {
430 if (in_array($r['href'], $this->blacklist)) {
433 $m = "parse".preg_replace('/[^A-Z]/i', '', $type);
438 function parseClass($o)
440 $d = $this->readDom($o['href']);
441 $this->readDesc($d,$o['id']);
442 $this->readClassData($d,$o['id']);
446 function parseConstructor($o)
448 $d = $this->readDom($o['href']);
449 $this->readDesc($d,$o['id']);
450 $this->readSignature($d,$o['id']);
452 function parseMethod($o)
454 $d = $this->readDom($o['href']);
455 $this->readDesc($d,$o['id']);
456 $this->readReturnType($d,$o['id']);
457 $this->readSignature($d,$o['id']);
459 function parseProperty($o)
461 $d = $this->readDom($o['href']);
462 $this->readDesc($d,$o['id']);
463 $this->readReturnType($d,$o['id']);
466 function parseEnum($o)
468 $d = $this->readDom($o['href']);
469 $this->readDesc($d,$o['id']);
470 //$this->readReturnType($d,$o['id']);
471 $ct = $d->getElementById('constants');
472 $props = $this->getElementsByClassName($ct->ownerDocument,'properties',$ct)->item(0);
473 //echo $this->innerHTML($props);
474 for($i = 0; $i< $props->childNodes->length; $i++) {
475 $cn = $props->childNodes->item($i);
476 //var_dump($cn->nodeName);
477 switch($cn->nodeName) {
478 case 'dt': // look for name
480 $name = $cn->getElementsByTagName('span')->item(0)->textContent; //$this->getElementsByClassName($cn->ownerDocument,'name',$cn)->item(0)->textContent;
483 'type' => 'enum-value',
484 'parent_id' => $o['id'],
485 'qualifiedName' => $o['qualifiedName'] .'.' . $name,
487 // enclosed by?? - leave?
491 case 'dd': // the description
493 $n['desc'] = $this->innerHTML($cn);
495 $id = $this->lookup('qualifiedName', $n['qualifiedName']);
496 if (!strlen(trim($n['name']))) {
499 $this->update($id, $n);
505 // we ignore methods???
510 function parseMixin($o)
512 // oddly enough mixin's have ctors???
513 $d = $this->readDom($o['href']);
514 $this->readDesc($d,$o['id']);
515 // methods and props should be handled ok anyway..
520 function parsetypeDef($o)
522 // these appear to be function signatures really.
523 $d = $this->readDom($o['href']);
524 $this->readDesc($d,$o['id']);
525 // methods and props should be handled ok anyway..
530 function parseConstant($o)
532 // these appear to be function signatures really.
533 $d = $this->readDom($o['href']);
534 $this->readDesc($d,$o['id']);
535 // methods and props should be handled ok anyway..
536 $this->readReturnType($d,$o['id']);
541 function parseTopLevelConstant($o)
545 // these appear to be function signatures really.
546 $d = $this->readDom($o['href']);
547 $this->readDesc($d,$o['id']);
548 // methods and props should be handled ok anyway..
549 //$this->readReturnType($d,$o['id']);
552 function parseTopLevelProperty($o)
556 // aliases to pre-created classes...
557 $d = $this->readDom($o['href']);
558 $this->readDesc($d,$o['id']);
559 // methods and props should be handled ok anyway..
560 $this->readReturnType($d,$o['id']);
563 function parseFunction($o)
567 // these appear to be function signatures really.
568 $d = $this->readDom($o['href']);
569 $this->readDesc($d,$o['id']);
570 // methods and props should be handled ok anyway..
571 $this->readReturnType($d,$o['id']);
572 $this->readSignature($d,$o['id']);
576 /* ----------------------------- OUTPUT -----------------------------*/
578 function outImplementorsToArray($id)
580 $res = $this->pdo->query("
586 id IN (SELECT distinct(class_id) FROM extends WHERE extends_id = {$id})
592 return $res->fetchAll(PDO::FETCH_COLUMN);
597 ///make a tree of the classes, and collapse the classes that have same prefix into the prefix.
598 echo "Creating Tree.json\n";
599 // our tree should only include classes (non-abstract), and namespaces
600 $res = $this->pdo->query("
603 qualifiedName as name,
606 CASE WHEN type != 'library' THEN 1 ELSE 0 END AS is_class,
612 type IN ('class', 'library', 'enum', 'mixin')
617 $all = $res->fetchAll(PDO::FETCH_ASSOC);
620 foreach($all as $o) {
623 $add->is_class = $add->is_class == 1;
625 $add->extends = strlen($add->extends) ? explode(',',$add->extends) : array();
626 if ($o['type'] == 'library') {
629 $stack = array($add);
633 $add->implementors = $this->outImplementorsToArray($o['id']);
635 // find if the element has a child or children...
636 // this is not really where this should go.. but we will add it for the time being.
639 $res = $this->pdo->query("
646 parent_id = {$o['id']}
648 parent_id IN (SELECT distinct(class_id) FROM extends WHERE extends_id = {$o['id']})
653 name IN ('child','children','home', 'body')
658 $types = $res->fetch(PDO::FETCH_ASSOC);
659 $add->childtype = '';
660 $add->childtypes = '0';
662 $car = explode(',', $types['value_type']);
663 $add->childtype = array_pop($car);
664 $add->childtypes = (count($car) && $car[0] == 'date:core.List') ? 2 : 1;
668 //echo "looking for " .$o['qualifiedName']; print_R($stack);
669 for($i = count($stack)-1; $i > -1; $i--) {
671 //print_r(array( substr($o['qualifiedName'], 0, strlen($last->qualifiedName)), $last->qualifiedName));
672 if (substr($add->qualifiedName, 0, strlen($last->qualifiedName)) == $last->qualifiedName) {
682 foreach($out as $c) {
683 $this->outTreeGroups($c);
684 $bits = explode(".", $c->qualifiedName);
685 if (count($bits)< 2 || !isset($libs[$bits[0]])) {
686 $libs[$c->qualifiedName] = $c;
689 $libs[$bits[0]]->cn[] = $c;
692 $out = array_values($libs);
693 echo "WRITE: " .TDIR ."tree.json\n";
694 file_put_contents(TDIR .'tree.json', json_encode($out, JSON_PRETTY_PRINT));
701 function outTreeGroups($obj)
705 foreach($obj->cn as $c) {
707 $name = substr($c->qualifiedName, strlen($obj->qualifiedName) +1);
708 $bits = preg_split('/(?<=[a-z])(?=[A-Z])|(?=[A-Z][a-z])/',
709 $name, -1, PREG_SPLIT_NO_EMPTY);
710 if (!isset($groups[$bits[0]])) {
711 $groups[$bits[0]] = array();
713 $groups[$bits[0]][] = $c;
716 foreach($groups as $k => $list) {
717 if (count($list) < 2) {
718 $obj->cn[] = $list[0];
721 $obj->cn[] = (object) array(
722 'name' => $obj->qualifiedName .'.' . $k,
723 'qualifiedName' => $obj->qualifiedName .'.' . $k,
725 'is_class' => 'false',
732 function typeStringToGeneric($str)
734 $bits = explode(',', $str);
735 if (count($bits) < 2) {
739 foreach($bits as $i => $add) {
740 $t .= ($i == 0) ? $add : ('<'. $add );
743 for($i =0;$i<count($bits)-1;$i++) {
750 function outClassSymbols()
752 echo "Writing Class Symbols\n";
753 $res = $this->pdo->query("
756 COALESCE(desc, '') as desc,
758 COALESCE(example, '') as example,
760 is_abstract as isAbstract,
762 is_deprecated as isDeprecated,
764 enclosedBy_name as memberOf,
765 qualifiedName as name,
771 type IN ('class', 'mixin', 'enum')
776 $all = $res->fetchAll(PDO::FETCH_ASSOC);
778 foreach($all as $clsar) {
779 $cls = (object) $clsar;
782 if (!isset($ns[$cls->memberOf])) {
783 $ns[$cls->memberOf] = (object) array(
786 //'constants' => array(), // within a class.. - we could treat as static properties..
787 'enum' => array(), // like a class...
788 'typedef' => array(), // need to query these seperatly... -- look very different to classes..
789 // functions (within a library... )
790 // top level constant? (within a library - without a class)
791 // top level proeprty? (predefined instances of clases) - within a library
795 $cls->isConstant = $cls->isConstant == 1;
797 $cls->isAbstract = $cls->isAbstract == 1;
798 $cls->isDeprecated = $cls->isDeprecated == 1;
799 $cls->is_enum = $cls->dtype == 'enum';
800 $cls->is_mixin = $cls->dtype == 'mixin';
801 //$cls->is_typedef = $cls->is_typedef = 1;
802 $cls->extends = strlen($cls->extends) ? explode(',',$cls->extends) : array();
803 $cls->realImplementors = $this->outImplementorsToArray($clsar['id']);
804 $cls->events = $this->outEventSymbols($clsar); // event's are properties that are typedefs..
805 $cls->methods = $this->outMethodSymbols($clsar);
806 $cls->props = $this->outPropertySymbols($clsar);
808 echo "OUT:".TDIR .'symbols/'.$cls->name. '.json' ."\n";
809 file_put_contents(TDIR .'symbols/'.$cls->name. '.json', json_encode($cls,JSON_PRETTY_PRINT));
812 $ns[$cls->memberOf]->{$cls->dtype}[] = $cls;
815 foreach($ns as $nm => $cls) {
816 echo "OUT:".TDIR .'ns/'.$nm. '.json' ."\n";
817 file_put_contents(TDIR .'ns/'.$nm. '.json', json_encode($cls,JSON_PRETTY_PRINT));
824 function outEventSymbols($c)
826 $res = $this->pdo->query("
829 COALESCE(desc, '') as desc,
830 COALESCE(example, '') as example,
832 is_deprecated as isDeprecated,
838 parent_id = {$c['id']}
842 'typedef' = (SELECT type from node as sc where sc.qualifiedName = (CASE
843 WHEN instr(node.value_type,',') > 0
844 THEN substr(node.value_type, 0, instr(node.value_type,','))
852 $all = $res->fetchAll(PDO::FETCH_ASSOC);
854 foreach($all as $evar) {
855 $ev = (object) $evar;
857 $ev->isDeprecated = $ev->isDeprecated == 1;
858 $ev->memberOf = $c['name'];
859 $ev->params = array(); // FIXME
860 $ev->type = $this->typeStringToGeneric($ev->type);
868 function outPropertySymbols($c)
870 $res = $this->pdo->query("
873 COALESCE(desc, '') as desc,
874 COALESCE(example, '') as example,
877 is_deprecated as isDeprecated,
884 parent_id = {$c['id']}
890 'typedef' != (SELECT type from node as sc where sc.qualifiedName = (CASE
891 WHEN instr(node.value_type,',') > 0
892 THEN substr(node.value_type, 0, instr(node.value_type,','))
896 type IN ('constant', 'enum-value')
905 $all = $res->fetchAll(PDO::FETCH_ASSOC);
907 foreach($all as $evar) {
908 $ev = (object) $evar;
910 $ev->isStatic = in_array($ev->dtype , array( 'constant', 'enum-value')); // since we do not know what static properties are...
911 $ev->isConstant = in_array($ev->dtype , array( 'constant', 'enum-value'));
912 $ev->memberOf = $c['name'];
913 $ev->isDeprecated = $ev->isDeprecated == 1;
914 $ev->params = array(); // FIXME
915 $ev->type = $this->typeStringToGeneric($ev->type);
922 function outMethodSymbols($c)
925 $res = $this->pdo->query("
928 COALESCE(desc, '') as desc,
929 COALESCE(example, '') as example,
931 is_deprecated as isDeprecated,
938 parent_id = {$c['id']}
940 type IN ('method','constructor')
947 $all = $res->fetchAll(PDO::FETCH_ASSOC);
949 foreach($all as $evar) {
950 $ev = (object) $evar;
952 $ev->isConstructor = $ev->dtype == 'constructor';
955 $ev->memberOf = $c['name'];
956 $ev->isDeprecated = $ev->isDeprecated == 1;
957 $ev->params = $this->outParamSymbols($evar);
958 $ev->type = $this->typeStringToGeneric($ev->type);
965 function outParamSymbols($c)
968 $res = $this->pdo->query("
972 COALESCE(desc, '') as desc,
973 COALESCE(example, '') as example,
975 is_deprecated as isDeprecated,
981 parent_id = {$c['id']}
990 $all = $res->fetchAll(PDO::FETCH_ASSOC);
992 foreach($all as $evar) {
993 $ev = (object) $evar;
995 $ev->isDeprecated = $ev->isDeprecated == 1;
996 $ev->isOptional = $c['dtype'] == 'constructor';
997 $ev->type = $this->typeStringToGeneric($ev->type);
1009 define( 'FDIR', '/home/alan/Downloads/flutterdocs/flutter/');
1010 define( 'TDIR', '/home/alan/gitlive/flutter-docs-json/');
1013 print_r($_SERVER['argv']);
1014 if (!empty($_SERVER['argv'][1]) && $_SERVER['argv'][1] == 'index') {
1015 echo "rebuilding Index" ;$sq->parseIndex();exit;
1017 //$sq->parse('library'); // what does this achieve?
1019 $sq->parse('class');
1020 $sq->parse('constructor');
1021 $sq->parse('method');
1022 $sq->parse('property');
1024 $sq->parse('mixin');
1025 $sq->parse('typedef');
1026 $sq->parse('constant');
1027 $sq->parse('top-level constant');
1028 $sq->parse('function');
1029 $sq->parse('top-level property');
1036 $sq->outClassSymbols();