1<?php
2/**
3 *
4 * This is a TEST sample, designed to by parsed by PHP_UML itself.
5 *
6 * Do not use it.
7 * It is probably an old version, already out of date.
8 *
9 * PHP version 5
10 *
11 * @category PHP
12 * @package  PHP_UML::tests
13 * @author   Baptiste Autin <ohlesbeauxjours@yahoo.fr>
14 * @license  http://www.gnu.org/licenses/lgpl.html LGPL License 3
15 * @link     http://pear.php.net/package/PHP_UML
16 */
17
18
19/*
20 * PHP File scanner
21 */
22abstract class PHP_UML_Scanner
23{
24    /**
25     * Directories to ignore during scan
26     * @var array
27     */
28    public $ignoredDirectories = array();
29
30    /**
31     * Recursive search
32     * @var bool
33     */
34    public $subDirectories = true;
35
36    /**
37     * Traverse recursively the directories for files to parse
38     *
39     * @param string $dir   Path folder to look into
40     * @param int    $level Level of recursion
41     *
42     * @return void
43     */
44    protected function traverseDirectory($dir, $level = 1)
45    {
46        if (is_dir($dir)) {
47            $this->atFolderIn($level, $dir);
48            if ($dh = opendir($dir)) {
49                while (($file = readdir($dh)) !== false) {
50                    if (array_search($file, $this->ignoredDirectories)===false) {
51                        if (filetype($dir.$file) == 'dir') {
52                            if ($this->subDirectories) {
53                                $this->traverseDirectory(
54                                    $dir.$file.DIRECTORY_SEPARATOR, $level+1
55                                );
56                            }
57                        }
58                        else {
59                            $this->atFile($level, $dir.$file);
60                        }
61                    }
62                }
63                closedir($dh);
64            }
65            $this->atFolderOut($level, $dir);
66        }
67    }
68}
69
70
71/**
72 * The main class to instantiate
73 *
74 */
75class PHP_UML extends PHP_UML_Scanner
76{
77    const FILE      = 1;
78    const DIR_OPEN  = 2;
79    const DIR_CLOSE = 3;
80
81    /**
82     * Extensions of files to scan
83     * @var Array
84     */
85    public $acceptedExtensions = array('php');
86
87    /**
88     * Filenames are added to classes and interfaces
89     * @var bool
90     */
91    public $tagFilename = true;
92
93    /**
94     * Each file generates an UML:Artifact (in the logicial view)
95     * @var bool
96     */
97    public $pageAsArtifact = true;
98
99    /**
100     * A component view is created at root model, with the whole scanned file system
101     * inside (as components)
102     * @var bool
103     */
104    public $componentView = true;
105
106    /**
107     * Docblocks are read (package, param and return). This includes class,
108     * function and file comments.
109     * @var bool
110     */
111    public $docblocks = true;
112
113    /**
114     * Keep the PHP variable prefix $
115     * @var bool
116     */
117    public $dollar = true;
118
119    /**
120     * A reference to a PHP_UML_Metamodel_Superstructure object
121     * Either parseFile() or parserDirectory() set it, once their job is done.
122     * Or you can set it yourself with a predefined instance of superstructure.
123     * @var PHP_UML_Metamodel_Superstructure
124     */
125    public $model;
126
127    /**
128     * XML Encoding (see the constructor)
129     * @var string
130     */
131    private $_xmlEncoding;
132
133    /**
134     * The concatened XMI string
135     * @var string
136     */
137    private $_xmi = '';
138
139    /**
140     * A reference to a PHP_UML_PHP_Parser object
141     * @var PHP_UML_PHP_Parser
142     */
143    private $_parser;
144
145    /**
146     * A reference to a PHP_UML_XMI_Factory object
147     * @var PHP_UML_XMI_Factory
148     */
149    private $_factory;
150
151    /**
152     * Original directory path
153     * @var string
154     */
155    private $_originalDir = '';
156
157    /**
158     * Stack of parsed files and folders. Used for building the filesystem tree.
159     * @var array
160     */
161    private $_visited = array();
162
163    /**
164     * Start position of the scanned filepath
165     * @var int
166     */
167    private $_basePathStart = 0;
168
169
170    public function __construct()
171    {
172    }
173
174    /**
175     * Parse a PHP file, and builds the resulting XMI.
176     *
177     * @param mixed  $filename File(s) to parse. Can be a single file,
178     *                         or an array of files.
179     * @param string $model    Name of the model placed at root (enclosing pkg)
180     *
181     * It is the "PHP global" namespace.
182     */
183    public function parseFile($filename, $model = 'default')
184    {
185        $this->_parser = new PHP_UML_PHP_Parser($model, $this->docblocks, $this->dollar);
186
187        $this->_visited = array();
188
189        if (!is_array($filename)) {
190            $filename = array($filename);
191        }
192
193        foreach ($filename as $filename_item) {
194            if (file_exists($filename_item)) {
195                $filename_item        = realpath($filename_item);
196                $name                 = basename($filename_item);
197                $this->_originalDir   = dirname($filename_item);
198                $this->_basePathStart = 1+strlen($this->_originalDir);
199                $path                 = $this->_originalDir.DIRECTORY_SEPARATOR;
200                $this->atFile(1, $path.$name);
201            }
202            else
203                throw new PHP_UML_Exception($filename_item.' : file not found.');
204        }
205        $this->_parser->finalize();
206        $this->model = &$this->_parser->model;
207    }
208
209    /**
210     * Parse a PHP file, and builds the resulting XMI.
211     *
212     * @param mixed  $path  Path(s) of the directories. Can be a single path,
213     *                      or an array of pathes.
214     * @param string $model Name of the model placed at root (enclosing pkg)
215     */
216    public function parseDirectory($path, $model = 'default')
217    {
218        $this->_parser = new PHP_UML_PHP_Parser($model, $this->docblocks, $this->dollar);
219
220        $this->_visited = array();
221
222        array_push($this->ignoredDirectories, '.');
223        array_push($this->ignoredDirectories, '..');
224
225        if (!is_array($path)) {
226            $path = array($path);
227        }
228
229        foreach ($path as $path_item) {
230            $this->_originalDir   = realpath($path_item);
231            $this->_basePathStart = 1+strlen($this->_originalDir);
232            if ($this->_originalDir != '') {
233                $this->traverseDirectory($this->_originalDir.DIRECTORY_SEPARATOR);
234            }
235            else
236                throw new PHP_UML_Exception($path_item.' : unknown path.');
237        }
238        $this->_parser->finalize();
239        $this->model = &$this->_parser->model;
240    }
241
242    /**
243     * XMI Generator
244     * Generates XMI corresponding to the PHP model stored in $this->model.
245     *
246     * If you need to use this XMI Generator without any previous PHP parsing,
247     * simply set $this->model with a proper PHP_UML_Metamodel_Superstructure object
248     *
249     * @param float  $version     XMI Version For XMI 1.x, any value below 2.
250     *                                        For XMI 2.1, any value above or equal to 2.
251     * @param string $xmlEncoding XML Encoding
252     */
253    public function generateXMI($version = 2.1, $xmlEncoding = 'iso-8859-1')
254    {
255        $this->_xmlEncoding = $xmlEncoding;
256        $this->_xmi         = '<?xml version="1.0" encoding="'.$this->_xmlEncoding.'"?>';
257        $this->_factory     = PHP_UML_XMI_Factory::factory($version);
258
259        if(empty($this->model)) {
260            throw new PHP_UML_Exception('No model given');
261        }
262
263        $_root       = &$this->model->packages->get(0);
264        $this->_xmi .= $this->_factory->getModelOpen($_root);
265
266        foreach ($this->model->datatypes->getIterator() as $type)
267            $this->_xmi .= $this->_factory->getDatatype($type);
268
269        $this->addPackage($_root, true);
270
271        if ($this->componentView) {
272            $this->_xmi .= $this->_factory->getComponentView($this->_visited);
273        }
274        $this->_xmi .= $this->_factory->getModelClose();
275    }
276
277    /**
278     * Save the previously generated XMI to a file.
279     *
280     * @param string $output_file Filename
281     */
282    public function saveXMI($output_file)
283    {
284        if ($ptr = fopen($output_file, 'w+')) {
285            fwrite($ptr, $this->XMI);
286            fclose($ptr);
287        }
288        else
289            throw new PHP_UML_Exception(
290                'File '.$output_file.' could not be created.'
291            );
292    }
293
294    /**
295     * Save a UML Profile XMI-suited with PHP_UML.
296     *
297     * THIS IS EXPERIMENTAL.
298     * Only XMI and UML >= 2.x
299     *
300     * @param string $output_file Filename
301     */
302    private function _saveXMIProfile($output_file)
303    {
304        if ($ptr = fopen($output_file, 'w+')) {
305            fwrite($ptr, '<?xml version="1.0" encoding="'.$this->_xmlEncoding.'"?>'.
306                $this->_factory->getProfile()
307            );
308            fclose($ptr);
309        }
310        else
311            throw new PHP_UML_Exception(
312                'File '.$output_file.' could not be created.'
313            );
314    }
315
316    /**
317     * Accessor to the XMI.
318     *
319     * @param string $name Must be "XMI" or "parsed"
320     *
321     * @return string The XMI code, or a PHP_UML_Parser_Result object
322     */
323    public function __get($name)
324    {
325        switch($name) {
326        case 'XMI':
327            if (strtolower($this->_xmlEncoding)=='utf-8')
328                return utf8_encode($this->_xmi);
329            else
330                return $this->_xmi;
331            break;
332        case 'XMIProfile':
333            return $this->_factory->getProfile();
334            break;
335        default:
336            return null;
337        }
338    }
339
340    /**
341     * Function executed each time a new file is traversed
342     *
343     * @param int    $level    Level of recursion in the sub-directories
344     * @param string $pathfile Current file path
345     */
346    protected function atFile($level, $pathfile)
347    {
348        $path_parts = pathinfo($pathfile);
349        if (isset($path_parts['extension'])) {
350            $extension = $path_parts['extension'];
351        }
352        else {
353            $extension = '';
354        }
355        if (in_array($extension, $this->acceptedExtensions)) {
356            $this->_parser->parse($pathfile);
357        }
358        $this->_visited[] = array(
359            self::FILE => substr($pathfile, $this->_basePathStart)
360        );
361    }
362
363    /**
364     * Enters a new folder
365     *
366     * @param int    $level Level of recursion
367     * @param string $dir   Name of folder
368     */
369    protected function atFolderIn($level, $dir)
370    {
371        $this->_visited[] = array(self::DIR_OPEN => $dir);
372    }
373
374    /**
375     * Exits a folder
376     *
377     * @param int    $level Level of recursion
378     * @param string $dir   Name of folder
379     */
380    protected function atFolderOut($level, $dir)
381    {
382        $this->_visited[] = array(self::DIR_CLOSE => $dir);
383    }
384
385    /**
386     * Traverses all packages, and adds recursively the elements found
387     * to the "xmi" string property.
388     *
389     * @param PHP_UML_Metamodel_Package $package  New package to traverse
390     * @param bool                      $stripTag Omit package XMI tag
391     */
392    protected function addPackage(PHP_UML_Metamodel_Package $package, $stripTag = false)
393    {
394        if (!$stripTag) {
395            $this->_xmi .= $this->_factory->getPackageOpen($package);
396        }
397
398        foreach ($package->ownedType as &$elt) {
399            if (get_class($elt)=='PHP_UML_Metamodel_Interface')
400                $this->_xmi .= $this->_factory->getInterface($elt);
401            else
402                $this->_xmi .= $this->_factory->getClass($elt);
403        }
404
405        foreach ($package->nestedPackage as $idx)
406            $this->addPackage($this->model->packages->get($idx));
407
408        /*if($this->pageAsArtifact) {
409            $files_list = self::getFilesInPackage($obj);
410            $this->_xmi .= $this->_factory->getArtifacts(
411                $files_list, $package
412            );
413        }*/
414        if (!$stripTag) {
415            $this->_xmi .= $this->_factory->getPackageClose();
416        }
417    }
418
419    /**
420     * Filename part of a given path
421     *
422     * @param string $x Filename
423     *
424     * @return string
425     */
426    private static function _getFilename($x)
427    {
428        $pathinfo = pathinfo($x);
429        return $pathinfo['filename'];
430    }
431
432    /**
433     * Basename part of a given path
434     *
435     * @param string $x Filename
436     *
437     * @return string
438     */
439    private static function _getBasename($x)
440    {
441        $pathparts = pathinfo(realpath($x));
442        return $pathparts['basename'];
443    }
444}
445
446
447/**
448 * Subclass for PHP_UML_Exception
449 *
450 */
451class PHP_UML_Exception extends PEAR_Exception
452{
453}
454
455/**
456 * Maintains of stack of warning messages. Worth to being checked, especially
457 * if multiple classes in your PHP files have the same name...
458 */
459class PHP_UML_Warning
460{
461    /**
462     * The $stack to read.
463     * @var array
464     */
465    static public $stack;
466
467    /**
468     * Adds a message to the pile
469     *
470     * @param string $message The warning message to add
471     */
472    static public function add($message)
473    {
474        self::$stack[] = $message;
475    }
476
477    /**
478     * Clears the pile
479     */
480    static public function clear()
481    {
482        self::$stack = array();
483    }
484}
485
486
487/**
488 * Abstract class to build UML elements through XMI code.
489 * Only basic UML concepts are available.
490 * To deal with the two different versions of XMI (1.4 and 2.1), you must use one of
491 * the two specialized versions : PHP_UML_XMI_Factory1, or PHP_UML_XMI_Factory2
492 *
493 */
494abstract class PHP_UML_XMI_Factory
495{
496    const EXPORTER_NAME = 'PEAR::PHP_UML';
497    const PHP_FILE      = 'PHP File';
498
499    static public $stereotypes = array('File', self::PHP_FILE);
500    static public $extensions  = array(''=>'File', 'php'=>self::PHP_FILE);
501
502    /**
503     * Retrieves the ID of a stereotype, given a filename
504     *
505     * @param string $filename The file name
506     *
507     * @return string The PHP_UML ID of the matching extension
508     */
509    static protected function guessStereotype($filename = '')
510    {
511        $path_parts = pathinfo($filename);
512        $extension  = isset($path_parts['extension']) ? $path_parts['extension'] : '';
513        if (isset(self::$extensions[$extension]))
514            return self::generateID('stereotype', self::$extensions[$extension]);
515        else
516            return self::generateID('stereotype', self::$extensions['']);
517    }
518
519    static protected function generateID($type, $element)
520    {
521        return md5(self::EXPORTER_NAME.'#'.$type.'#'.$element);
522    }
523
524    protected static function getFilesInPackage(Array &$package)
525    {
526        $files_list = array();
527        if (!empty($package)) {
528            foreach ($package as $c => &$value) {
529                if (!in_array($value['file'], $files_list))
530                    $files_list[] = $value['file'];
531            }
532        }
533        return $files_list;
534    }
535
536    /**
537     * Insert a component view of the scanned file system.
538     * Files are treated as components (all files are inserted)
539     * Folders are treated as subsystems in UML1, and as nested components in UML2.
540     *
541     * @param array &$obj Visited files
542     *
543     * @return string XMI
544     */
545    public function getComponentView(Array &$obj)
546    {
547        $str = $this->getSubsystemOpen('Component View');
548        foreach ($obj as $value) {
549            $keys    = array_keys($value);
550            $type    = $keys[0];
551            $element = $value[$type];
552            switch($type) {
553            case PHP_UML::FILE :
554                $str .= $this->getComponent(
555                    basename($element), $element, self::guessStereotype($element)
556                );
557                break;
558            case PHP_UML::DIR_OPEN :
559                $str .= $this->getSubsystemOpen(basename($element), $element);
560                break;
561            case PHP_UML::DIR_CLOSE :
562                $str .= $this->getSubsystemClose();
563                break;
564            }
565        }
566        $str .= $this->getSubsystemClose();
567        return $str;
568    }
569
570    /**
571     * Factory method
572     *
573     * @param int $version XMI version
574     *
575     * @return PHP_UML_XMI_Factory
576     */
577    static function factory($version)
578    {
579        if ($version < 2)
580            return new PHP_UML_XMI_Factory1();
581        else
582            return new PHP_UML_XMI_Factory2();
583    }
584}
585
586/**
587 * Implementation class to create XMI in version 1
588 *
589 */
590class PHP_UML_XMI_Factory1 extends PHP_UML_XMI_Factory
591{
592    const XMI_VERSION = 1.2;
593    const UML_VERSION = 1.4;
594
595    const DEFAULT_CLASSIFIER_ATT = ' visibility="public" isAbstract="false"
596        isSpecification="false" isRoot="false" isLeaf="false" ';
597
598    /**
599     * Formates the XMI header
600     *
601     * @param string &$model Name of the model (root package)
602     */
603    public function getModelOpen(PHP_UML_Metamodel_Package &$model)
604    {
605        $str = '<XMI xmi.version="'.self::XMI_VERSION.'"
606            xmlns:UML="http://www.omg.org/spec/UML/1.4">
607            <XMI.header>
608                <XMI.documentation>
609                    <XMI.exporter>'.self::EXPORTER_NAME.'</XMI.exporter>
610                </XMI.documentation>
611                <XMI.metamodel XMI.name="UML" XMI.version="'.self::XMI_VERSION.'" />
612            </XMI.header>
613            <XMI.content>
614            <UML:Model name="'.$model->name.'"
615                xmi.id="'.parent::generateID('model', $model->name).'" '.
616                self::DEFAULT_CLASSIFIER_ATT.'>
617                <UML:Namespace.ownedElement>';
618
619        foreach (self::$stereotypes as $item)
620            $str .= '<UML:Stereotype xmi.id="'.parent::generateID('stereotype', $item).'"
621                name="'.$item.'" '.self::DEFAULT_CLASSIFIER_ATT.' />';
622
623        $str .= '<UML:Stereotype xmi.id="'.parent::generateId('stereotype', 'realize').'"
624            name="realize" '.self::DEFAULT_CLASSIFIER_ATT.'>
625            <UML:Stereotype.baseClass>Abstraction</UML:Stereotype.baseClass>
626             </UML:Stereotype>';
627        return $str;
628    }
629
630    /**
631     * Formates the opening tag for a package
632     *
633     * @param PHP_UML_Package &$package Package
634     */
635    public function getPackageOpen(PHP_UML_Metamodel_Package &$package)
636    {
637        return '<UML:Package xmi.id="'.parent::generateID('package', $package->name).
638            '" name="'.$package->name.'"><UML:Namespace.ownedElement>';
639    }
640
641    /**
642     * Formates the closing tag of a package
643     *
644     */
645    public function getPackageClose()
646    {
647        return '</UML:Namespace.ownedElement></UML:Package>';
648    }
649
650    /**
651     * Formates the XMI declaration of the main PHP types (official and unofficial ones)
652     *
653     * @param PHP_UML_Type &$type Datatype
654     */
655    public function getDatatype(PHP_UML_Metamodel_Type &$type)
656    {
657        return '<UML:DataType xmi.id="'.self::generateID('datatype', $type->name).
658            '" name="'.$type->name.'" visibility="public" isRoot="false" '.
659            ' isLeaf="false" isAbstract="false"/>';
660    }
661        /*$str .= '<UML:TagDefinition xmi.id="'.parent::generateID('tag','src_path').'"
662            name="src_path" isSpecification="false" tagType="String">
663            <UML:TagDefinition.multiplicity>
664                <UML:Multiplicity xmi.id="'.parent::generateID('tag','src_path_multi').'">
665                <UML:Multiplicity.range>
666                <UML:MultiplicityRange xmi.id="'.
667                    parent::generateID('tag','src_path_multi_range').
668                '" lower="0" upper="1" />
669                </UML:Multiplicity.range>
670                </UML:Multiplicity>
671            </UML:TagDefinition.multiplicity>
672            </UML:TagDefinition>';*/
673
674
675    /**
676     * Formates the closing tag of an XMI:Model
677     */
678    public function getModelClose()
679    {
680        return '</UML:Namespace.ownedElement></UML:Model></XMI.content></XMI>';
681    }
682
683
684    public function getClass(PHP_UML_Metamodel_Class &$class)
685    {
686        $strRealization = '';
687        $strGeneral     = '';
688
689        $cn = $class->name;
690        $nn = $class->package->name;
691
692        $str = '<UML:Class name="'.$cn.'" xmi.id="'.
693            parent::generateID('class', $nn.'#'.$cn).'" visibility="package"
694            isAbstract="'.($class->isAbstract?'true':'false').'">';
695
696        $str            .= self::_getGeneralizations($class, $strGeneral);
697        $strRealization .= self::_getRealizations($class);
698
699        $str .= '<UML:Classifier.feature>';
700
701        foreach ($class->ownedAttribute as &$property) {
702            $str .= self::getProperty($property);
703        }
704
705        foreach ($class->ownedOperation as &$operation)
706            $str .= self::getOperation($operation);
707
708        $str .= '</UML:Classifier.feature>';
709        $str .= '</UML:Class>';
710
711        return $str.$strGeneral.$strRealization;
712    }
713
714    public function getInterface(PHP_UML_Metamodel_Interface &$interface)
715    {
716        $in = $interface->name;
717        $nn = $interface->package->name;
718
719        $strGeneral = '';
720
721        $str = '<UML:Interface name="'.$in.'"'.
722            ' xmi.id="'.parent::generateID('class', $nn.'#'.$in).'"'.
723            ' visibility="package" isAbstract="true">';
724
725        $str .= '<UML:Classifier.feature>';
726        foreach ($interface->ownedOperation as &$operation)
727            $str .= self::getOperation($operation, $nn, $in);
728
729        $str .= '</UML:Classifier.feature>';
730        $str .= self::_getGeneralizations($interface, $strGeneral);
731
732        $str .= '</UML:Interface>';
733        return $str.$strGeneral;
734    }
735
736    static private function _getGeneralizations(PHP_UML_Metamodel_Type &$client, &$general)
737    {
738        $str = '';
739        $set = $client->superClass;
740        $cn  = $client->name;
741        $nn  = $client->package->name;
742
743        foreach ($set as &$gclass) {
744            if (!empty($gclass)) {
745                $gcn = $gclass->name;
746                $gnn = $gclass->package->name;
747                $id  = parent::generateID(
748                    'generalization', $nn.'#'.$cn.'-'.$gnn.'#'.$gcn
749                );
750
751                $str .= '<UML:GeneralizableElement.generalization>
752                    <UML:Generalization xmi.idref="'.$id.'"/>
753                    </UML:GeneralizableElement.generalization>';
754
755                $general .= '<UML:Generalization xmi.id="'.$id.'">
756                    <UML:Generalization.child><UML:Class xmi.idref="'.
757                    parent::generateID('class', $nn.'#'.$cn).
758                    '" /></UML:Generalization.child>
759                    <UML:Generalization.parent><UML:Class xmi.idref="'.
760                    parent::generateID('class', $gnn.'#'.$gcn).'"/>
761                    </UML:Generalization.parent></UML:Generalization>';
762            }
763        }
764        return $str;
765    }
766
767    static private function _getRealizations(PHP_UML_Metamodel_Class &$client)
768    {
769        $str = '';
770        $set = $client->implements;
771        $cn  = $client->name;
772        $nn  = $client->package->name;
773
774        foreach ($set as &$rclass) {
775            if (!empty($rclass)) {
776                $rcn  = $rclass->name;
777                $rnn  = $rclass->package->name;
778                $str .= '<UML:Abstraction '.
779                    'xmi.id="'.parent::generateID(
780                        'realize', $nn.'#'.$cn.'-'.$rnn.'#'.$rcn
781                    ).'" isSpecification="false">'.
782                    '<UML:ModelElement.stereotype><UML:Stereotype xmi.idref="'.
783                    parent::generateID('stereotype', 'realize').'"/>'.
784                    '</UML:ModelElement.stereotype>'.
785                    '<UML:Dependency.client><UML:Class xmi.idref="'.
786                    parent::generateID('class', $nn.'#'.$cn).
787                    '"/></UML:Dependency.client>'.
788                    '<UML:Dependency.supplier><UML:Interface xmi.idref="'.
789                    parent::generateID('class', $rnn.'#'.$rcn).'"/>'.
790                    '</UML:Dependency.supplier></UML:Abstraction>';
791            }
792        }
793        return $str;
794    }
795
796    static public function getProperty(PHP_UML_Metamodel_Property &$property)
797    {
798        $pn = $property->name;
799        $cn = $property->class->name;
800        $nn = $property->class->package->name;
801        $id = parent::generateID('property', $nn.'#'.$cn.'#'.$pn);
802
803        $str = '<UML:Attribute name="'.$pn.'"'.
804            ' xmi.id="'.$id.'"'.
805            ' visibility="'.$property->visibility.'" ';
806        if (!$property->isInstantiable)
807            $str .= ' isStatic="true" ownerScope="classifier" ';
808        else
809            $str .= 'ownerScope="instance" ';
810        if ($property->isReadOnly)
811            $str .= 'changeability="frozen" isReadOnly="true" ';
812
813        $str .= '>';
814        $str .= self::_getTypeAndDefProp($property,
815            parent::generateID('literal', $nn.'#'.$cn.'#'.$pn.'##dv')
816        );
817
818        $str .= '</UML:Attribute>';
819        return $str;
820    }
821
822    /**
823     * Special version of getTypeAndDefault for XMI 1.x
824     * Splits a parameter into its type, name and default value
825     *
826     * @param PHP_UML_TypedElement &$parameter Parameter to split
827     * @param int                  $id         Id of tag Expression
828     */
829    static private function _getTypeAndDefProp(PHP_UML_Metamodel_TypedElement &$parameter, $id)
830    {
831        $str = '';
832        if (get_class($parameter->type)=='PHP_UML_Class') {
833            $cn   = $parameter->type->name;
834            $nn   = $parameter->type->package->name;
835            $str .= '<UML:StructuralFeature.type>'.
836                '<UML:DataType xmi.idref="'.self::generateID(
837                    'class', $nn.'#'.$cn
838                ).'"/>'.
839                '</UML:StructuralFeature.type>';
840        }
841        elseif (get_class($parameter->type)=='PHP_UML_Type') {
842            $cn   = $parameter->type->name;
843            $str .= '<UML:StructuralFeature.type>'.
844                '<UML:DataType xmi.idref="'.self::generateID(
845                    'datatype', $cn
846                ).'"/>'.
847                '</UML:StructuralFeature.type>';
848        }
849        if ($parameter->default!='')
850            $str .= '<UML:Attribute.initialValue>'.
851                '<UML:Expression xmi.id="'.$id.'"'.
852                ' body="'.htmlentities($parameter->default, ENT_QUOTES, "UTF-8").'" />'.
853                '</UML:Attribute.initialValue>';
854        return $str;
855    }
856
857    /*
858    private function _getMethods(Array &$obj, $package, $c)
859    {
860        $str = '';
861        foreach ($obj as $m => &$v) {
862            $str .= '
863                <UML:Operation name="'.$m.'" xmi.id="'.
864                parent::generateID('method', $package.'#'.$c.'#'.$m).
865                '" visibility="'.$v['visibility'].'" ';
866            if ($v['static'])
867                $str .= ' isStatic="true"';
868            if ($v['abstract'])
869                $str .= ' isAbstract="true"';
870            $str .= ' isQuery="false" concurrency="sequential">'.
871                '<UML:BehavioralFeature.parameter>';
872
873            $str .= $this->prepareParameters($v, $package, $c, $m);
874
875            $str .= '</UML:BehavioralFeature.parameter></UML:Operation>';
876        }
877        return $str;
878    }*/
879
880    static public function getOperation(PHP_UML_Metamodel_Operation &$operation)
881    {
882        $on = $operation->name;
883        $cn = $operation->class->name;
884        $nn = $operation->class->package->name;
885
886        $str = '<UML:Operation name="'.$on.'" xmi.id="'.
887            parent::generateID('method', $nn.'#'.$cn.'#'.$on).'"
888            visibility="'.$operation->visibility.'" ';
889        if (!$operation->isInstantiable)
890            $str .= ' isStatic="true"';
891        if ($operation->isAbstract)
892            $str .= ' isAbstract="true"';
893
894        $str .= ' isQuery="false" concurrency="sequential">'.
895            '<UML:BehavioralFeature.parameter>';
896
897        $i = 0;
898        foreach ($operation->ownedParameter as &$parameter) {
899            $str .= self::getParameter($parameter, $i++);
900        }
901
902        $str .= '</UML:BehavioralFeature.parameter></UML:Operation>';
903
904        return $str;
905    }
906
907    /*
908    protected function getParameter($p, $c, $m, $name, $type, $kind, $default = '')
909    {
910        $temp = $p.'#'.$c.'#'.$m.'#'.$name;
911        $id = parent::generateID('parameter', $temp);
912        $str = '<UML:Parameter name="'.$name.'" xmi.id="'.$id.'" kind="'.$kind.'">'.
913            '<UML:Parameter.type>'.
914                '<UML:DataType xmi.idref="'.$type.'" />'.
915            '</UML:Parameter.type>';
916        if ($default != '') {
917            $id = parent::generateID('expression', $temp.'#'.$default);
918            $str .= '<UML:Parameter.defaultValue>'.
919                '<UML:Expression xmi.id="'.$id.'" body="'.$default.'"/>'.
920                '</UML:Parameter.defaultValue>';
921        }
922        $str .= '</UML:Parameter>';
923        return $str;
924    }*/
925
926    static public function getParameter(PHP_UML_Metamodel_Parameter &$parameter, $order = 0)
927    {
928        $pn = $parameter->name;
929        $on = $parameter->operation->name;
930        $cn = $parameter->operation->class->name;
931        $nn = $parameter->operation->class->package->name;
932        $id = parent::generateID('parameter', $nn.'#'.$cn.'#'.$on.'#'.$order);
933
934        $str  = '<UML:Parameter name="'.$pn.'" xmi.id="'.$id.'" '.
935            'kind="'.$parameter->direction.'">';
936        $str .= self::_getTypeAndDefault($parameter,
937                parent::generateID('literal', $nn.'#'.$cn.'#'.$on.'#'.$pn.'##dv')
938        );
939
940        $str .= '</UML:Parameter>';
941
942        return $str;
943    }
944
945    static private function _getTypeAndDefault(PHP_UML_Metamodel_TypedElement &$parameter, $id)
946    {
947        // Exception to MOF : a PHP class can have the name of a datatype
948        $str = '';
949        if (get_class($parameter->type)=='PHP_UML_Metamodel_Class') {
950            $cn   = $parameter->type->name;
951            $nn   = $parameter->type->package->name;
952            $str .= '<UML:Parameter.type>'.
953                '<UML:DataType xmi.idref="'.self::generateID(
954                    'class', $nn.'#'.$cn
955                ).'"/>'.
956                '</UML:Parameter.type>';
957        }
958        elseif (get_class($parameter->type)=='PHP_UML_Metamodel_Type') {
959            $cn   = $parameter->type->name;
960            $str .= '<UML:Parameter.type>'.
961                '<UML:DataType xmi.idref="'.self::generateID(
962                    'datatype', $cn
963                ).'"/>'.
964                '</UML:Parameter.type>';
965        }
966        if ($parameter->default!='')
967            $str .= '<UML:Parameter.defaultValue>'.
968                '<UML:Expression xmi.id="'.$id.'"'.
969                ' body="'.htmlentities($parameter->default, ENT_QUOTES, "UTF-8").'" />'.
970                '</UML:Parameter.defaultValue>';
971        return $str;
972    }
973
974    /*
975    static private function _getTaggedValue($id, $value, $id_type)
976    {
977        return '<UML:ModelElement.taggedValue><UML:TaggedValue xmi.id="'.$id.'">'.
978            '<UML:TaggedValue.dataValue>'.$value.'</UML:TaggedValue.dataValue>'.
979            '<UML:TaggedValue.type><UML:TagDefinition xmi.idref="'.$id_type.'" />'.
980            '</UML:TaggedValue.type>'.
981            '</UML:TaggedValue></UML:ModelElement.taggedValue>';
982    }
983    */
984
985    /**
986     * Gets the XMI code of the artifacts in a given package
987     *
988     * @param array  $files_list List of files to map to artifacts
989     * @param string $package    Package to retrieve (for ID generation)
990     *
991     * @return string XMI Code
992     */
993    public function getArtifacts(Array $files_list, $package)
994    {
995        $str = '';
996        foreach ($files_list as $name)
997            $str .= '<UML:Artifact xmi.id="'.
998                parent::generateID('artifact', $package.'#'.$name).'" name="'.$name.'">
999                <UML:ModelElement.stereotype>
1000                <UML:Stereotype xmi.idref="'.parent::generateID('stereotype', self::PHP_FILE).'"/>
1001                </UML:ModelElement.stereotype>
1002            </UML:Artifact>';
1003
1004        return $str;
1005    }
1006
1007    /**
1008     * Formates the XMI code for a subsystem
1009     *
1010     * @param string $name Name of the subsystem
1011     * @param string $id   Identifier (optional)
1012     *
1013     * @return string XMI Code
1014     */
1015    public function getSubsystemOpen($name, $id = null)
1016    {
1017        $str = '<UML:Subsystem name="'.$name.'" xmi.id="'.
1018            (is_null($id) ? parent::generateID('subsystem', $name) : $id).
1019            '" isInstantiable="false"><UML:Namespace.ownedElement>';
1020        return $str;
1021    }
1022
1023    /*
1024     * Formates the closing tag of a subsystem
1025     */
1026    public function getSubsystemClose()
1027    {
1028        return '</UML:Namespace.ownedElement></UML:Subsystem>';
1029    }
1030
1031    /**
1032     * Formates the XMI for a component
1033     *
1034     * @param string $name       Name of the component.
1035     * @param string $id         Identifier (optional)
1036     * @param string $stereotype Stereotype
1037     *
1038     * @return string XMI code
1039     */
1040    public function getComponent($name, $id = null, $stereotype = '')
1041    {
1042        return '<UML:Component xmi.id="'.
1043            (is_null($id) ? parent::generateID('component', $name) : $id).
1044            '" name="'.$name.'" '.
1045            self::DEFAULT_CLASSIFIER_ATT.' stereotype="'.$stereotype.'">'.
1046            '</UML:Component>';
1047    }
1048
1049    public function getProfile()
1050    {
1051    }
1052}
1053
1054
1055/**
1056 * Implementation class to create XMI in version 2. See version 1 for explanations.
1057 *
1058 *
1059 */
1060class PHP_UML_XMI_Factory2 extends PHP_UML_XMI_Factory
1061{
1062    const XMI_VERSION = '2.1';
1063    const UML_VERSION = '2.1.2';
1064
1065    const DEFAULT_CLASSIFIER_ATT = ' visibility="public" isAbstract="false" ';
1066
1067    /**
1068     * PHP_UML UML Profile (TODO)
1069     * @var string
1070     */
1071    public $profile = '';
1072
1073    public function getModelOpen(PHP_UML_Metamodel_Package &$model)
1074    {
1075        return '<xmi:XMI xmi:version="'.self::XMI_VERSION.'"
1076            xmlns:uml="http://schema.omg.org/spec/UML/'.self::UML_VERSION.'"
1077              xmlns:xmi="http://schema.omg.org/spec/XMI/'.self::XMI_VERSION.'">
1078                <xmi:Documentation exporter="'.self::EXPORTER_NAME.'"
1079                    exporterVersion="0.2" />
1080                <uml:Model xmi:type="uml:Model" name="'.$model->name.'"
1081                xmi:id="'.parent::generateID('model', $model->name).'" '.
1082                self::DEFAULT_CLASSIFIER_ATT.'>';
1083    }
1084
1085    public function getPackageOpen(PHP_UML_Metamodel_Package &$package)
1086    {
1087        return '<packagedElement xmi:type="uml:Package" xmi:id="'.
1088            parent::generateID('package', $package->name).
1089            '" name="'.$package->name.'">';
1090    }
1091
1092    public function getPackageClose()
1093    {
1094        return '</packagedElement>';
1095    }
1096
1097    public function getDatatype(PHP_UML_Metamodel_Type &$type)
1098    {
1099        return '<packagedElement xmi:type="uml:DataType"'.
1100            ' xmi:id="'.self::generateID('datatype', $type->name).'"'.
1101            ' name="'.$type->name.'" />';
1102    }
1103
1104    public function getModelClose()
1105    {
1106        return '</uml:Model></xmi:XMI>';
1107    }
1108
1109    public function getClass(PHP_UML_Metamodel_Class &$class)
1110    {
1111        $strRealization = '';
1112
1113        $cn  = $class->name;
1114        $nn  = $class->package->name;
1115        $str = '<packagedElement xmi:type="uml:Class" name="'.$cn.'" xmi:id="'.
1116            parent::generateID('class', $nn.'#'.$cn).'" visibility="package"
1117            isAbstract="'.($class->isAbstract?'true':'false').'">';
1118
1119        $str .= self::_getGeneralizations($class);
1120
1121        $strRealization .= self::_getRealizations($class);
1122
1123        foreach ($class->ownedAttribute as &$property)
1124            $str .= self::getProperty($property);
1125
1126        foreach ($class->ownedOperation as &$operation)
1127            $str .= self::getOperation($operation);
1128
1129        /*
1130        if ($tagFilename)
1131            $str .= $this->getComment(
1132                parent::generateID('comment', $nn.'#'.$cn), 'src_path', $class->file->name
1133        );*/
1134
1135        $str .= '</packagedElement>';
1136
1137        return $str.$strRealization;
1138    }
1139
1140    public function getInterface(PHP_UML_Metamodel_Interface &$interface)
1141    {
1142        $in  = $interface->name;
1143        $nn  = $interface->package->name;
1144        $str = '<packagedElement xmi:type="uml:Interface" name="'.$in.'"'.
1145            ' xmi:id="'.parent::generateID('class', $nn.'#'.$in).'"'.
1146            ' visibility="package" isAbstract="true">';
1147
1148        foreach ($interface->ownedOperation as &$operation)
1149            $str .= self::getOperation($operation, $nn, $in);
1150
1151        $str .= self::_getGeneralizations($interface);
1152
1153        /*
1154        if ($tagFilename)
1155            $str .= $this->getComment(
1156                parent::generateID('comment', $nn.'#'.$in),
1157                'src_path', $interface->file->name
1158        );*/
1159
1160        $str .= '</packagedElement>';
1161        return $str;
1162    }
1163
1164    static private function _getRealizations(PHP_UML_Metamodel_Class &$client)
1165    {
1166        $str = '';
1167        $set = $client->implements;
1168        $cn  = $client->name;
1169        $nn  = $client->package->name;
1170
1171        foreach ($set as &$rclass) {
1172            if (!empty($rclass)) {
1173                $rcn  = $rclass->name;
1174                $rnn  = $rclass->package->name;
1175                $str .= '<packagedElement xmi:type="uml:Realization" '.
1176                'xmi:id="'.parent::generateID('realize', $nn.'#'.$cn.'-'.$rnn.'#'.$rcn).'" '.
1177                'client="'.parent::generateID('class', $nn.'#'.$cn).'" '.
1178                'supplier="'.parent::generateID('class', $rnn.'#'.$rcn).'" '.
1179                'realizingClassifier="'.parent::generateID('class', $rnn.'#'.$rcn).'"/>';
1180            }
1181        }
1182        return $str;
1183    }
1184
1185    static private function _getGeneralizations(PHP_UML_Metamodel_Type &$client)
1186    {
1187        $str = '';
1188        $set = $client->superClass;
1189        $cn  = $client->name;
1190        $nn  = $client->package->name;
1191
1192        foreach ($set as &$gclass) {
1193            if (!empty($gclass)) {
1194                $gcn  = $gclass->name;
1195                $gnn  = $gclass->package->name;
1196                $str .= '<generalization xmi:type="uml:Generalization" '.
1197                    'xmi:id="'.parent::generateID('generalization', $nn.'#'.$cn.'-'.$gnn.'#'.$gcn).'"'.
1198                    ' general="'.parent::generateID('class', $gnn.'#'.$gcn).'"/> ';
1199            }
1200        }
1201        return $str;
1202    }
1203
1204    static public function getProperty(PHP_UML_Metamodel_Property &$property)
1205    {
1206        $pn = $property->name;
1207        $cn = $property->class->name;
1208        $nn = $property->class->package->name;
1209        $id = parent::generateID('property', $nn.'#'.$cn.'#'.$pn);
1210
1211        $str = '<ownedAttribute xmi:type="uml:Property"'.
1212            ' name="'.$pn.'"'.
1213            ' xmi:id="'.$id.'"'.
1214            ' visibility="'.$property->visibility.'" ';
1215        if (!$property->isInstantiable)
1216            $str .= ' isStatic="true"';
1217        if ($property->isReadOnly)
1218            $str .= ' isReadOnly="true" ';
1219
1220        $str .= '>';
1221        $str .= self::_getTypeAndDefault($property,
1222            parent::generateID('literal', $nn.'#'.$cn.'#'.$pn.'##dv')
1223        );
1224
1225        $str .= '</ownedAttribute>';
1226        return $str;
1227    }
1228
1229    static public function getOperation(PHP_UML_Metamodel_Operation &$operation)
1230    {
1231        $on = $operation->name;
1232        $cn = $operation->class->name;
1233        $nn = $operation->class->package->name;
1234
1235        $str = '<ownedOperation name="'.$on.'" xmi:id="'.
1236            parent::generateID('method', $nn.'#'.$cn.'#'.$on).'"
1237            visibility="'.$operation->visibility.'" ';
1238        if (!$operation->isInstantiable)
1239            $str .= ' isStatic="true"';
1240        if ($operation->isAbstract)
1241            $str .= ' isAbstract="true"';
1242        $str .= '>';
1243
1244        $i = 0;
1245        foreach ($operation->ownedParameter as &$parameter)
1246            $str .= self::getParameter($parameter, $i++);
1247
1248        $str .= '</ownedOperation>';
1249
1250        return $str;
1251    }
1252
1253    static public function getParameter(PHP_UML_Metamodel_Parameter &$parameter, $order = 0)
1254    {
1255        $pn   = $parameter->name;
1256        $on   = $parameter->operation->name;
1257        $cn   = $parameter->operation->class->name;
1258        $nn   = $parameter->operation->class->package->name;
1259        $id   = parent::generateID('parameter', $nn.'#'.$cn.'#'.$on.'#'.$pn.$order);
1260        $str  = '<ownedParameter name="'.$pn.'" xmi:id="'.$id.'" '.
1261            'direction="'.$parameter->direction.'">';
1262        $str .= self::_getTypeAndDefault($parameter,
1263                parent::generateID('literal', $nn.'#'.$cn.'#'.$on.'#'.$pn.'##dv')
1264        );
1265        $str .= '</ownedParameter>';
1266
1267        return $str;
1268    }
1269
1270    static private function _getTypeAndDefault(PHP_UML_Metamodel_TypedElement &$parameter, $id)
1271    {
1272        // Exception to MOF : a PHP class can have the name of a datatype
1273        $str = '';
1274        if (get_class($parameter->type)=='PHP_UML_Metamodel_Class') {
1275            $cn   = $parameter->type->name;
1276            $nn   = $parameter->type->package->name;
1277            $str .= '<type xmi:idref="'.self::generateID('class', $nn.'#'.$cn).'"/>';
1278        }
1279        elseif (get_class($parameter->type)=='PHP_UML_Metamodel_Type') {
1280            $cn   = $parameter->type->name;
1281            $str .= '<type xmi:idref="'.self::generateID('datatype', $cn).'"/>';
1282        }
1283        if ($parameter->default!='')
1284            $str .= '<defaultValue xmi:type="uml:LiteralString" xmi:id="'.$id.'"'.
1285                ' value="'.htmlentities($parameter->default, ENT_QUOTES, "UTF-8").'" />';
1286            //htmlentities($parameter->default, ENT_QUOTES, "UTF-8")
1287        return $str;
1288    }
1289
1290    public function getComment($id, $name, $body)
1291    {
1292        $body = '';
1293        return '<ownedComment xmi:type="uml:Comment"
1294            xmi:id="'.$id.'" name="'.$name.'" body="'.$body.'"/>';
1295    }
1296
1297    public function getArtifacts(Array &$obj = array(), $package = '')
1298    {
1299        $files_list = parent::getFilesInPackage($obj);
1300        $str        = '';
1301        foreach ($files_list as $name)
1302            $str .= '<packagedElement xmi:type="uml:Artifact"'.
1303                ' xmi:id="'.parent::generateID('artifact', $package.'#'.$name).'"'.
1304                ' name="'.$name.'"'.
1305                ' stereotype="'.parent::generateID('stereotype', self::PHP_FILE).'">'.
1306                '</packagedElement>';
1307        return $str;
1308    }
1309
1310    public function getSubsystemOpen($name, $id = null)
1311    {
1312        return '<packagedElement xmi:type="uml:Component" xmi:id="'.
1313            (is_null($id) ? parent::generateID('subsystem', $name) : $id).
1314            '" name="'.$name.'" '.self::DEFAULT_CLASSIFIER_ATT.'>';
1315    }
1316
1317    public function getSubsystemClose()
1318    {
1319        return '</packagedElement>';
1320    }
1321
1322    public function getComponent($name, $id = null)
1323    {
1324        return '<packagedElement xmi:type="uml:Component" xmi:id="'.
1325            (is_null($id) ? parent::generateID('component', $name) : $id).
1326            '" name="'.$name.'" '.self::DEFAULT_CLASSIFIER_ATT.' />';
1327    }
1328
1329    /**
1330     * Formates a Profile adapted to PHP_UML.
1331     *
1332     * TODO. Experimental.
1333     *
1334     * @return string
1335     */
1336    public function getProfile()
1337    {
1338        $str = '
1339        <uml:Profile xmi:version="'.self::XMI_VERSION.'"
1340        nsURI="http://PHP_UML" nsPrefix="PHP_UML"
1341        xmlns:uml="http://schema.omg.org/spec/UML/'.self::UML_VERSION.'/uml.xml"
1342        xmlns:xmi="http://schema.omg.org/spec/XMI/'.self::XMI_VERSION.'"
1343        xmi:id="'.parent::generateID('profile', 'PHP_UML').'" name="PHP_UML"
1344        metamodelReference="PHP_UML_Metamodel">
1345        <packageImport xmi:id="PHP_UML_Metamodel">
1346        <importedPackage href="http://schema.omg.org/spec/UML/'.self::UML_VERSION.'/uml.xml"/>
1347        </packageImport>
1348        <ownedMember xmi:type="uml:Stereotype" xmi:id="'.
1349        parent::generateID('stereotype', self::PHP_FILE).'" name="'.self::PHP_FILE.'" '.
1350        self::DEFAULT_CLASSIFIER_ATT.' />
1351        </uml:Profile>';
1352        return $str;
1353    }
1354
1355}
1356
1357/**
1358 * A combination of string iteration and regular expressions.
1359 * It stores all the elements if finds in MOF program elements :
1360 * $packages, $interfaces, $classes, $functions, $parameters
1361 *
1362 * Most navigabilities between associated elements are bidirectional
1363 * (the packages know their owned elements, and the classes know their
1364 * nesting package)
1365 * At first, relations use string references (the name of the element).
1366 * Once the parsing is completed, the method finalize() must be called,
1367 * so that the named references be replaced by PHP references (&$xxx).
1368 *
1369 *
1370 */
1371class PHP_UML_PHP_Parser
1372{
1373    /**
1374     * Regular expressions for a PHP variable
1375     */
1376    const PREG_VARIABLE = '[a-z_\\x7f-\\xff][a-z0-9_\\x7f-\\xff]*';
1377    const PREG_HEREDOC  = '<<<([^<\n\r]*)[\n\r]';
1378    const PREG_COMMENT  = '\/\/[^\n]*\n|\/\*.*\*\/|#[^\n]*\n';
1379    const PREG_PACKAGE  = '\*[ \t]+@package[ \t]+([^\s]+)\s';
1380
1381    /**
1382     * Reference to a PHP_UML_Metamodel_Superstructure
1383     * (where the parser stores all the program elements it finds)
1384     *
1385     * @var PHP_UML_Metamodel_Superstructure
1386     */
1387    public $model;
1388
1389    private $_text = '';
1390    private $_filename;
1391    private $_docblocks;
1392    private $_dollar;
1393    private $_cancel;
1394
1395    private $_packageSeqIdx;    // current pkg index
1396
1397    /**
1398     * Constructor
1399     *
1400     * @param string $root      Root package name
1401     * @param bool   $docblocks True = docblocks are scanned
1402     * @param bool   $dollar    True = $ in variables is kept
1403     */
1404    public function __construct($root, $docblocks = true, $dollar = true)
1405    {
1406        $this->_docblocks = $docblocks;
1407        $this->_dollar    = $dollar;
1408
1409        $this->model         = new PHP_UML_Metamodel_Superstructure();
1410        $this->_packageSeqId = $this->_addPackage($root);
1411    }
1412
1413    /**
1414     * Parses a PHP file
1415     *
1416     * @param string $filename File to parse
1417     */
1418    public function parse($filename)
1419    {
1420        if (file_exists($filename)) {
1421            $this->_text     = file_get_contents($filename);
1422            $this->_filename = $filename;
1423        }
1424        else
1425            throw new PHP_UML_Exception('File '.$filename.' does not exist.');
1426
1427        $f       = new PHP_UML_Metamodel_File;
1428        $f->name = $filename;
1429        $this->model->files->add($f);
1430
1431        $set         = array();
1432        $lenText     = strlen($this->_text);
1433        $modePHP     = false;
1434        $modeQuotesD = false;    // double quote
1435        $modeQuotesS = false;    // single quote
1436        $modeHeredoc = false;
1437        $modeQuotes  = $modeQuotesD || $modeQuotesS || $modeHeredoc;
1438        $heredoc     = '';
1439        $attributes  = array(); // a collector for attributes (public, static..)
1440        $clasName    = '';
1441        $clasType    = '';
1442        $propName    = '';
1443        $funcName    = '';
1444
1445        $lastCsLevel  = 0; // braces level at which current class is defined
1446        $lastFnLevel  = 0; // braces level at which current function is defined
1447        $lastFnPos    = 0; // character position of last visited function
1448        $lastPrPos    = 0; // character position of last visited prop. default. value
1449        $lastDocblock = '';
1450
1451        $modeClass      = false;
1452        $modeFunction   = false;
1453        $modeInterface  = false;
1454        $modeProperty   = false;
1455        $modeExpression = false;
1456
1457        $i        = 0;
1458        $level    = 0;    // curly braces level
1459        $levelPar = 0;    // parens level
1460
1461        $this->_packageSeqIdx = 0;
1462        if ($this->_docblocks) {
1463            // First, let's have a look at the file docblock :
1464            $package = $this->_getFilePackage();
1465            if ($package!='')
1466                $this->_packageSeqIdx = $this->_addPackage($package, 0);
1467        }
1468
1469        while($i<$lenText) {
1470            $one       = substr($this->_text, $i, 1);
1471            $two       = substr($this->_text, $i, 2);
1472            $remaining = substr($this->_text, $i);
1473
1474            if ((!$modePHP)) {
1475                if ($two=='<?') {
1476                    $modePHP = true;
1477                    $i      += 2;
1478                }
1479                else {
1480                    $nxt = strpos($this->_text, '<?', $i);
1481                    if ($nxt===false)
1482                        $i = $lenText;
1483                    else
1484                        $i = $nxt;
1485                }
1486            }
1487            else {
1488                if ((!$modeQuotes) && $two=='?>') {
1489                    $modePHP = false;
1490                    $i      += 2;
1491                }
1492                elseif ((!$modeQuotes) && $two=='/*') {
1493                    $nxt = strpos($this->_text, '*/', $i+2);
1494                    if ($nxt===false)
1495                        $i = $lenText;
1496                    else
1497                        $i = ($nxt+2);
1498                }
1499                elseif ((!$modeQuotes) && ($two=='//' || $one=='#')) {
1500                    $nxt = preg_match(
1501                        '/(\n|\?>)/', $this->_text, $set, PREG_OFFSET_CAPTURE, $i
1502                    );
1503                    if ($nxt==0)
1504                        $i = $lenText;
1505                    else
1506                        $i = $set[1][1];
1507                }
1508                elseif ($modeQuotes && $two=='\\\\') {
1509                    $i += 2;
1510                }
1511                elseif ($modeQuotesD && $two=='\"') {
1512                    $i += 2;
1513                }
1514                elseif ($modeQuotesS && $two=='\\\'') {
1515                    $i += 2;
1516                }
1517                elseif ((!$modeQuotes)
1518                    && preg_match('/^'.self::PREG_HEREDOC.'/s', $remaining, $set)>0
1519                ) {
1520                    $heredoc     = trim($set[1]);
1521                    $modeHeredoc = true;
1522                    $modeQuotes  = $modeQuotesD || $modeQuotesS || $modeHeredoc;
1523                    $i          += strlen($set[0]);
1524                }
1525                elseif ($modeHeredoc
1526                    && (preg_match('/^'.$heredoc.'/s', $remaining, $set)>0)
1527                ) {
1528                    $heredoc     = '';
1529                    $modeHeredoc = false;
1530                    $modeQuotes  = $modeQuotesD || $modeQuotesS || $modeHeredoc;
1531                    $i          += strlen($set[0]);
1532                }
1533                elseif ((!($modeQuotesS || $modeHeredoc)) && $this->_text[$i]=='"') {
1534                    $modeQuotesD = (!$modeQuotesD);
1535                    $modeQuotes  = $modeQuotesD || $modeQuotesS || $modeHeredoc;
1536                    $i++;
1537                }
1538                elseif ((!($modeQuotesD || $modeHeredoc)) && $this->_text[$i]=="'") {
1539                    $modeQuotesS = (!$modeQuotesS);
1540                    $modeQuotes  = $modeQuotesD || $modeQuotesS || $modeHeredoc;
1541                    $i++;
1542                }
1543                elseif ((!$modeQuotes)) {
1544                    if ($one=='{') {
1545                        if ($modeClass && $clasName!='') {
1546                            $idxNs = $this->_getClassDocIdx($lastDocblock);
1547                            if ($modeInterface)
1548                                $this->_addInterface($clasName, $attributes, $idxNs);
1549                            else
1550                                $this->_addClass($clasName, $attributes, $idxNs);
1551                            // Classes are not always defined at 1st level :
1552                            $lastCsLevel  = $level+1;
1553                            $attributes   = array();
1554                            $clasName     = '';
1555                            $lastDocblock = '';
1556                        }
1557                        $i++;
1558                        $level++;
1559
1560                    }
1561                    elseif ($one=='}') {
1562                        if ($modeClass && $level == $lastCsLevel)
1563                            $modeClass = false;
1564                        $level--;
1565                        $i++;
1566                    }
1567                    elseif ($one=='(') {
1568                        $levelPar++;
1569                        $i++;
1570                    }
1571                    elseif ($one==')') {
1572                        if ($modeFunction && $levelPar==1 && !$this->_cancel) {
1573                            $this->_addOperation(
1574                                $funcName, $attributes, $modeInterface
1575                            );
1576                            $str = substr($this->_text, $lastFnPos, $i-$lastFnPos);
1577                            if ($str!='')
1578                                $this->_addParameters($str, $lastDocblock);
1579                            $attributes   = array();
1580                            $propName     = '';
1581                            $modeFunction = false;
1582                            $lastDocblock = '';
1583                        }
1584                        $levelPar--;
1585                        $i++;
1586                    }
1587                    elseif ($this->_findNamespace($remaining, $set)>0) {
1588                        $this->_packageSeqIdx = $this->_addPackage($set[1], 0);
1589                        $i                   += strlen($set[0]);
1590                    }
1591                    elseif ($this->_findAttr($remaining, $set)>0) {
1592                        $attributes[]  = strtolower($set[1]);
1593                        $lastDocblock .= $this->_revDocblock(
1594                            substr($this->_text, 0, $i)
1595                        );
1596                        $i += strlen($set[0]);
1597                    }
1598                    elseif ($this->_findClass($remaining, $set)>0) {
1599                        // Class / Interface :
1600                        $modeClass     = true;
1601                        $clasName      = trim($set[2]);
1602                        $modeInterface = (strtolower($set[1])=='interface');
1603                        $lastDocblock .= $this->_revDocblock(
1604                            substr($this->_text, 0, $i)
1605                        );
1606                        $i += strlen($set[0]);
1607                    }
1608                    elseif ($modeClass && $clasName!=''
1609                    && $this->_findClassRelation($remaining, $set)>0) {
1610                        // Superclass :
1611                        $attributes[$set[1]] = $set[2];
1612                        $i += strlen($set[0]);
1613                    }
1614                    elseif ($modeClass && $level==$lastCsLevel
1615                    && $this->_findFunction($remaining, $set)>0
1616                    && !$this->_cancel) {
1617                        $lastDocblock .= $this->_revDocblock(
1618                            substr($this->_text, 0, $i)
1619                        );
1620                        $funcName      = $set[1];
1621                        $modeFunction  = true;
1622                        $levelPar      = 1;
1623                        $i            += strlen($set[0]);
1624                        $lastFnPos     = $i;
1625                        $lastFnLevel   = $level;
1626                    }
1627                    elseif ($modeClass && (!$modeFunction) && $level==$lastCsLevel
1628                    && (!$modeExpression)
1629                    && $this->_findProperty($remaining, $set)>0) {
1630                        $i           += strlen($set[0]);
1631                        $lastPrPos    = $i;
1632                        $propName     = $set[1];
1633                        $modeProperty = true;
1634                        if (isset($set[2]) && $set[2]=='=')
1635                            $modeExpression = true;
1636                    }
1637                    elseif ($modeProperty && $one==';' && !$this->_cancel) {
1638                        $default        = $this->_stripComments(
1639                            trim(substr($this->_text, $lastPrPos, $i-$lastPrPos))
1640                        );
1641                        $this->_addProperty($propName, $attributes, $default);
1642                        $modeProperty   = false;
1643                        $modeExpression = false;
1644                        $attributes     = array();
1645                        $lastDocblock   = '';
1646                        $propName       = '';
1647                        $i++;
1648                    }
1649                    else
1650                        $i++;
1651                }
1652                else
1653                    $i++;
1654            }
1655        }
1656    }
1657
1658    /**
1659     * Launches the resolution of the references for all stacks from root
1660     *
1661     * Every reference (a temporary string) is replaced by a PHP reference
1662     * to the corresponding type (that is, a class or a datatype)
1663     * To be run once the filesystem scan is complete.
1664     *
1665     */
1666    public function finalize()
1667    {
1668        $oDef = &$this->model->packages->get(0);
1669        $this->_resolveReferences($oDef, $oDef);
1670    }
1671
1672
1673    /**
1674     * Adds a package to the "packages" stack
1675     *
1676     * @param string $name    Name of the package
1677     * @param int    $baseIdx Current nesting package index
1678     *
1679     * @return int Index of the newly created package (or of the existing one)
1680     */
1681    private function _addPackage($name, $baseIdx = null)
1682    {
1683        $index = $this->model->packages->searchElement($name);
1684        if ($index===false) {
1685            $p                 = new PHP_UML_Metamodel_Package;
1686            $p->name           = $name;
1687            $p->nestingPackage = $baseIdx;
1688            $p->nestedPackage  = array();
1689            $this->model->packages->add($p);
1690            $index = $this->model->packages->key();
1691            if (!is_null($baseIdx)) {
1692                $parent = $this->model->packages->get($baseIdx);
1693                $parent->nestedPackage[] = $index;
1694            }
1695        }
1696        return $index;
1697    }
1698
1699    /**
1700     * Get the index of the corresponding @package class docblock
1701     *
1702     * @param string $classDocblock Preceding code (before the class declaration)
1703     *
1704     * @return int
1705     */
1706    private function _getClassDocIdx($classDocblock)
1707    {
1708        // Where's the class package ?
1709        if ($this->_docblocks) {
1710            // Is there a @package in the class docblock ?
1711            $r = $this->_findPackageInDocblock($classDocblock, $set);
1712            if ($r) {
1713                return $this->_addPackage($set[1], $this->_packageSeqIdx);
1714            }
1715        }
1716        return $this->_packageSeqIdx;
1717    }
1718
1719    /**
1720     * Adds an interface to the "interfaces" stack
1721     *
1722     * @param string $name          Interface name
1723     * @param array  $attr          Some interface attributes (superclasses)
1724     * @param int    $classPkgIndex The index of the current nesting package
1725     *
1726     */
1727    private function _addInterface($name, $attr, $classPkgIndex)
1728    {
1729        $c = new PHP_UML_Metamodel_Interface;
1730
1731        if (isset($attr['extends'])) {
1732            $c->superClass[] = trim($attr['extends']);
1733        }
1734        $c->name       = $name;
1735        $c->isAbstract = in_array('abstract', $attr);
1736        $c->file       = &$this->model->files->current();
1737        $nestingPkg    = $this->model->packages->get($classPkgIndex);
1738        $c->package    = &$nestingPkg;
1739        if ($this->_searchIntoPackage($c->package, $c->name)===false) {
1740            $nestingPkg->ownedType[] = &$c;
1741            $c->implements           = null;
1742            $this->model->interfaces->add($c);
1743        }
1744        else
1745            PHP_UML_Warning::add(
1746                'Interface '.$c->name.' already defined in '.$this->_filename
1747            );
1748    }
1749
1750    /**
1751     * Adds a class to the "classes" stack ($this->model->classes)
1752     *
1753     * @param string $name          Class name
1754     * @param array  $attr          Some class attributes (superclasses)
1755     * @param int    $classPkgIndex The index of the current nesting package
1756     */
1757    private function _addClass($name, $attr, $classPkgIndex)
1758    {
1759        $c = new PHP_UML_Metamodel_Class;
1760
1761        if (isset($attr['extends'])) {
1762            $c->superClass[] = trim($attr['extends']);
1763        }
1764        if (isset($attr['implements'])) {
1765            $imp = explode(',', $attr['implements']);
1766            foreach ($imp as $item) {
1767                $c->implements[] = trim($item);
1768            }
1769        }
1770        $c->name       = $name;
1771        $c->isAbstract = in_array('abstract', $attr);
1772        $c->file       = &$this->model->files->current();
1773        $nestingPkg    = $this->model->packages->get($classPkgIndex);
1774        $c->package    = &$nestingPkg;
1775        if ($this->_searchIntoPackage($c->package, $c->name)===false) {
1776            $nestingPkg->ownedType[] = &$c;
1777            $this->model->classes->add($c);
1778            $this->_cancel = false;
1779        }
1780        else {
1781            PHP_UML_Warning::add(
1782                'Class '.$c->name.' already defined in '.$this->_filename
1783            );
1784            $this->_cancel = true;
1785        }
1786    }
1787
1788    private function _addOperation($name, $attr, $modeInterface)
1789    {
1790        $f = new PHP_UML_Metamodel_Operation;
1791
1792        $f->name           = trim(str_replace('&', '', $name));
1793        $f->isInstantiable = !in_array('static', $attr);
1794        $f->isAbstract     = in_array('abstract', $attr);
1795        $f->visibility     = self::_getVisibility($attr);
1796        $this->model->functions->add($f);
1797        $obj = null;
1798        if ($modeInterface)
1799            $obj = &$this->model->interfaces->current();
1800        else
1801            $obj = &$this->model->classes->current();
1802        $obj->ownedOperation[] = &$f;
1803        $f->class              = &$obj;
1804    }
1805
1806    private function _addProperty($name, $attr, $default)
1807    {
1808        $p = new PHP_UML_Metamodel_Property;
1809
1810        $p->name           = $name;
1811        $p->isReadOnly     = in_array('const', $attr);
1812        $p->isInstantiable = !(in_array('static', $attr) || $p->isReadOnly);
1813        $p->visibility     = self::_getVisibility($attr);
1814        $p->default        = self::_stripComments($default);
1815        $p->type           = self::_guessType($p->default);
1816
1817        $class                   = &$this->model->classes->current();
1818        $class->ownedAttribute[] = &$p;
1819        $p->class                = &$class;
1820    }
1821
1822    private function _addParameters($set, $docblock)
1823    {
1824        $function = &$this->model->functions->current();
1825
1826        $docblockParameter = array();
1827        if ($this->_docblocks) {
1828            $set_comment = $this->_findParamInDocblock($docblock);
1829            $return = false;
1830            foreach ($set_comment as $k) {
1831                if (substr($k[1], 0, 6)=='return' && !$return) {
1832                    $pr                         = new PHP_UML_Metamodel_Parameter;
1833                    $pr->name                   = 'return';
1834                    $pr->direction              = 'return';
1835                    $pr->type                   = $k[2];
1836                    $pr->operation              = &$function;
1837                    $function->ownedParameter[] = &$pr;
1838                    $return                     = true;
1839                }
1840                elseif ($k[1]=='param') {
1841                    $docblockParameter[self::_cleanVariable($k[3])] = $k[2];
1842                }
1843            }
1844        }
1845        $arr = explode(',', $set);
1846        foreach ($arr as &$parameters) {
1847            $parametre     = explode('=', $parameters);
1848            $parameterName = $this->_cleanParameter($parametre[0]);
1849
1850            // Any default value given ?
1851            if (count($parametre)>1)
1852                $default = $this->_cleanParameter($parametre[1]);
1853            else
1854                $default = '';
1855            // Any @param in the method docblock ?
1856            $tmpParameterName = self::_cleanVariable($parameterName);
1857            if (isset($docblockParameter[$tmpParameterName]))
1858                $param = $docblockParameter[$tmpParameterName];
1859            else
1860                $param = '';
1861            // By ref or by value ? (inout/in)
1862            if (strpos($parameterName, '&')===false)
1863                $direction = 'in';
1864            else
1865                $direction = 'inout';
1866
1867            list($name, $type) = self::_splitNameType($parameterName, $default, $param);
1868
1869            $p            = new PHP_UML_Metamodel_Parameter;
1870            $p->name      = $name;
1871            $p->default   = $default;
1872            $p->type      = $type;
1873            $p->direction = $direction;
1874            $p->operation = &$function;
1875            $this->model->parameters->add($p);
1876            $function->ownedParameter[] = $p;
1877        }
1878    }
1879
1880    private function _findClass(&$text, &$set)
1881    {
1882        return preg_match(
1883            '/^\s+(class|interface)\s+('.self::PREG_VARIABLE.')/si', $text, $set
1884        );
1885    }
1886
1887    private function _findClassRelation(&$text, &$set)
1888    {
1889        $r = preg_match(
1890            '/^\s+(implements)\s+(('.self::PREG_VARIABLE.'[ \t,]*)+)?/si', $text, $set
1891        );
1892        if ($r==0) {
1893            $r = preg_match(
1894                '/^\s+(extends)\s+('.self::PREG_VARIABLE.')?/si', $text, $set
1895            );
1896            if ($r>0)
1897                $set = array($set[0], 'extends', $set[2]);
1898        }
1899        else
1900            $set = array($set[0], 'implements', $set[2]);
1901        return $r;
1902    }
1903
1904    private function _findNamespace(&$text, &$set)
1905    {
1906        return preg_match('/^namespace\s+('.
1907            self::PREG_VARIABLE.')[ \t]*;/si', $text, $set
1908        );
1909    }
1910
1911    private function _findPackageInDocblock($text, &$set)
1912    {
1913        return preg_match('/'.self::PREG_PACKAGE.'/si', $text, $set);
1914    }
1915
1916    /**
1917     * Looks into file docblock, till it finds the 1st @package
1918     *
1919     * @param string &$text Content of file
1920     * @param array  &$set  Preg result
1921     *
1922     * @return int
1923     */
1924    private function _findFileDocblock(&$text, &$set)
1925    {
1926        return preg_match('/^\s*<\?(?:php)?\s+'.
1927            '(\/\/[^\n\r]*\s+|'.
1928            '\/\*[^\n\r]*?\*\/\s+)?'.
1929            '\/\*(.*?)\*\//si', $text, $set);
1930    }
1931
1932    private function _findProperty(&$text, &$set)
1933    {
1934        return preg_match('/^(\$?'.self::PREG_VARIABLE.')\s*(=)?/si', $text, $set);
1935    }
1936
1937    private function _findAttr(&$text, &$set)
1938    {
1939        return preg_match(
1940            '/^[\s](var|public|protected|private|const|static|abstract|final)/si',
1941            $text, $set
1942        );
1943    }
1944
1945    private function _findFunction(&$text, &$set)
1946    {
1947        return preg_match(
1948            '/^function\s([&\s]*'.self::PREG_VARIABLE.')\s*\(/si', $text, $set
1949        );
1950    }
1951
1952    /**
1953     * Looks for a docblock backwards
1954     *
1955     * @param string $text The text to search in (from the end)
1956     *
1957     * @return string
1958     */
1959    private function _revDocblock($text)
1960    {
1961        $r = preg_match('/^\s*\/\*(.*?)\*\//s', strrev($text), $set);
1962        if ($r>0)
1963            return strrev($set[1]);
1964        else
1965            return '';
1966    }
1967
1968    /**
1969     * Search for docblock tags
1970     *
1971     * @param string $text Text
1972     *
1973     * @return array
1974     */
1975    private function _findParamInDocblock($text)
1976    {
1977        $r = preg_match_all(
1978            '/\*[ \t]*@([\w]+)[ \t]+([\w]+)[ \t]*([\w\$]*)\s/', $text, $set,
1979            PREG_SET_ORDER
1980        );
1981        return $set;
1982    }
1983
1984    private function _getFilePackage()
1985    {
1986        $r = $this->_findFileDocblock($this->_text, $set);
1987        if ($r>0) {
1988            $r = $this->_findPackageInDocblock($set[1], $doc);
1989            if ($r>0)
1990                return $doc[1];
1991            else {
1992                $r = $this->_findPackageInDocblock($set[2], $doc);
1993                if ($r>0)
1994                    return $doc[1];
1995            }
1996        }
1997        return '';
1998    }
1999
2000    private function _cleanParameter($str)
2001    {
2002        if (!$this->_dollar)
2003            $str = str_replace('$', '', $str);
2004        return htmlspecialchars(preg_replace('/\s\s+/', ' ', trim($str)));
2005    }
2006
2007
2008    /**
2009     * Splits a parameter into its name and type
2010     *
2011     * @param string $parameter The parameter to analyse
2012     * @param string $default   Default value
2013     * @param string $param     Value of docblock "param"
2014     *
2015     * @return array
2016     */
2017    private static function _splitNameType($parameter, $default = '', $param = '')
2018    {
2019        $exp_param_name = explode(' ', trim($parameter));
2020        $nat            = array();
2021        if (count($exp_param_name)>1) {
2022            // Parameter like "MyType $myVariable"
2023            $nat[0] = trim($exp_param_name[1]);
2024            $nat[1] = trim($exp_param_name[0]);
2025        }
2026        else {
2027            // Parameter like "$myVariable"
2028            $nat[0] = $exp_param_name[0];
2029            if ($param!='') {
2030                // if a @param was provided, let's use it :
2031                $nat[1] = $param;
2032            }
2033            else
2034                $nat[1] = self::_guessType($default);
2035        }
2036        return $nat;
2037    }
2038
2039    static private function _cleanVariable($str)
2040    {
2041        return str_replace('$', '', str_replace('&amp;', '', $str));
2042    }
2043
2044    static private function _stripComments($str)
2045    {
2046        $patt = array('/'.self::PREG_COMMENT.'/s');
2047        $repl = array('');
2048        while ($str!=preg_replace($patt, $repl, $str))
2049            $str = preg_replace($patt, $repl, $str);
2050        return $str;
2051    }
2052
2053    static private function _getVisibility($arr)
2054    {
2055        if (in_array('private', $arr))
2056            return 'private';
2057        elseif (in_array('protected', $arr))
2058            return 'protected';
2059        else
2060            return 'public';
2061    }
2062
2063    /**
2064     * Tries to guess the type of a given value
2065     *
2066     * @param string $value The type to check
2067     *
2068     * @return string The corresponding XMI DataType.
2069     */
2070    static private function _guessType($value)
2071    {
2072        $value = trim(strtolower($value));
2073        if (substr($value, 0, 6) == 'array(')
2074            $type = 'array';
2075        elseif (!(strpos($value, "'")===false && strpos($value, '"')===false))
2076            $type = 'string';
2077        elseif ($value=='true' || $value=='false')
2078            $type = 'bool';
2079        elseif ($value=='void')
2080            $type = 'void';
2081        elseif (is_numeric($value)) {
2082            if (strpos($value, '.')===false)
2083                $type = 'int';
2084            else
2085                $type = 'float';
2086        }
2087        else
2088            $type = 'mixed';
2089        return $type;
2090    }
2091
2092    /**
2093     * Recursively replaces the temporary string values by references in a package
2094     *
2095     * @param PHP_UML_Metamodel_Package &$ns    Package to look into
2096     * @param PHP_UML_Metamodel_Package &$_oDef Root package
2097     */
2098    private function _resolveReferences(PHP_UML_Metamodel_Package &$ns, PHP_UML_Metamodel_Package &$_oDef)
2099    {
2100        if (!is_null($ns->nestedPackage)) {
2101            foreach ($ns->nestedPackage as &$nsIdx) {
2102                $this->_resolveReferences($this->model->packages->get($nsIdx), $_oDef);
2103            }
2104        }
2105        if (!is_null($ns->ownedType))
2106        foreach ($ns->ownedType as &$elt) {
2107            if (isset($elt->superClass) && !is_null($elt->superClass)) {
2108                foreach ($elt->superClass as &$className) {
2109                    $this->_resolveType($ns, $className, $_oDef, $elt);
2110                }
2111            }
2112            if (isset($elt->implements) && !is_null($elt->implements)) {
2113                foreach ($elt->implements as &$className) {
2114                    $this->_resolveType($ns, $className, $_oDef, $elt);
2115                }
2116            }
2117            foreach ($elt->ownedOperation as &$function) {
2118                foreach ($function->ownedParameter as &$parameter) {
2119                    $this->_resolveType($ns, $parameter->type, $_oDef, $elt);
2120                }
2121            }
2122            if (isset($elt->ownedAttribute)) {
2123                foreach ($elt->ownedAttribute as &$attribute) {
2124                     $this->_resolveType($ns, $attribute->type, $_oDef, $elt);
2125                }
2126            }
2127        }
2128    }
2129
2130    private function _searchIntoPackage(PHP_UML_Metamodel_Package &$ns, $value)
2131    {
2132        foreach ($ns->ownedType as $key => &$o) {
2133            if (strtolower($o->name)==strtolower($value)) {
2134                return $o;
2135            }
2136        }
2137        return false;
2138    }
2139
2140    private function _searchIntoDatatype($value)
2141    {
2142        foreach ($this->model->datatypes->getIterator() as $dt) {
2143            if (strtolower($dt->name)==strtolower($value)) {
2144                return $dt;
2145            }
2146        }
2147        return false;
2148    }
2149
2150    /**
2151     * Makes the type resolution for a given element in a given package
2152     *
2153     * @param PHP_UML_Metamodel_Package &$pkg     The nesting package
2154     * @param string                    &$element The element to resolve, provided as a name
2155     * @param PHP_UML_Metamodel_Package &$_pkgDef The root package
2156     * @param PHP_UML_Metamodel_Type    &$context The context (the nesting class/interface, which
2157     *                                            is the only element to know the nesting file)
2158     */
2159    private function _resolveType(PHP_UML_Metamodel_Package &$pkg, &$element,
2160        PHP_UML_Metamodel_Package &$_pkgDef, PHP_UML_Metamodel_Type &$context)
2161    {
2162        $_o = $this->_searchIntoPackage($pkg, $element);
2163        if (!($_o===false)) {
2164            $element = $_o;
2165        }
2166        else {
2167            $_o = $this->_searchIntoPackage($_pkgDef, $element);
2168            if (!($_o===false)) {
2169                $element = $_o;
2170            }
2171            else {
2172                $_o = $this->_searchIntoDatatype($element);
2173                if (!($_o===false)) {
2174                    $element = $_o;
2175                }
2176                else {
2177                    PHP_UML_Warning::add('Could not resolve '.$element.
2178                        ' in '.$context->file->name);
2179                    $element = null;
2180                }
2181            }
2182        }
2183    }
2184}
2185
2186?>
2187