AbstractElement   A
last analyzed

Complexity

Total Complexity 36

Size/Duplication

Total Lines 272
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 61.48%

Importance

Changes 0
Metric Value
wmc 36
lcom 1
cbo 7
dl 0
loc 272
ccs 75
cts 122
cp 0.6148
rs 9.52
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A __destruct() 0 16 4
A addChild() 0 15 2
A adjustDepth() 0 9 2
A build() 0 13 1
B buildAttributes() 0 23 6
A getAllowedChildTypes() 0 3 1
A getAttributeByName() 0 5 2
A getChildByName() 0 6 2
A getParent() 0 3 1
A getRoot() 0 8 2
A removeAttribute() 0 9 2
A removeAttributeByName() 0 4 1
A removeChild() 0 10 2
A removeChildByName() 0 13 2
A setAttribute() 0 9 2
A setAttributes() 0 10 3
A setParent() 0 3 1
1
<?php
2
3
/**
4
 * @file
5
 * Grafizzi\Graph\AbstractElement: a component of the Grafizzi library.
6
 *
7
 * (c) 2012 Frédéric G. MARAND <[email protected]>
8
 *
9
 * Grafizzi is free software: you can redistribute it and/or modify it under the
10
 * terms of the GNU Lesser General Public License as published by the Free
11
 * Software Foundation, either version 3 of the License, or (at your option) any
12
 * later version.
13
 *
14
 * Grafizzi is distributed in the hope that it will be useful, but WITHOUT ANY
15
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16
 * A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
17
 * details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20
 * along with Grafizzi, in the COPYING.LESSER.txt file.  If not, see
21
 * <http://www.gnu.org/licenses/>
22
 */
23
24
namespace Grafizzi\Graph;
25
26
/**
27
 * Status: working, some possibly useless code. Handle error situations better.
28
 */
