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