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: protected function validateParent(IContainer $parent)
240: {
241: }
242:
243:
244:
245: 246: 247: 248: 249: 250: 251:
252: private function refreshMonitors($depth, & $missing = NULL, & $listeners = array())
253: {
254: if ($this instanceof IContainer) {
255: foreach ($this->getComponents() as $component) {
256: if ($component instanceof Component) {
257: $component->refreshMonitors($depth + 1, $missing, $listeners);
258: }
259: }
260: }
261:
262: if ($missing === NULL) {
263: foreach ($this->monitors as $type => $rec) {
264: if (isset($rec[1]) && $rec[1] > $depth) {
265: if ($rec[3]) {
266: $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
267: $listeners[] = array($this, $rec[0]);
268: } else {
269: unset($this->monitors[$type]);
270: }
271: }
272: }
273:
274: } else {
275: foreach ($this->monitors as $type => $rec) {
276: if (isset($rec[0])) {
277: continue;
278:
279: } elseif (!$rec[3]) {
280: unset($this->monitors[$type]);
281:
282: } elseif (isset($missing[$type])) {
283: $this->monitors[$type] = array(NULL, NULL, NULL, TRUE);
284:
285: } else {
286: $this->monitors[$type] = NULL;
287: if ($obj = $this->lookup($type, FALSE)) {
288: $listeners[] = array($this, $obj);
289: } else {
290: $missing[$type] = TRUE;
291: }
292: $this->monitors[$type][3] = TRUE;
293: }
294: }
295: }
296:
297: if ($depth === 0) {
298: $method = $missing === NULL ? 'detached' : 'attached';
299: foreach ($listeners as $item) {
300: $item[0]->$method($item[1]);
301: }
302: }
303: }
304:
305:
306:
307:
308:
309:
310:
311: 312: 313:
314: public function __clone()
315: {
316: if ($this->parent === NULL) {
317: return;
318:
319: } elseif ($this->parent instanceof Container) {
320: $this->parent = $this->parent->_isCloning();
321: if ($this->parent === NULL) {
322: $this->refreshMonitors(0);
323: }
324:
325: } else {
326: $this->parent = NULL;
327: $this->refreshMonitors(0);
328: }
329: }
330:
331:
332:
333: 334: 335:
336: final public function __sleep()
337: {
338: throw new Nette\NotImplementedException('Object serialization is not supported by class ' . get_class($this));
339: }
340:
341:
342:
343: 344: 345:
346: final public function __wakeup()
347: {
348: throw new Nette\NotImplementedException('Object unserialization is not supported by class ' . get_class($this));
349: }
350:
351: }
352: