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