Project

General

Profile

1
<?php
2
require_once('util/utilityFunctions.php');
3
require_once('objects/types.php');
4
require_once('objects/lookups.php');
5

    
6
class Entity {
7
  private $references = array();
8
  private $members = array();
9
  private $referrers = array();
10
  private $tableName;
11
  private $extensionType;
12
  private $inited;
13

    
14
  public function __construct($tableName,$node = null) {
15
    global $eList;
16
    // Entity name = table name
17
    $this->tableName = $tableName;
18
    $this->inited = False;
19
    $this->extensionType = '';
20

    
21
    if(!is_null($node)) {
22
      $this->init($tableName,$node);
23
      $this->inited = True;
24
    }
25
  }
26

    
27
  public function init($tableName,$node) {
28
    global $primitiveTypes;
29
    global $eList;
30
    $xpath = new DOMXPath($node->ownerDocument);
31
    $childNodesXPath = $xpath->query("child::node()",$node);
32
    $childNodes = array();
33
    foreach($childNodesXPath as $childNodeXPath) {
34
      $cNodeName = preg_replace("/.*:/","",$childNode->nodeName);
35
      if(!isIgnoreType($cNodeName)) {
36
        $childNodes[] = $childNodeXPath;
37
      }
38
    }
39

    
40
    for($i = 0; $i < count($childNodes); $i++) {
41
      $childNode = $childNodes[$i];
42

    
43
      $cNodeName = preg_replace("/.*:/","",$childNode->nodeName);
44

    
45
      if(isIgnoreType($cNodeName)) {
46
        continue;
47
      }
48

    
49
      #Very inefficient, but I want to be sure that I've handled all possible
50
      #attribute types.
51
      $attrs = $xpath->query("attribute::*",$childNode);
52
      foreach($attrs as $attr) {
53
        $attrName = $attr->name;
54
        if(!isKnownAttribute($attrName)) {
55
          throw new Exception("Encountered unkown attribute: $attrName in element $this->tableName.");
56
        }
57
      }
58

    
59
      switch($cNodeName) {
60
        case 'any':
61
          $any_e = $eList->newEntity($tableName . "_any_nodes");
62
          $any_e->addMember("node_name","models.CharField(max_length=255)");
63
          $any_e->addMember("node_value","models.TextField()");
64
          $any_e->addOneToManyReference($tableName . "_id",$this);
65
          $this->referrers['anyNode'] = $tableName . '_any_nodes';
66
          break;
67
        case 'anyAttribute':
68
          $any_a = $eList->newEntity($tableName . "_any_attr");
69
          $any_a->addMember("attr_name","models.CharField(max_length=255)");
70
          $any_a->addMember("attr_value","models.CharField(max_length=255)");
71
          $any_a->addOneToManyReference($tableName . "_id",$this);
72
          $this->referrers['anyAttribute'] = $tableName . '_any_attr';
73
          break;
74
        case 'attribute':
75
          $a_name = $childNode->getAttribute('name');
76
          if($a_name == '') {
77
            throw new Exception("Nodes defining attributes must have a name.  " .
78
                                "Unnamed attribute node found in $this->tableName.");
79
          }
80
          $default = $childNode->getAttribute('default');
81
          $use = $childNode->getAttribute('use');
82
          $type = preg_replace("/.*:/","",$childNode->getAttribute('type'));
83
          $guess = False;
84
          if($primitiveTypes[$type] != '') {
85
            $def = $primitiveTypes[$type];
86
          } else {
87
            $def = "models.CharField(max_length=255";
88
          }
89
          if($use == 'optional') {
90
            if($def == 'models.CharField(max_length=255') { $def .= ", "; }
91
            $def .= "null=True, blank=True";
92
          }
93
          if($default != '') {
94
            if($use == 'optional' ||
95
               $def == 'models.CharField(max_length=255') {
96
              $def .= ", "; 
97
            }
98
            $def .= "default = '$default'";
99
          }
100
          if(needsIndex("attr_$a_name")) {
101
            if($use == 'optional' ||
102
               $def == 'models.CharField(max_length=255' ||
103
               $default != '') {
104
              $def .= ", "; 
105
            }
106
            $def .= "db_index=True";
107
          }
108
          $def .= ")";
109
          if($guess) { $def .= " # This is a guess"; }
110
          $this->addMember("attr_$a_name",$def);
111
          break;
112
        #Untested
113
        case 'group':
114
          if(hasImportantNodes($childNode)) {
115
            throw new Exception("No defined functionality for group nodes defining complex entities " .
116
                                "within other complex entities.");
117
          }
118
          $ref = preg_replace("/.*:/","",$childNode->getAttribute('ref'));
119
          if($ref == '') {
120
            throw new Exception("No defined functionality for group nodes " .
121
                                "that don't have a 'ref' attribute.");
122
          }
123
          $this->determineRelationship($ref,$ref,$childNode);
124
          break;
125
        case 'complexType':
126
          $name = $childNode->getAttribute('name');
127
          if($name != '') {
128
            throw new Exception("Named complexType definition within entity definition. " .
129
                                "This functionality is not yet implemented.");
130
          }
131
        case 'simpleType':
132
          $name = $childNode->getAttribute('name');
133
          if(isPrimitiveType($name)) {
134
            break;
135
          }
136
          if($name != '') {
137
            throw new Exception("Named simpleType definition within entity definition. " .
138
                                "This functionality is not yet implemented.");
139
          }
140
        case 'choice':
141
          # Fracking hack
142
          $isChoice = True;
143
        case 'complexContent':
144
        case 'simpleContent':
145
        case 'sequence':
146
          $minOccurs = $childNode->getAttribute('minOccurs');
147
          if($isChoice) {
148
            $minOccurs = 0;
149
          }
150
          $maxOccurs = $childNode->getAttribute('maxOccurs');
151
         
152
          #If defined, need to use pass these attribues through to child nodes.
153
          #Then, we add the grandchid nodes to the node list since we need the
154
          #grandchild nodes to complete the entity definition.
155
          $grandchildNodesXPath = $xpath->query("child::node()",$childNode);
156
          foreach($grandchildNodesXPath as $grandchildNodeXPath) {
157
            $gcNodeName = preg_replace("/.*:/","",$grandchildNodeXPath->nodeName);
158
            if(isIgnoreType($gcNodeName)) {
159
              continue;
160
            }
161

    
162
            if(((string)$minOccurs) != '') {
163
              $grandchildNodeXPath->setAttribute("minOccurs",$minOccurs);
164
            }
165
            if(((string)$maxOccurs) != '') {
166
              $grandchildNodeXPath->setAttribute("maxOccurs",$maxOccurs);
167
            }
168
            $childNodes[] = $grandchildNodeXPath;
169
          }
170

    
171
          #None of these have implemented functionality, need to fail if we reach
172
          #them during exection.
173
          $err_type = '';
174
          $default = $childNode->getAttribute('default');
175
          if($default != '' ) {
176
            $err_type = "default";
177
          }
178
          $use = $childNode->getAttribute('use');
179
          if($use != '' ) {
180
            $err_type = "use";
181
          }
182
          $mixed = $childNode->getAttribute('mixed');
183
          if($mixed != '' ) {
184
            $err_type = "mixed";
185
          }
186
          $type = $childNode->getAttribute('type');
187
          if($type != '' ) {
188
            $err_type = "type";
189
          }
190
          $namespace = $childNode->getAttribute('namespace');
191
          if($namespace != '' ) {
192
            $err_type = "namespace";
193
          }
194
          $nillable = $childNode->getAttribute('nillable');
195
          if($nillable != '' ) {
196
            $err_type = "nillable";
197
          }
198
          $ref = $childNode->getAttribute('ref');
199
          if($ref != '' ) {
200
            $err_type = "ref";
201
          }
202
          $base = $childNode->getAttribute('base');
203
          if($base != '' ) {
204
            $err_type = "base";
205
          }
206
          if($err_type != '') {
207
            throw new Exception("$err_type not defined for pass-through.");
208
          }
209
          break;
210
        case 'element':
211
          $this->handleElementCase($childNode);
212
          break;
213
        case 'extension':
214
          $name = findFirstAncestorName($childNode);
215
          $base = preg_replace("/.*:/","",$childNode->getAttribute('base'));
216
          if(isPrimitiveType($base)) {
217
            $grandchildNodesXPath = $xpath->query("child::node()",$childNode);
218
            foreach($grandchildNodesXPath as $grandchildNodeXPath) {
219
              $gcNodeName = preg_replace("/.*:/","",$grandchildNodeXPath->nodeName);
220
              if(isIgnoreType($gcNodeName)) {
221
                continue;
222
              }
223
              if($gcNodeName != 'attribute') {
224
                throw new Exception("Extension of primitive types can only add attributes, ".
225
                                    "found $gcNodeName.");
226
              }
227
              $a_name = $grandchildNodeXPath->getAttribute('name');
228
              if($a_name == '') {
229
                throw new Exception("Nodes defining attributes must have a name.  " .
230
                                    "Unnamed attribute node found in $this->tableName.");
231
              }
232
              $default = $grandchildNodeXPath->getAttribute('default');
233
              $use = $grandchildNodeXPath->getAttribute('use');
234
              $type = preg_replace("/.*:/","",$grandchildNodeXPath->getAttribute('type'));
235
              $guess = False;
236
              if($primitiveTypes[$type] != '') {
237
                $def = $primitiveTypes[$type];
238
              } else {
239
                $def = "models.CharField(max_length=255";
240
                $guess = True;
241
              }
242
              if($use == 'optional') {     
243
                if($def == 'models.CharField(max_length=255') { $def .= ", "; }
244
                $def .= "null=True, blank=True";
245
              }
246
              if($default != '') {
247
                if($use == 'optional' ||
248
                   $def == 'models.CharField(max_length=255') { $def .= ", "; }
249
                $def .= "default = '$default'";
250
              }
251
              if(needsIndex("attr_$a_name")) {
252
                if($use == 'optional' ||
253
                   $def == 'models.CharField(max_length=255' ||
254
                   $default != '') {
255
                  $def .= ", "; 
256
                }
257
                $def .= "db_index=True";
258
              }
259
              $def .= ")";
260
              if($guess) { $def .= " # This is a guess"; }
261
              $this->addMember("attr_$a_name",$def);
262
            }
263
            $def = $primitiveTypes[$base] . ")";
264
            $this->addMember("primitive_type_value",$def);
265
          } else {
266
            $ref_e = $eList->getEntityForReference($base);
267
//            $this->addOneToOneReference($base . "_extend",$ref_e);
268
             $this->addOneToManyReference($base . "_extend",$ref_e,False);
269
            $this->extensionType = $base;
270

    
271
            $grandchildNodesXPath = $xpath->query("child::node()",$childNode);
272
            foreach($grandchildNodesXPath as $grandchildNodeXPath) {
273
              $gcNodeName = preg_replace("/.*:/","",$grandchildNodeXPath->nodeName);
274
              if(isIgnoreType($gcNodeName)) {
275
                continue;
276
              }
277
              $childNodes[] = $grandchildNodeXPath;
278
            }
279
          }
280
          break;
281
        case 'restriction':
282
        case 'union':
283
        case 'maxInclusive':
284
        case 'minInclusive':
285
        case 'minExclusive':
286
        case 'list':
287
        case 'pattern':
288
        case 'fractionDigits':
289
          throw new Exception("No functionality defined for nodes of type: $cNodeName");
290
          break;
291
        default:
292
          throw new Exception("Encountered uknown nodes type: $cNodeName");
293
          break;
294
      }
295
    }
296
  }
297

    
298
  public function handleElementCase($childNode) {
299
    global $eList;
300
    global $primitiveTypes;
301
    $ref = preg_replace("/.*:/","",$childNode->getAttribute('ref'));
302
    $type = preg_replace("/.*:/","",$childNode->getAttribute('type'));
303
    if($ref != '') { 
304
      $type = $ref; 
305
    }
306
    $name = $childNode->getAttribute('name');
307
    if($ref != '' && $name == '') {
308
      $name = $this->tableName . "_" . $ref;
309
    }
310

    
311
    if($name == '') {
312
      throw new Exception("All element nodes must have a name attribute, " .
313
                          "or a ref attribute to serve as the name.");
314
    }
315

    
316
    if($type != '') {
317
      $this->determineRelationship($name,$type,$childNode);
318
    } else {
319
      #Very hacky, but there a some cases where the element
320
      #has no type, and either 1) has some children nodes, but none of the
321
      #children nodes define a type, or 2) Has no useful nodes at all.
322
      # In this case the node is considered to be an anyType.
323
      $isDefined = False;
324
      $xpath = new DOMXPath($childNode->ownerDocument);
325
      $grandchildNodesXPath = $xpath->query("child::node()",$childNode);
326
      foreach($grandchildNodesXPath as $grandchildNodeXPath) {
327
        $gcNodeName = preg_replace("/.*:/","",$grandchildNodeXPath->nodeName);
328
        if(isIgnoreType($gcNodeName)) {
329
          continue;
330
        }
331
        if(hasImportantNodes($grandchildNodeXPath) || $grandchildNodeXPath->hasAttributes()) {
332
          $isDefined = True;
333
        }
334
      }
335
      if(!$isDefined) {
336
        $primitiveTypes[$name] = 'models.TextField(';
337
      }
338
      
339
      if(!isPrimitiveType($name)) {
340
        $eList->newEntity($name,$childNode);
341
      }
342
      $this->determineRelationship($name,$name,$childNode);
343
    }
344
  }
345

    
346
  public function determineRelationship($colName,$typeName,$node) {
347
    global $eList;
348
    global $primitiveTypes;
349
    global $nativeVegXPointers;
350
    global $manyToManyRelationships;
351

    
352
    $tableName = $this->tableName;
353

    
354
    $minOccurs = $node->getAttribute('minOccurs');
355
    $optional = $minOccurs == 0 ? True : False;
356
    $maxOccurs = $node->getAttribute('maxOccurs');
357
    $default = $node->getAttribute('default');
358
    $default = $node->getAttribute('use');
359

    
360
    if(isNativePointer($colName)) {
361
      $def = $nativeVegXPointers[$colName]['def'];
362
      $this->addMember($colName,$def);
363
    } else if(isPrimitiveType($typeName)) {
364
      $def = $primitiveTypes[$typeName];
365
      if($use == 'optional' || $optional) {
366
        if($def == 'models.CharField(max_length=255') { $def .= ", "; }
367
        $def .= "null=True, blank=True";
368
      }
369
      if($default != '') {
370
        if($use == 'optional' || $optional || 
371
           $def == 'models.CharField(max_length=255') { 
372
          $def .= ", "; 
373
        }
374
        $def .= "default = '$default'";
375
      }
376
      if(needsIndex("$colName")) {
377
        if($use == 'optional' || $optional ||
378
           $def == 'models.CharField(max_length=255' ||
379
           $default != '') {
380
          $def .= ", "; 
381
        }
382
        $def .= "db_index=True";
383
      }
384
      $def .= ")";
385

    
386
      if(((string)$maxOccurs) == '' || $maxOccurs == 1) {
387
        $this->addMember($colName,$def);
388
      } else {
389
        $refName = $this->tableName . "_" . $colName;
390
        $new_e = $eList->getEntityForReference($refName);
391
        $new_e->addMember("primitive_type_value",$def);
392
        $new_e->addOneToManyReference($tableName . "_id",$this);
393
        $this->referrers[$colName] = $refName;
394
      }
395
    } else {
396
      $ref_e = $eList->getEntityForReference($typeName);
397
      if(((string)$maxOccurs) == '' || $maxOccurs == 1) {
398
        //$this->addOneToOneReference($colName,$ref_e,$optional);
399
        $this->addOneToManyReference($colName,$ref_e,False,$optional);
400
      } else {
401
#        echo "Need to define relationship between:\n  $tableName <--> $typeName\n";
402
#        echo "    (O)ne to Many\n    (M)any to Many\n";
403
#        echo "    Your choice:  ";
404
#        $handle = fopen ("php://stdin","r");
405
#        $line = fgets($handle);
406
        $line = 'O';
407
        if($manyToManyRelationships[$tableName][$typeName]) {
408
          $line = 'M';
409
        }
410
        if(trim($line) == 'O' || trim($line) == 'o'){
411
          $ref_e->addOneToManyReference($tableName . "_id",$this,False,$optional);
412
          $this->referrers[$colName] = $ref_e->getName();
413
        } else {
414
          $this->addManyToManyReference($colName,$ref_e,$tableName . "_" . $typeName . "_" . $colName);
415
        }
416
      }
417
    }
418
  }
419

    
420
  private function addReference($colName,$definition) {
421
    # Probably shouldn't allow duplicate entries, most likely points to a problem.
422
    if(array_key_exists($colName,$this->references)) {
423
     $curDef = $this->references[$colName];
424
     throw new Exception("Attempting to overwrite/reinsert member $colName:$curDef with $colName:$definition in entity " . $this->tableName . ".");
425
    }
426
    $this->references[$colName] = $definition;
427
  }
428

    
429
  public function isInited() {
430
    return $this->inited;
431
  }
432

    
433
  public function addOneToOneReference($colName,$e,$optional = False) {
434
    $eName = $e->getName();
435
    $relatedName = $this->tableName . "_" . $colName;
436
    $def = "models.ForeignKey('$eName', unique=True,related_name='$relatedName'";
437
    if($optional) {
438
      $def .= ", null=True, blank=True";
439
    }
440
    $def .= ")";
441
    $this->addReference($colName,$def); 
442
  }
443

    
444
  public function addOneToManyReference($colName,$e,$primitiveRef = True,$optional = False) {
445
    $eName = $e->getName();
446
    if($eName == $this->tableName) { 
447
      $eName = 'self'; 
448
    }
449
    $relatedName = $this->tableName . "_" . $colName;
450
    #$def = "models.OneToOneField('$eName', primary_key=True,related_name='$relatedName'";
451
    $def = "models.ForeignKey('$eName', related_name='$relatedName'";
452
    if($optional) {
453
      $def .= ", null=True, blank=True";
454
    }
455
    $def .= ")";
456
    if(!$primitiveRef) {
457
      $def .= " #Complex one2Many";
458
    }
459
    $this->addReference($colName,$def); 
460
  }
461

    
462
  public function addManyToManyReference($colName,$e,$db_table = '') {
463
    $eName = $e->getName();
464
    $relatedName = $this->tableName . "_" . $colName;
465

    
466
    $def = "models.ManyToManyField('$eName',related_name='$relatedName'";
467

    
468
    #Django chokes if the table/relation names are too long.  54 characters seems to
469
    #be a decent magic number.
470
    if($db_table != '' && strlen($db_table) > 54) {
471
      $db_table = 'v_' . substr($db_table,0,54);
472
      $def .= ",db_table = '$db_table'";
473
    }
474
    $def .= ")";
475
    $this->addReference($colName,$def); 
476
  }
477

    
478
  public function addMember($colName, $definition) {
479
    # Probably shouldn't allow duplicate entries, most likely points to a problem.
480
    if(array_key_exists($colName,$this->members)) {
481
      $curDef = $this->members[$colName];
482
      if($curDef != $definition) {
483
        throw new Exception("Attempting to overwrite/reinsert member $colName:$curDef with $colName:$definition.");
484
      }
485
    }
486
    $this->members[$colName] = $definition;
487
  }
488

    
489
  public function getName() {
490
    return $this->tableName;
491
  }
492

    
493
  public function getReferences() {
494
    return $this->references;
495
  }
496

    
497
  public function getMembers() {
498
    return $this->members;
499
  }
500

    
501
  public function getReferrers() {
502
    return $this->referrers;
503
  }
504

    
505
  public function postProcess() {
506
    global $eList;
507

    
508
    #If we're dealing with extensions we need to pull the members, references, &
509
    #referrers of the base class.
510
    if($this->extensionType != '') {
511
      $base = $eList->getEntityForReference($this->extensionType);
512
      $baseReferences = $base->getReferences();
513
      $baseMembers = $base->getMembers();
514
      $baseReferrers = $base->getReferrers();
515

    
516
      $this->references = array_merge($this->references,$baseReferences);
517
      foreach(array_keys($this->references) as $k) {
518
        $def = $this->references[$k];
519
        $baseToken = $base->getName() . "_";
520
        $tableToken = $this->tableName . "_";
521
        $def = preg_replace("/$baseToken/","$tableToken",$def);
522
        $this->references[$k] = $def;
523
      }
524
        
525
      $this->members = array_merge($this->members,$baseMembers);
526
      $this->referrers = array_merge($this->referrers,$baseReferrers);
527

    
528
    }
529

    
530
  }
531

    
532
  public function toString() {
533
    $str = "class " . $this->tableName . "(models.Model):\n";
534

    
535
    #All entities need reference to their creation info since
536
    #VegX does not support unique identification for all types of information,
537
    #at least not as defined in BIEN3.
538
    $str .= "  entryInfo = models.ForeignKey('EntryInfo')\n";
539

    
540
    foreach(array_keys($this->references) as $colName) {
541
      $def = $this->references[$colName];
542
      $str .= "  $colName = $def\n";
543
    }
544
    foreach(array_keys($this->members) as $colName) {
545
      $def = $this->members[$colName];
546
      $str .= "  $colName = $def\n";
547
    }
548

    
549
    global $geoSpatialTypes;
550
    if($geoSpatialTypes[$this->tableName]) {
551
      //Only one geometry type per table currently so this works.
552
      $geoColDef = $geoSpatialTypes[$this->tableName]['colDef']; 
553
      $str .= "  \n  # GeoDjango-specific: a geometry field, and\n".
554
              "  # overriding the default manager with a GeoManager instance.\n".
555
              "  $geoColDef\n".
556
              "  objects = models.GeoManager()\n";
557

    
558
    }
559

    
560
    $str .= "\n" . $this->makeExportFunction();
561
    $str .= "\n" . $this->makeImportFunction();
562
    $str .= "\n" . $this->makeExportDummyDataFunction();
563
    $str .= "\n" . $this->makeRetrieveOrNewFunction();
564

    
565
    #truncate table names that are too long for Django/Postgre to support
566
    if(strlen($this->tableName) > 54) {
567
      $tabName = 'v_' . substr($this->tableName,0,54);
568
      $str .= "\n\n  class Meta:\n".
569
              "    db_table = u'$tabName'\n\n";
570
    }
571

    
572
    #there is a tablename resolution conflict between taxonNames/TaxonNames, taxonConcepts/TaxonConcepts,
573
    #temporalCoverage/TemporalCoverage in
574
    #that Django & Postgres store the table names in a case-insensitive way.  Need to force the table name
575
    #to be something different here.
576
    if($this->tableName == 'taxonNames') {
577
      $str .= "\n\n  class Meta:\n".
578
              "    db_table = u'v_taxonnames_lc'\n\n";
579
    } else if($this->tableName == 'TaxonNames') {
580
      $str .= "\n\n  class Meta:\n".
581
              "    db_table = u'v_taxonnames_uc'\n\n";
582
    } else if($this->tableName == 'taxonConcepts') {
583
      $str .= "\n\n  class Meta:\n".
584
              "    db_table = u'v_taxonconcepts_lc'\n\n";
585
    } else if($this->tableName == 'TaxonConcepts') {
586
      $str .= "\n\n  class Meta:\n".
587
              "    db_table = u'v_taxonconcepts_uc'\n\n";
588
    } else if($this->tableName == 'temporalCoverage') {
589
      $str .= "\n\n  class Meta:\n".
590
              "    db_table = u'v_temporalcoverage_lc'\n\n";
591
    } else if($this->tableName == 'TemporalCoverage') {
592
      $str .= "\n\n  class Meta:\n".
593
              "    db_table = u'v_temporalcoverage_uc'\n\n";
594
    }
595

    
596
    return $str;
597
  }
598

    
599
  public function makeImportFunction() {
600
    #Slightly different if there are no members
601
    if(count($this->references) == 0 &&
602
       count($this->members) == 0 &&
603
       count($this->referrers) == 0) {
604
      return "  def importVegX(self,node,info):\n".
605
             "    self.entryInfo = info\n".
606
             "    self.save()\n";
607
    }
608

    
609
    $def =  "  def importVegX(self,node,info):\n".
610
            "    self.entryInfo = info\n";
611

    
612
    if(preg_match_all("/([a-zA-Z]*)_extend/",implode(",",array_keys($this->references)),$matches) > 0) {
613
      $base = $matches[1][0];
614
      $def .= "    extendedEl = $base().retrieveOrNew(node)\n".
615
              "    extendedEl.importVegX(node,info)\n".
616
              "    self.$base"."_extend = extendedEl\n\n";
617
    }
618

    
619
    $def .= "    self.save()\n";
620

    
621
    #Fun hack for anyAttribute & anyNode nodes
622
    if(preg_match("/_any_attr/",$this->tableName,$matches) > 0 ||
623
       preg_match("/_any_nodes/",$this->tableName,$matches) > 0) {
624
      return $def;
625
    }
626

    
627
    if(preg_match("/attr_/",implode(",",array_keys($this->members))) > 0 ||
628
       preg_match("/anyAttribute/",implode(",",array_keys($this->referrers))) > 0) {
629
      $def .= "    #attributes\n".
630
              "    for attr in node.attributes.keys():\n".
631
              "      aName = 'attr_' + attr\n".
632
              "      aVal = node.attributes[attr].nodeValue\n";
633
      $i = 0;
634
      foreach(array_keys($this->members) as $colName) {
635
        $ifStmt = $i == 0 ? 'if' : 'elif';
636
        if(preg_match("/^attr_/",$colName,$matches) > 0) {
637
          $i += 1;
638
          if(preg_match("/BooleanField/",$this->members[$colName],$matches) > 0) {
639
            $def .= "      $ifStmt aName == '$colName' and (aVal == 'true' or\n".
640
                    "      aVal == 'false' or aVal == 0 or aVal == 1):\n".
641
                    "        self.$colName = (aVal == 'true' or aVal == 1)\n";
642
          } else {
643
            $def .= "      $ifStmt aName == '$colName':\n".
644
                    "        self.$colName = aVal\n";
645
          }
646
        }
647
      }
648
    }
649
    if(preg_match("/anyAttribute/",implode(",",array_keys($this->referrers))) > 0) {
650
      $type = $this->referrers['anyAttribute'];
651
      $tIDName = $this->tableName . "_id";
652
      if(preg_match_all("/attr_/",implode(",",array_keys($this->members)),$matches) > 0) {
653
        $def .= "      else:\n".
654
                "        newEl = $type()\n".
655
                "        newEl.$tIDName = self\n".
656
                "        newEl.attr_name = attr\n".
657
                "        newEl.attr_value = aVal\n".
658
                "        newEl.entryInfo = info\n".
659
                "        newEl.save()\n";
660
      } else {
661
        $def .= "      newEl = $type()\n".
662
                "      newEl.$tIDName = self\n".
663
                "      newEl.attr_name = attr\n".
664
                "      newEl.attr_value = aVal\n".
665
                "      newEl.entryInfo = info\n".
666
                "      newEl.save()\n";
667
      }
668
    }
669
  
670
    if(count($this->members) - 
671
       preg_match_all("/attr_/",implode(",",array_keys($this->members)),$matches) -
672
       preg_match_all("/primitive_type_value/",implode(",",array_keys($this->members)),$matches) > 0 ||
673
       count($this->references) > 0 || count($this->referrers) > 0) {
674
      $def .= "    #members & relationships\n".
675
              "    for child in node.childNodes:\n".
676
              "      cName = child.nodeName\n";
677
    }
678

    
679
    $i = 0;
680
    if(count($this->members) - 
681
       preg_match_all("/attr_/",implode(",",array_keys($this->members)),$matches) -
682
       preg_match_all("/primitive_type_value/",implode(",",array_keys($this->members)),$matches) > 0) {
683
      foreach(array_keys($this->members) as $colName) {
684
        if(preg_match("/^attr_/",$colName) > 0) { continue; }
685
        else if(isNativePointer($colName)) {
686
          global $nativeVegXPointers;
687
          $ifStmt = $i == 0 ? 'if' : 'elif';
688
          $i += 1;
689
          $tabType = $nativeVegXPointers[$colName]['fk_type'];
690
          $toField = $nativeVegXPointers[$colName]['to_field'];
691
          $def .= "      $ifStmt cName == '$colName':\n".
692
                  "        if(len(child.childNodes) > 0):\n".
693
                  "          cVal = child.childNodes[0].nodeValue\n".
694
                  "          q = $tabType.objects.filter($toField" . "__exact = cVal)\n".
695
                  "          q = $tabType.objects.filter(entryInfo__id__exact = self.entryInfo.id)\n".
696
                  "          if len(q) > 0:\n".
697
                  "            self.$colName = q[0]\n";
698
        } else {
699
          $ifStmt = $i == 0 ? 'if' : 'elif';
700
          $i += 1;
701
          $def .= "      $ifStmt cName == '$colName':\n".
702
                  "        if(len(child.childNodes) > 0):\n".
703
                  "          cVal = child.childNodes[0].nodeValue\n".
704
                  "          self.$colName = cVal\n";
705
        }
706
      }
707
    }
708

    
709
    if(preg_match_all("/primitive_type_value/",implode(",",array_keys($this->members)),$matches) > 0) {
710
      $def .= "    if(len(node.childNodes) > 0):\n".
711
              "      cVal = node.childNodes[0].nodeValue\n".
712
              "      self.primitive_type_value = cVal\n";
713
    }
714

    
715
    foreach(array_keys($this->references) as $colName) {
716
      $ifStmt = $i == 0 ? 'if' : 'elif';
717
      $colDef = $this->references[$colName];
718

    
719
      if(preg_match("/(ForeignKey\(')([a-zA-Z]*)/",$colDef,$matches) > 0 &&
720
         preg_match("/Complex one2Many/",$colDef) > 0 && preg_match("/_extend/",$colDef) == 0 &&
721
         preg_match("/_id/",$colName) == 0) {
722
        $i += 1;
723
        $def .= "      $ifStmt cName == '$colName':\n".
724
                "        newEl = $matches[2]().retrieveOrNew(child)\n".
725
                "        newEl.importVegX(child,info)\n".
726
                "        self.$colName = newEl\n";
727
      }
728
      if(preg_match("/(ManyToManyField\(')([a-zA-Z]*)/",$colDef,$matches) > 0) {
729
        $i += 1;
730
        $def .= "      $ifStmt cName == '$colName':\n".
731
                "        newEl = $matches[2]().retrieveOrNew(child)\n".
732
                "        newEl.importVegX(child,info)\n".
733
                "        self.$colName.add(newEl)\n";
734
      }
735
    }
736

    
737
    if(count($this->referrers) > 0) {
738
      $def .= "      #These are the elements that refer to this one\n";
739
    }
740
    $allowAnyNode = False;
741
    foreach(array_keys($this->referrers) as $colName) {
742
      if($colName == 'anyAttribute' || $colName == 'anyNode') { continue; }
743
      $ifStmt = $i == 0 ? 'if' : 'elif';
744
      $i += 1;
745
      $colDef = $this->referrers[$colName];
746
      $elReferenceBack = $this->tableName . '_id';
747
      $def .= "      $ifStmt cName == '$colName':\n".
748
              "        newEl = $colDef().retrieveOrNew(child)\n".
749
              "        newEl.$elReferenceBack = self\n".
750
              "        newEl.importVegX(child,info)\n";
751
    }
752
    if(preg_match("/anyNode/",implode(",",array_keys($this->referrers))) > 0) {
753
      $type = $this->referrers['anyNode'];
754
      $tIDName = $this->tableName . "_id";
755
      if($i > 0) {
756
        $def .= "      else:\n".
757
                "        newEl = $type()\n".
758
                "        newEl.$tIDName = self\n".
759
                "        newEl.node_name = cName\n".
760
                "        for grandchild in child.childNodes:\n".
761
                "          newEl.node_value += grandchild.toxml()\n".
762
                "        newEl.entryInfo = info\n".
763
                "        newEl.save()\n";
764
      } else {
765
        $def .= "      newEl = $type()\n".
766
                "      newEl.$tIDName = self\n".
767
                "      newEl.node_name = cName\n".
768
                "      for grandchild in child.childNodes:\n".
769
                "        newEl.node_value += grandchild.toxml()\n".
770
                "      newEl.entryInfo = info\n".
771
                "      newEl.save()\n";
772
      }
773
    }
774

    
775
    global $geoSpatialTypes;
776
    if($geoSpatialTypes[$this->tableName]) {
777
      //Only one geometry type per table currently so this works.
778
      $geoColImportDef = $geoSpatialTypes[$this->tableName]['importDef']; 
779
      $def .= "  \n    # Convert to geometry types to make use of GeoDjango's\n".
780
              "    # capabilities.\n".
781
              "  $geoColImportDef\n";
782

    
783
    }
784

    
785
    $def .= "\n    self.save()\n"; 
786
    return $def;
787
    
788
  }
789

    
790
  public function makeExportFunction() {
791
    #Slightly different if there are no members
792
    if(count($this->references) == 0 &&
793
       count($this->members) == 0 &&
794
       count($this->referrers) == 0) {
795
      return "  def exportVegX(self,myNode,doc):\n".
796
             "    return myNode\n";
797
    }
798

    
799
    $def =  "  def exportVegX(self,myNode,doc):\n";
800

    
801
    #Fun hack for anyAttribute & anyNode nodes
802
    if(preg_match("/_any_attr/",$this->tableName,$matches) > 0) {
803
      $def .= "    newAttr = doc.createAttribute(self.attr_name)\n".
804
              "    newAttr.value = self.attr_value\n".
805
              "    return newAttr\n";
806
      return $def;
807
    }
808
    if(preg_match("/_any_nodes/",$this->tableName,$matches) > 0) {
809
      $def .= "    newEl = doc.createElement(self.node_name)\n".
810
              "    newElText = doc.createTextNode(self.node_value)\n".
811
              "    newEl.appendChild(newElText)\n".
812
              "    return newEl\n\n";
813
      return $def;
814
    }
815

    
816
    foreach(array_keys($this->members) as $colName) {
817
      $nodeName = $colName;
818
      $nodeText = "str(self.$colName)";
819
      //Python's string representation of booleans is True/False but xsd requires
820
      //true/false (lowercase) so we need this hack.
821
      if(preg_match("/NullBooleanField/",$this->members[$colName],$matches) > 0) {
822
        $nodeText = "string.lower(str(self.$colName))";
823
      }
824
      if(preg_match("/^attr_/",$colName,$matches) > 0) {
825
        $nodeName = preg_replace("/^attr_/","",$colName);
826
        $def .= "    if self.$colName != None:\n".
827
                "      newAttr = doc.createAttribute('$nodeName')\n".
828
                "      newAttr.value = $nodeText\n".
829
                "      myNode.setAttributeNode(newAttr)\n\n";
830
      } else if(preg_match("/primitive_type_value/",$colName,$matches) > 0) {
831
        $def .= "    if self.$colName != None:\n".
832
                "      newElText = doc.createTextNode(self.$colName)\n".
833
                "      myNode.appendChild(newElText)\n\n";
834
      }else if(isNativePointer($colName)) {
835
        global $nativeVegXPointers;
836
        $to_field = $nativeVegXPointers[$colName]['to_field'];
837
        $nodeText = "str(self.$colName.$to_field)";
838
        $def .= "    if self.$colName != None:\n".
839
                "      newEl = doc.createElement('$colName')\n".
840
                "      newElText = doc.createTextNode($nodeText)\n".
841
                "      newEl.appendChild(newElText)\n".
842
                "      myNode.appendChild(newEl)\n\n";
843
      }else {
844
/*
845
        if(preg_match("/DateField/",$this->members[$colName],$matches) > 0 ||
846
           preg_match("/TimeField/",$this->members[$colName],$matches) > 0 ||
847
           preg_match("/PositiveIntegerField/",$this->members[$colName],$matches) > 0 ||
848
           preg_match("/NullBooleanField/",$this->members[$colName],$matches) > 0 ||
849
           preg_match("/FloatField/",$this->members[$colName],$matches) > 0) {
850
          $nodeText = "str(self.$colName)";
851
        }
852
*/
853
        $def .= "    if self.$colName != None:\n".
854
                "      newEl = doc.createElement('$colName')\n".
855
                "      newElText = doc.createTextNode($nodeText)\n".
856
                "      newEl.appendChild(newElText)\n".
857
                "      myNode.appendChild(newEl)\n\n";
858
      }
859
    }
860

    
861
    foreach(array_keys($this->references) as $colName) {
862
      $colDef = $this->references[$colName];
863

    
864
      if(preg_match("/(ForeignKey\(')([a-zA-Z]*)/",$colDef,$matches) > 0 &&
865
         (preg_match("/Complex one2Many/",$colDef) > 0 || preg_match("/_extend/",$colName) > 0) &&
866
         preg_match("/_id/",$colName) == 0) {
867
        if(preg_match("/_extend/",$colName) == 0) {
868
          $def .= "    if self.$colName != None:\n".
869
                  "      newEl = doc.createElement('$colName')\n".
870
                  "      myNode.appendChild(self.$colName.exportVegX(newEl,doc))\n\n";
871
        } else {
872
          $def .= "    if self.$colName != None:\n".
873
                  "      myNode = self.$colName.exportVegX(myNode,doc)\n\n";
874
        }
875
      }
876
      if(preg_match("/(ManyToManyField\(')([a-zA-Z]*)/",$colDef,$matches) > 0) {
877
        $def .= "    for childRef in self.$colName.all():\n".
878
                "      newEl = doc.createElement('$colName')\n".
879
                "      myNode.appendChild(childRef.exportVegX(newEl,doc))\n\n";
880
      }
881
    }
882

    
883
    foreach(array_keys($this->referrers) as $nodeName) {
884
      $nodeNameToUse = $this->referrers[$nodeName];
885
      if($nodeName == 'anyAttribute') {
886
        $nodeNameToUse = $this->tableName . '_any_attr';
887
        $relatedName = $nodeNameToUse . '_' . $this->tableName . '_id';
888
        $def .= "    for childRef in self.$relatedName.all():\n".
889
                "      newEl = doc.createAttribute('$nodeName')\n".
890
                "      myNode.setAttributeNode(childRef.exportVegX(newEl,doc))\n\n";
891
      } else {
892
        if($nodeName == 'anyNode') {
893
          $nodeNameToUse = $this->tableName . '_any_nodes';
894
        }
895
        $relatedName = $nodeNameToUse . '_' . $this->tableName . '_id';
896
        $def .= "    for childRef in self.$relatedName.all():\n".
897
                "      newEl = doc.createElement('$nodeName')\n".
898
                "      myNode.appendChild(childRef.exportVegX(newEl,doc))\n\n";
899
      }
900
    }
901

    
902
    $def .= "    return myNode\n";
903
    return $def;
904
  }
905

    
906
  public function makeExportDummyDataFunction() {
907
    #Slightly different if there are no members
908
    if(count($this->references) == 0 &&
909
       count($this->members) == 0 &&
910
       count($this->referrers) == 0) {
911
      return "  def exportDummyVegX(self,myNode,doc,usedObjectsStack):\n".
912
             "    return myNode\n";
913
    }
914

    
915
    $def =  "  def exportDummyVegX(self,myNode,doc,usedObjectsStack):\n";
916

    
917
    #Fun hack for anyAttribute & anyNode nodes
918
    if(preg_match("/_any_attr/",$this->tableName,$matches) > 0) {
919
      $def .= "    newAttr = doc.createAttribute(getRandomString())\n".
920
              "    newAttr.value = getRandomString()\n".
921
              "    return newAttr\n";
922
      return $def;
923
    }
924
    if(preg_match("/_any_nodes/",$this->tableName,$matches) > 0) {
925
      $def .= "    newEl = doc.createElement(getRandomString())\n".
926
              "    newElText = doc.createTextNode(getRandomText())\n".
927
              "    newEl.appendChild(newElText)\n".
928
              "    return newEl\n\n";
929
      return $def;
930
    }
931

    
932
    foreach(array_keys($this->members) as $colName) {
933
      $nodeName = $colName;
934
      $colDef = $this->members[$colName];
935
      $randomFun = "getRandomString";
936
      if(preg_match("/FloatField/",$colDef,$matches) > 0) {
937
        $randomFun = "getRandomFloat";
938
      } else if(preg_match("/IntegerField/",$colDef,$matches) > 0) {
939
        $randomFun = "getRandomInt";
940
      } else if(preg_match("/TextField/",$colDef,$matches) > 0) {
941
        $randomFun = "getRandomText";
942
      } else if(preg_match("/DateField/",$colDef,$matches) > 0) {
943
        $randomFun = "getRandomDate";
944
      } else if(preg_match("/TimeField/",$colDef,$matches) > 0) {
945
        $randomFun = "getRandomTime";
946
      } else if(preg_match("/NullBooleanField/",$colDef,$matches) > 0) {
947
        $randomFun = "getRandomBool";
948
      }
949

    
950
      #Need to explicitly convert to string so minidom doesn't
951
      #roll over in nasty convulsions
952
      $randomFun = "str($randomFun())";
953

    
954
      if(preg_match("/^attr_/",$colName,$matches) > 0) {
955
        $nodeName = preg_replace("/^attr_/","",$colName);
956
        $def .= "    newAttr = doc.createAttribute('$nodeName')\n".
957
                "    newAttr.value = $randomFun\n".
958
                "    myNode.setAttributeNode(newAttr)\n\n";
959
      } else if(preg_match("/primitive_type_value/",$colName,$matches) > 0) {
960
        $def .= "    newElText = doc.createTextNode($randomFun)\n".
961
                "    myNode.appendChild(newElText)\n\n";
962
      }else {
963
        $def .= "    newEl = doc.createElement('$colName')\n".
964
                "    newElText = doc.createTextNode($randomFun)\n".
965
                "    newEl.appendChild(newElText)\n".
966
                "    myNode.appendChild(newEl)\n\n";
967
      }
968
    }
969

    
970
    foreach(array_keys($this->references) as $colName) {
971
      $colDef = $this->references[$colName];
972

    
973
      if(preg_match("/(ForeignKey\(')([a-zA-Z]*)/",$colDef,$matches) > 0 &&
974
         (preg_match("/Complex one2Many/",$colDef) > 0 || preg_match("/_extend/",$colName) > 0) &&
975
         preg_match("/_id/",$colName) == 0) {
976
        if(preg_match("/_extend/",$colName) == 0) {
977
          $def .= "    if '$matches[2]' not in usedObjectsStack:\n".
978
                  "      usedObjectsStack.append('$matches[2]')\n".
979
                  "      newEl = doc.createElement('$colName')\n".
980
                  "      newElObj = $matches[2]()\n".
981
                  "      myNode.appendChild(newElObj.exportDummyVegX(newEl,doc,usedObjectsStack))\n".
982
                  "      usedObjectsStack.pop()\n\n";
983
        } else {
984
          $def .= "    if '$matches[2]' not in usedObjectsStack:\n".
985
                  "      usedObjectsStack.append('$matches[2]')\n".
986
                  "      newElObj = $matches[2]()\n".
987
                  "      myNode = newElObj.exportDummyVegX(myNode,doc,usedObjectsStack)\n".
988
                  "      usedObjectsStack.pop()\n\n";
989
        }
990
      }
991
      if(preg_match("/(ManyToManyField\(')([a-zA-Z]*)/",$colDef,$matches) > 0) {
992
        $def .= "    if '$matches[2]' not in usedObjectsStack:\n".
993
                "      usedObjectsStack.append('$matches[2]')\n".
994
                "      newEl = doc.createElement('$colName')\n".
995
                "      newElObj = $matches[2]()\n".
996
                "      myNode.appendChild(newElObj.exportDummyVegX(newEl,doc,usedObjectsStack))\n".
997
                "      usedObjectsStack.pop()\n\n";
998
      }
999
    }
1000

    
1001
    foreach(array_keys($this->referrers) as $nodeName) {
1002
      $colDef = $this->referrers[$nodeName];
1003
      if($nodeName == 'anyAttribute') {
1004
        $def .= "    if '$colDef' not in usedObjectsStack:\n".
1005
                "      usedObjectsStack.append('$colDef')\n".
1006
                "      newEl = doc.createAttribute(getRandomText())\n".
1007
                "      childRef = $colDef()\n".
1008
                "      myNode.setAttributeNode(childRef.exportDummyVegX(newEl,doc,usedObjectsStack))\n".
1009
                "      usedObjectsStack.pop()\n\n";
1010
      } else {
1011
        $def .= "    if '$colDef' not in usedObjectsStack:\n".
1012
                "      usedObjectsStack.append('$colDef')\n\n".
1013
                "      newEl = doc.createElement('$nodeName')\n".
1014
                "      childRef = $colDef()\n".
1015
                "      myNode.appendChild(childRef.exportDummyVegX(newEl,doc,usedObjectsStack))\n".
1016
                "      usedObjectsStack.pop()\n\n";
1017
      }
1018
    }
1019

    
1020
    $def .= "    return myNode\n";
1021
    return $def;
1022
  }
1023

    
1024
  public function makeRetrieveOrNewFunction() {
1025
    global $idSysScope;
1026
    global $idExtend;
1027
    global $adHoc;
1028
    global $adHocAttr;
1029

    
1030
    $tName = $this->tableName;
1031
    $def = "  def retrieveOrNew(self,node):\n";
1032

    
1033
    if($idExtend[$tName] != '' || $idSysScope[$tName] != ''){
1034
      $extendedId = '';
1035
      if($idExtend[$tName] != '') {$extendedId = $idExtend[$tName] . "__";}
1036
      $def .= "    a_id = None\n".
1037
              "    a_system = None\n".
1038
              "    a_scope = None\n".
1039
              "    for attr in node.attributes.keys():\n".
1040
              "      aVal = node.attributes[attr].nodeValue\n".
1041
              "      if attr == 'id':\n".
1042
              "        a_id = aVal\n".
1043
              "      elif attr == 'system':\n".
1044
              "        a_system = aVal\n".
1045
              "      elif attr == 'scope':\n".
1046
              "        a_scope = aVal\n".
1047
              "    if a_id != None:\n".
1048
              "      q = $tName.objects.filter($extendedId" . "attr_id__exact = a_id)\n".
1049
              "      if a_system != None:\n".
1050
              "        q = q.filter($extendedId" . "attr_system__exact = a_system)\n".
1051
              "      if a_scope != None:\n".
1052
              "        q = q.filter($extendedId" . "attr_scope__exact = a_scope)\n".
1053
              "      if len(q) > 0:\n".
1054
              "        return q[0]\n".
1055
              "      else:\n".
1056
              "        return $tName()\n".
1057
              "    else:\n".
1058
              "      return $tName()\n";
1059
    } else if($adHoc[$tName] != '') {
1060
      $def .= handleAdHoc($tName);
1061
    } else if($adHocAttr[$tName] != '') {
1062
      $def .= handleAdHocAttr($tName);
1063
    } else {
1064
      $def .= "    return $tName()\n";
1065
    }
1066
    return $def;
1067
  }
1068

    
1069
  public function makeSetupTestFunction() {
1070
    if(isPrimitiveType($this->tableName)) { return ""; }
1071

    
1072
    $def = "  def setUp(self):\n".
1073
           "    self.impl = getDOMImplementation()\n".
1074
           "    self.newdoc = self.impl.createDocument(None, " . $this->tableName . ", None)\n".
1075
           "    top_element = newdoc.documentElement\n";
1076
  }
1077

    
1078
  public function makeImportTestFunction() {
1079
  }
1080

    
1081
  public function makeExportTestFunction() {
1082
  }
1083

    
1084
}
1085
?>
(1-1/4)