29
abstract class AbstractElement extends AbstractNamed implements ElementInterface {
30
  const DEPTH_INDENT = 2;
31
32
  /**
33
   * @var \Grafizzi\Graph\AttributeInterface[]
34
   */
35
  public $fAttributes = array();
36
37
  /**
38
   * @var \Grafizzi\Graph\AbstractElement[] $fChildren
39
   */
40
  public $fChildren = array();
41
42
  /**
43
   * The nesting level of the element.
44
   *
45
   * An unbound element, like the root graph, has depth 0.
46
   *
47
   * @var int
48
   */
49
  public $fDepth = 0;
50
51
  /**
52
   * The parent element, when bound, or null otherwise.
53
   *
54
   * @var ElementInterface
55
   */
56
  public $fParent = null;
57
58
  /**
59
   * Possibly not needed with an efficient garbage collector, but might help in
60
   * case of dependency loops.
61
   *
62
   * XXX 20120512 check if really useful.
63
   */
64 39
  public function __destruct() {
65
    try {
66 39
      $name = $this->getName();
67
    }
68
    catch (AttributeNameException $e) {
69
      $name = 'unnamed';
70
    }
71 39
    $type = $this->getType();
72 39
    $this->logger->debug("Destroying $type $name");
73 39
    foreach ($this->fAttributes as &$attribute) {
74 13
      unset($attribute);
75
    }
76 39
    foreach ($this->fChildren as &$child) {
77
      unset($child);
78
    }
79 39
  }
80
81
  /**
82
   * {@inheritdoc}
83
   */
84 39
  public function addChild(ElementInterface $child) {
85 39
    $name = $this->getName();
86 39
    $childName = $child->getName();
87 39
    $childType = $child->getType();
88 39
    $this->logger->debug("Adding child $childName, type $childType, to $name, depth {$this->fDepth}.");
89 39
    if (!in_array($childType, $this->getAllowedChildTypes())) {
90
      $message = "Invalid child type $childType for element $name.";
91
      $this->logger->error($message);
92
      throw new ChildTypeException($message);
93
    }
94 39
    $this->fChildren[$childName] = $child;
95 39
    $child->adjustDepth($this->fDepth + 1);
96 39
    $child->setParent($this);
97 39
    return $this;
98
  }
99
100
  /**
101
   * {@inheritdoc}
102
   */
103 39
  public function adjustDepth($extra = 0) {
104 39
    $this->logger->debug("Adjusting depth {$this->fDepth} by $extra.");
105 39
    $this->fDepth += $extra;
106
    /** @var AbstractElement $child */
107 39
    foreach ($this->fChildren as $child) {
108 2
      $child->adjustDepth($extra);
109
    }
110 39
    return $this->fDepth;
111
  }
112
113
  /**
114
   * Build the DOT string for this subtree.
115
   *
116
   * Ignores $directed.
117
   *
118
   * @see NamedInterface::build()
119
   *
120
   * @param bool $directed
121
   *
122
   * @return string
123
   */
124
  public function build($directed = null) {
125
    $type = $this->getType();
126
    $name = $this->getName();
127
    $this->logger->debug("Building element $name.");
128
    $attributes = array_map(function (AttributeInterface $attribute) use ($directed) {
129
      return $attribute->build($directed);
130
    }, $this->fAttributes);
131
    $name = $this->escape($name);
132
    $ret = str_repeat(' ', $this->fDepth * self::DEPTH_INDENT)
133
      . $this->buildAttributes($attributes, $type, $name)
134
      . ";\n";
135
    return $ret;
136
  }
137
138
  /**
139
   * @param array $attributes
140
   * @param string $type
141
   * @param string $name
142
   *
143
   * @return string
144
   */
145 28
  protected function buildAttributes($attributes, $type, $name) {
146 28
    $ret = '';
147 28
    if (!empty($attributes)) {
148 17
      $builtAttributes = implode(', ', array_filter($attributes));
149 17
      if (!empty($builtAttributes)) {
150 13
        $prefix = '';
151 13
        if ($type) {
152
          $prefix .= "$type ";
153
        }
154 13
        if ($name) {
155
          $prefix .= "$name ";
156
        }
157 13
        if (empty($prefix)) {
158 13
          $prefix = ' ';
159
        }
160
161 13
        $ret .= "{$prefix}[ $builtAttributes ]";
162
      }
163
    }
164
165 28
    $ret .= ";\n";
166 28
    return $ret;
167
  }
168
169
  /**
170
   * {@inheritdoc}
171
   */
172
  public static function getAllowedChildTypes() {
173
    return array();
174
  }
175
176
  /**
177
   * {@inheritdoc}
178
   */
179 1
  public function getAttributeByName($name) {
180 1
    $ret = isset($this->fAttributes[$name]) ? $this->fAttributes[$name] : null;
181 1
    $this->logger->debug("Getting attribute [$name]: " . print_r($ret, true) . ".");
182 1
    return $ret;
183
  }
184
185
  /**
186
   * {@inheritdoc}
187
   */
188 1
  public function getChildByName($name) {
189 1
    $ret = isset($this->fChildren[$name])
190 1
      ? $this->fChildren[$name]
191 1
      : null;
192 1
    return $ret;
193
  }
194
195
  /**
196
   * {@inheritdoc}
197
   */
198 1
  public function getParent() {
199 1
    return $this->fParent;
200
  }
201
202
  /**
203
   * {@inheritdoc}
204
   */
205 1
  public function getRoot() {
206 1
    $current = $this;
207
    // Beware of priorities: do not remove parentheses.
208 1
    while (($parent = $current->getParent()) instanceof ElementInterface) {
209 1
      $current = $parent;
210
    }
211 1
    return $current;
212
  }
213
214
  /**
215
   * {@inheritdoc}
216
   */
217 4
  public function removeAttribute(AttributeInterface $attribute) {
218 4
    $name = $attribute->getName();
219 4
    if (!isset($name)) {
220
      $message = 'Trying to remove unnamed attribute.';
221
      $this->logger->warning($message);
222
      throw new AttributeNameException($message);
223
    }
224 4
    $this->removeAttributeByName($name);
225 4
  }
226
227
  /**
228
   * {@inheritdoc}
229
   */
230 4
  public function removeAttributeByName($name) {
231 4
    $this->logger->debug("Removing attribute [$name].");
232 4
    unset($this->fAttributes[$name]);
233 4
  }
234
235
  /**
236
   * {@inheritdoc}
237
   */
238
  public function removeChild(ElementInterface $child) {
239
    $name = $child->getName();
240
    if (!isset($name)) {
241
      $message = 'Trying to remove unnamed child.';
242
      $this->logger->warning($message);
243
      throw new ChildNameException($message);
244
    }
245
    $ret = $this->removeChildByName($name);
246
    return $ret;
247
  }
248
249
  /**
250
   * {@inheritdoc}
251
   */
252
  public function removeChildByName($name) {
253
    $this->logger->debug("Removing child [$name].");
254
    if (isset($this->fChildren[$name])) {
255
      $child = $this->fChildren[$name];
256
      $child->adjustDepth(- $this->fDepth - 1);
257
      unset($this->fChildren[$name]);
258
      $ret = $child;
259
    }
260
    else {
261
      $ret = null;
262
    }
263
    return $ret;
264
  }
265
266
  /**
267
   * {@inheritdoc}
268
   */
269 41
  public function setAttribute(AttributeInterface $attribute) {
270 41
      $name = $attribute->getName();
271 41
      if (!isset($name)) {
272
        $message = 'Trying to set unnamed attribute.';
273
        $this->logger->warning($message, debug_backtrace(false));
274
        throw new ChildNameException($message);
275
      }
276 41
    $this->fAttributes[$name] = $attribute;
277 41
  }
278
279
  /**
280
   * {@inheritdoc}
281
   */
282 50
  public function setAttributes(array $attributes) {
283 50
    foreach ($attributes as $attribute) {
284 36
      if (!in_array('Grafizzi\\Graph\\AttributeInterface', class_implements($attribute))) {
285
        $message = 'Trying to set non-attribute as an attribute';
286
        $this->logger->warning($message);
287
        throw new AttributeNameException($message);
288
      }
289 36
      $this->setAttribute($attribute);
290
    }
291 50
  }
292
293
  /**
294
   * {@inheritdoc}
295
   */
296 39
  public function setParent(ElementInterface $parent) {
297 39
    $this->fParent = $parent;
298 39
  }
299
300
}
301