1: <?php
2:
3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Nette\ComponentModel;
13:
14: use Nette;
15:
16:
17:
18: 19: 20: 21: 22: 23: 24: 25: 26: 27:
28: abstract class Component extends Nette\Object implements IComponent
29: {
30:
31: private $parent;
32:
33:
34: private $name;
35:
36:
37: private $monitors = array();
38:
39:
40:
41: 42:
43: public function __construct(IContainer $parent = NULL, $name = NULL)
44: {
45: if ($parent !== NULL) {
46: $parent->addComponent($this, $name);
47:
48: } elseif (is_string($name)) {
49: $this->name = $name;
50: }
51: }
52:
53:
54:
55: 56: 57: 58: 59: 60:
61: public function lookup($type, $need = TRUE)
62: {
63: if (!isset($this->monitors[$type])) {
64: $obj = $this->parent;
65: $path = self::NAME_SEPARATOR . $this->name;
66: $depth = 1;
67: while ($obj !== NULL) {
68: if ($obj instanceof $type) {
69: break;
70: }
71: $path = self::NAME_SEPARATOR . $obj->getName() . $path;
72: $depth++;
73: $obj = $obj->getParent();
74: if ($obj === $this) {
75: $obj = NULL;
76: }
77: }
78:
79: if ($obj) {
80: $this->monitors[$type] = array($obj, $depth, substr($path, 1), FALSE);
81:
82: } else {
83: $this->monitors[$type] = array(NULL, NULL, NULL, FALSE);
84: }
85: }
86:
87: if ($need && $this->monitors[$type][0] === NULL) {
88: throw new Nette\InvalidStateException("Component '$this->name' is not attached to '$type'.");
89: }
90:
91: return $this->monitors[$type][0];
92: }
93:
94:
95:
96: 97: 98: 99: 100: 101: 102:
103: public function lookupPath($type, $need = TRUE)
104: {
105: $this->lookup($type, $need);
106: return $this->monitors[$type][2];
107: }
108:
109:
110:
111: 112: 113: 114: 115:
116: public function monitor($type)
117: {
118: if (empty($this->monitors[$type][3])) {
119: if ($obj = $this->lookup($type, FALSE)) {
120: $this->attached($obj);
121: }
122: $this->monitors[$type][3] = TRUE;
123: }
124: }
125:
126:
127:
128: 129: 130: 131: 132:
133: public function unmonitor($type)
134: {
135: unset($this->monitors[$type]);
136: }
137:
138:
139:
140: 141: 142: 143: 144: 145:
146: protected function attached($obj)
147: {
148: }
149:
150:
151:
152: 153: 154: 155: 156: 157:
158: protected function detached($obj)
159: {
160: }
161:
162:
163:
164:
165:
166:
167:
168: 169: 170:
171: final public function getName()
172: {
173: return $this->name;
174: }
175:
176:
177:
178: 179: 180: 181:
182: final public function getParent()
183: {
184: return $this->parent;
185: }
186:
187:
188:
189: 190: 191: 192: 193: 194: 195: 196: 197:
198: public function setParent(IContainer $parent = NULL, $name = NULL)
199: {
200: if ($parent === NULL && $this->parent === NULL && $name !== NULL) {
201: $this->name = $name;
202: return $this;
203:
204: } elseif ($parent === $this->parent && $name === NULL) {
205: return $this;
206: }
207:
208:
209: if ($this->parent !== NULL && $parent !== NULL) {
210: throw new Nette\InvalidStateException("Component '$this->name' already has a parent.");
211: }
212:
213:
214: if ($parent === NULL) {
215: $this->refreshMonitors(0);
216: $this->parent = NULL;
217:
218: } else {
219: $this->validateParent($parent);
220: $this->parent = $parent;
221: if ($name !== NULL) {
222: $this->name = $name;
223: }
224:
225: $tmp = array();
226: $this->refreshMonitors(0, $tmp);
227: }
228: return $this;
229: }
230:
231:
232:
233: 234: 235: 236: 237: 238: 239:
240: protected function validateParent(IContainer $parent)
241: {
242: }
243:
244:
245:
246: 247: 248: 249: 250: 251: 252:
253: private function refreshMonitors($depth, & $missing = NULL, & $listeners = array())
254: {
255: if ($this instanceof IContainer) {
256: foreach ($this->getComponents() as $component) {
257: if ($component instanceof Component) {
258: $component->refreshMonitors($depth + 1, $missing, $listeners);
259: }
260: }
261: }
262:
263: if ($missing === NULL) {
264: foreach ($this->monitors as $type => $rec) {
265: if (isset($rec[1]) && $rec[1] > $depth) {
266: if ($rec[3]) {
267: $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
268: $listeners[] = array($this, $rec[0]);
269: } else {
270: unset($this->monitors[$type]);
271: }
272: }
273: }
274:
275: } else {
276: foreach ($this->monitors as $type => $rec) {
277: if (isset($rec[0])) {
278: continue;
279:
280: } elseif (!$rec[3]) {
281: unset($this->monitors[$type]);
282:
283: } elseif (isset($missing[$type])) {
284: $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
285:
286: } else {
287: $this->monitors[$type] = NULL;
288: if ($obj = $this->lookup($type, FALSE)) {
289: $listeners[] = array($this, $obj);
290: } else {
291: $missing[$type] = TRUE;
292: }
293: $this->monitors[$type][3] = TRUE;
294: }
295: }
296: }
297:
298: if ($depth === 0) {
299: $method = $missing === NULL ? 'detached' : 'attached';
300: foreach ($listeners as $item) {
301: $item[0]->$method($item[1]);
302: }
303: }
304: }
305:
306:
307:
308:
309:
310:
311:
312: 313: 314:
315: public function __clone()
316: {
317: if ($this->parent === NULL) {
318: return;
319:
320: } elseif ($this->parent instanceof Container) {
321: $this->parent = $this->parent->_isCloning();
322: if ($this->parent === NULL) {
323: $this->refreshMonitors(0);
324: }
325:
326: } else {
327: $this->parent = NULL;
328: $this->refreshMonitors(0);
329: }
330: }
331:
332:
333:
334: 335: 336:
337: final public function __sleep()
338: {
339: throw new Nette\NotImplementedException('Object serialization is not supported by class ' . get_class($this));
340: }
341:
342:
343:
344: 345: 346:
347: final public function __wakeup()
348: {
349: throw new Nette\NotImplementedException('Object unserialization is not supported by class ' . get_class($this));
350: }
351:
352: }
353: