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