--- /dev/null
+<?php
+
+class Obj {
+
+ static $out_props = array(
+ 'eClass' => array('props', 'events', 'methods'),
+ 'eMixin' => array('props', 'events', 'methods'),
+ 'eTypedef' => array('name', 'type', 'sig', 'static', 'desc','memberOf'),
+ 'eProperty' => array('name', 'type', 'desc','memberOf'),
+ 'eConstant' => array('name', 'type', 'desc','memberOf'),
+ 'eMethod' => array('name', 'type', 'sig', 'static', 'desc','memberOf', 'isConstructor'),
+ 'eConstructor' => array('name', 'type', 'sig', 'static', 'desc','memberOf', 'isConstructor'),
+ 'eEnum' => array('name', 'type', 'desc','memberOf'), // fixme .. .values.?
+ 'eFunction' => array('name', 'type', 'sig', 'static', 'desc','memberOf', 'isConstructor'),// fixme .. .memberof == package.
+ 'Prop' => array('name', 'type', 'desc','memberOf'),
+ );
+
+ var $href = '';
+ var $desc = '';
+ var $example = '';
+ var $isDeprecated = false;
+
+ function __construct($ar)
+ {
+ foreach($ar as $k=>$v) {
+ $this->{$k} = $v;
+ }
+
+ }
+
+
+ function parseHTML()
+ {
+ $dom = new DomDocument();
+ libxml_use_internal_errors(true);
+ echo "Reading : {$this->href}\n";
+ $dom->loadHTMLFile(FDIR . $this->href);
+ libxml_clear_errors();
+ $xp = new DomXPath($dom);
+ $desc = $this->getElementsByClassName($dom, 'desc');
+ if ($desc->length) {
+ $this->desc = $this->innerHTML($desc->item(0));
+ }
+ $sc = $this->getElementsByClassName($dom, 'source-code');
+ if ($sc->length) {
+ $this->example = $this->innerHTML($sc->item(0));
+ }
+ return $dom;
+ }
+
+ function getElementsByClassName($dom, $class)
+ {
+ $xp = new DomXPath($dom);
+ return $xp->query("//*[contains(concat(' ', @class, ' '), ' ".$class." ')]");
+ }
+ function innerHTML($node)
+ {
+ if (!$node) {
+ print_r($this);
+ }
+ $dom= $node->ownerDocument;
+ $ret = '';
+ foreach ($node->childNodes as $child)
+ {
+ $ret.= $dom->saveHTML($child);
+ }
+ return $ret;
+
+ }
+ function parseType($sp)
+ {
+ if (!$sp) {
+ print_R($this);
+ echo "parseType got invalid value";
+ }
+ $ar = $sp->getElementsByTagName('a');
+ if (!$ar->length) {
+ $this->type = $sp->textContent;
+ }
+ if ($ar->length == 1) {
+ $this->type = eClass::$url_map[$ar->item(0)->getAttribute('href')]->name;
+ return;
+ }
+ $this->types = array();
+ $t = '';
+ for($i =0;$i<$ar->length;$i++) {
+ $add = eClass::$url_map[$ar->item($i)->getAttribute('href')]->name;;
+ $this->types[] = $add;
+
+ $t .= ($i == 0) ? $add : ('<'. $add );
+
+ }
+ for($i =0;$i<$ar->length-1;$i++) {
+ $t .= '>';
+ }
+ $this->type = $t;
+
+
+ }
+ function toSummaryArray()
+ {
+ $ret = array();
+ if (!isset(self::$out_props[get_class($this)] )) {
+ die("don't know how to handle class: " . get_class($this));
+ }
+ foreach(self::$out_props[get_class($this)] as $k) {
+ $out = $this->{$k};
+ if (is_array($out)) {
+ $out = array();
+ foreach($this->{$k} as $v) {
+ $out[] = $v->toSummaryArray();
+ }
+ }
+ $ret[$k] = $out;
+ }
+ return $ret;
+ }
+ function isA($str)
+ {
+ return false;
+ }
+
+}
+
+class Ns extends Obj {
+ static $tree = array();
+ static $kv = array();
+ var $name = '';
+ var $href = '';
+ var $cn = array();
+ var $isFakeNamespace = false;
+ function __construct($ar)
+ {
+ parent::__construct($ar);
+
+ if ($this->isFakeNamespace) {
+ return;
+ }
+
+ $bits= explode('.', $this->name);
+
+ self::$kv[$this->name] = $this;
+
+ if (count($bits) == 1 ) {
+ self::$tree[] = $this;
+ return;
+ }
+ array_pop($bits);
+ $par = implode('.', $bits);
+ $this->memberOf = $par;
+ self::add($this);
+
+
+ }
+ static function add($cls)
+ {
+ self::$kv[$cls->memberOf]->cn[] = $cls;
+ }
+
+ function fakeTree()
+ {
+
+ foreach($this->cn as $c) {
+ if (!isset($c->shortname)) {
+ continue;
+ }
+ $map[$c->shortname] = $c;
+
+
+
+ }
+
+ $cn = array();
+ foreach($this->cn as $c) {
+ if (!isset($c->shortname)) {
+ continue;
+ }
+ $bits = preg_split('/(?<=[a-z])(?=[A-Z])|(?=[A-Z][a-z])/',
+ $c->shortname, -1, PREG_SPLIT_NO_EMPTY);
+ //print_r($bits);
+ if (count($bits) < 2 ) {
+ $cn[] = $c;
+ continue;
+ }
+ if (!isset($map[$bits[0]])) {
+ $add = new Ns(array(
+ 'name' => $c->memberOf .'.'. $bits[0],
+ 'isFakeNamespace' => true,
+
+ ));
+ $map[$bits[0]] = $add;
+ $cn[] = $add;
+ }
+
+ $map[$bits[0]]->cn[] = $c;
+
+ }
+
+ // finally remove from tree if it's saving '1'
+ $cc = array();
+ foreach($cn as $c) {
+ if (empty($c->isFakeNamespace)) {
+ $cc[] = $c;
+ continue;
+ }
+ if (count($c->cn) < 2) {
+ $cc[] = $c->cn[0];
+ continue;
+ }
+ $cc[] = $c;
+
+ }
+
+ $this->cn = $cc;
+
+
+ }
+
+ function toTreeArray()
+ {
+ // tidy the tree before starting...
+
+
+
+ $ret = array(
+ 'name' => $this->name,
+ 'is_class' => false,
+ 'cn' => array()
+ );
+ // in theory flutter has a flat tree... ?
+ foreach($this->cn as $e) {
+ if (in_array(get_class($e) , array('eClass', 'eMixin', 'Ns'))) {
+ $ret['cn'][] = $e->toTreeArray();
+ }
+
+ }
+ return $ret;
+ }
+}
+
+
+class eClass extends Obj {
+
+ static $all = array();
+ static $url_map = array();
+ var $name;
+ var $extends = array();
+ var $memberOf; /// really the package..
+ var $events = array();
+ var $methods = array();
+ var $props = array();
+ var $isMixin = false;
+ var $isEnum = false;
+ var $isTypedef = false;
+ var $isConstant = false;
+ var $isAbstract = false;
+ var $implementors = array();
+ var $realImplementors = array();
+ var $cn = array();
+ function __construct($ar)
+ {
+ parent::__construct($ar);
+ $bits = explode('.', $this->name);
+ $this->shortname = array_pop($bits);
+
+ self::$all[$this->name] = $this;
+ self::$url_map[$this->href] = $this;
+ Ns::add($this);
+ }
+
+ function parseHTML()
+ {
+ // do children first..
+
+
+
+ $dom = parent::parseHTML();
+
+ $sc = $this->getElementsByClassName($dom,'self-crumb');
+ if ($sc->length) {
+ // abstracts actually impletment stuff in flutter...
+ if (preg_match('/abstract class/', $this->innerHTML($sc->item(0)))) {
+ $this->isAbstract = true;
+ }
+
+ }
+
+
+
+ $dl = $dom->getElementsByTagName('dl')->item(0);
+ if ($dl->getAttribute('class') != 'dl-horizontal') {
+ $this->extends = array();
+ return;
+ }
+
+ if (strpos($this->innerHTML($dl), '@deprecated')) {
+ $this->isDeprecated = true;
+ }
+
+
+
+ $dt = $dl->getElementsByTagName('dt');
+ if (!$dt->length) {
+ return;
+ }
+ if ($this->innerHTML($dt->item(0)) != 'Inheritance') {
+ return;
+ }
+
+ $dd = $dl->getElementsByTagName('dd');
+ if (!$dd->length) {
+ return;
+ }
+ $as = $dd->item(0)->getElementsByTagName('a');
+ $this->extends = array();
+ for($i = $as->length-1;$i > -1; $i--) {
+
+ if (!isset(self::$url_map[$as->item($i)->getAttribute('href')])) {
+ die("could not find " . $as->item($i)->getAttribute('href') . " when parsing" . $this->href);
+ }
+
+ $this->extends[] = self::$url_map[$as->item($i)->getAttribute('href')]->name;
+ self::$url_map[$as->item($i)->getAttribute('href')]->addImplementor($this->name);
+ }
+
+
+
+
+
+ }
+ function addImplementor($n)
+ {
+ if (!in_array($n, $this->implementors)) {
+ $this->implementors[] = $n;
+ }
+ }
+ function expandImplementors($exclude = array())
+ {
+ $exclude[] = $this->name;
+ $add = array();
+ $orig = $this->implementors;
+ foreach($orig as $c) {
+ if (in_array($c, $exclude)) {
+ continue;
+ }
+ $cl = self::$all[$c]->expandImplementors($exclude);
+ foreach($cl as $cc) {
+ if (!in_array($cc, $this->implementors)) {
+ $this->implementors[]= $cc;
+ }
+ }
+ }
+ return $this->implementors;
+
+ }
+ function realImplementors()
+ {
+
+ $this->realImplementors = array();
+ foreach($this->implementors as $c) {
+ if (self::$all[$c]->isAbstract) {
+ return;
+ }
+ $this->realImplementors[] = $c;
+ }
+
+ }
+
+ function readDocs()
+ {
+ $this->parseHTML();
+ foreach($this->events as $e) {
+ $e->parseHTML();
+ }
+ foreach($this->methods as $e) {
+ $e->parseHTML();
+ }
+ foreach($this->props as $e) {
+ $e->parseHTML();
+ }
+ // loop through children.
+
+ }
+ function toTreeArray()
+ {
+ $cn = array();
+ foreach($this->cn as $e) {
+ if (in_array(get_class($e) , array('eClass', 'eMixin'))) {
+ $cn[] = $e->toTreeArray();
+ }
+ }
+ $child = $this->prop('child');
+ $child = $child ? $child : $this->prop('children');
+ $child = $child ? $child : $this->prop('home'); // MaterialApp??
+ // above might be a constant... - technically we could work out the type of that..
+ if ($child && !in_array(get_class($child), array( 'eProperty', 'Prop'))) {
+ $child = false;
+ }
+ $childtypes = 0;
+ $childtype = '';
+
+
+ // to complicated to check if these are widget children ... some are wrappers around
+
+ if ($child ) {
+ $childtypes = $child->isA('dart:core.List') ? 2 : 1;
+ $childtype = count($child->types) ? array_pop($child->types) : $child->type;
+
+ }
+
+ return array(
+ 'name' => $this->name,
+ 'is_class' => true,
+ 'cn' => $cn,
+ 'extends' => $this->extends,
+ 'is_abstract' => $this->isAbstract,
+ 'childtypes' => $childtypes,
+ 'childtype' => $childtype,
+ 'implementors' => $this->realImplementors, // this is not really complete...
+ );
+ }
+ function isA($name)
+ {
+ return in_array($name,$this->extends);
+ }
+ function prop($name)
+ {
+ foreach($this->props as $p) {
+ if ($p->name == $name) {
+ return $p;
+ }
+ }
+ return false;
+ }
+
+
+}
+class eMixin extends eClass
+{
+ function parseHTML()
+ {
+ $dom = Obj::parseHTML();
+ }
+}
+class eConstant extends Obj
+{
+ var $type = '';
+ function parseHTML()
+ {
+ $dom = Obj::parseHTML();
+ }
+
+}
+class eEnum extends eClass // enums look alot like classes..
+{
+ var $type = '';
+ function parseHTML()
+ {
+ $dom = Obj::parseHTML();
+ }
+}
+
+class eProperty extends Obj
+{
+ var $name = '';
+ var $type = '?';
+ var $desc = '';
+ var $memberOf = '';
+ function parseHTML()
+ {
+ $dom = Obj::parseHTML();
+ }
+}
+
+class Prop extends Obj {
+ var $name = '';
+ var $type = '';
+ var $types = array(); // generics...
+ var $desc = '';
+ var $memberOf = '';
+ var $isConstant = false;
+ function parseHTML()
+ {
+ $dom = Obj::parseHTML();
+ // work out the type..
+ $rt = $this->getElementsByClassName($dom, 'returntype');
+ $this->parseType($rt->item(0));
+ }
+ function isA($name)
+ {
+ if (empty($this->types)) {
+ return $name == $this->type;
+ }
+
+ if (in_array($name,$this->types)) {
+ return true;
+ }
+ foreach($this->types as $ty) {
+ if (!isset(eClass::$all[$ty])) {
+ print_R($this);
+ die("could not find type $ty\n");
+ }
+ if (in_array($name, eClass::$all[$ty]->extends)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+
+
+class eMethod extends Obj { // doubles up for events? - normally 'on' is the name
+ var $name = '';
+ var $type = ''; // return...
+ var $desc = '';
+ var $static = false;
+ var $memberOf = '';
+ var $sig = '';
+ var $params = array();
+
+ //"isStatic" : false,
+ var $isConstructor = false;
+ //"example" : "",
+ // "deprecated" : "",
+ // "since" : "",
+ // "see" : "",
+ // return_desc
+ function parseHTML()
+ {
+
+ $dom = parent::parseHTML();
+ $sp = $this->getElementsByClassName($dom,'returntype')->item(0);
+ $this->parseType($sp);
+
+ // params...
+ $ar = $this->getElementsByClassName($dom,'parameter');
+ for($i =0;$i<$ar->length;$i++) {
+ $this->params[] = new Param( $ar->item($i) );
+ }
+
+ return $dom;
+ }
+
+}
+class eConstructor extends eMethod {
+ function parseHTML()
+ {
+
+ $dom = Obj::parseHTML();
+ // doesnt have a 'type'
+ // params...
+ $ar = $this->getElementsByClassName($dom,'parameter');
+ for($i =0;$i<$ar->length;$i++) {
+ $this->params[] = new Param( $ar->item($i) );
+ }
+
+ return $dom;
+ }
+}
+
+
+class eFunction extends eMethod
+{
+ function __construct($ar)
+ {
+ parent::__construct($ar);
+ eClass::$all[$this->name] = $this;
+ eClass::$url_map[$this->href] = $this;
+ Ns::add($this);
+ }
+ function readDocs()
+ {
+ $this->parseHTML();
+ // loop through children.
+
+ }
+ function parseHTML()
+ {
+
+ $dom = parent::parseHTML();
+ }
+}
+class eTypedef extends eFunction
+{
+
+}
+class Param extends Obj {
+ var $name = '';
+ var $type = '';
+ var $desc = '';
+ var $isOptional = true;
+ function __construct($node)
+ {
+
+ $ar = $node->getElementsByTagName('span');
+ if (!$ar->length) {
+ echo "mssing paramter info", $this->innerHTML($node); exit;
+ }
+ for($i = 0; $i < $ar->length; $i++) {
+
+ switch($ar->item($i)->getAttribute('class')) {
+ case 'parameter-name':
+ $this->name = $ar->item($i)->textContent;
+ break;
+ case 'type-annotation':
+ $this->parseType($ar->item($i));
+ break;
+
+ }
+ }
+
+
+
+ }
+}
\ No newline at end of file