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