Adding nemo engine.
[nemo.git] / nemo-impl / src / main / java / org / opendaylight / nemo / intent / IntentResolverUtils.java
1 /*\r
2  * Copyright (c) 2015 Huawei, Inc. and others. All rights reserved.\r
3  *\r
4  * This program and the accompanying materials are made available under the\r
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
6  * and is available at http://www.eclipse.org/legal/epl-v10.html\r
7  */\r
8 \r
9 package org.opendaylight.nemo.intent;\r
10 \r
11 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.physical.network.rev151010.physical.network.physical.hosts.PhysicalHost;\r
12 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.networks.virtual.network.virtual.links.VirtualLink;\r
13 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.networks.virtual.network.virtual.nodes.VirtualNode;\r
14 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.intent.mapping.result.rev151010.intent.vn.mapping.results.user.intent.vn.mapping.IntentVnMappingResult;\r
15 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.intent.mapping.result.rev151010.intent.vn.mapping.results.user.intent.vn.mapping.intent.vn.mapping.result.VirtualResource;\r
16 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.common.rev151010.*;\r
17 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.engine.common.rev151010.PhysicalHostId;\r
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.engine.common.rev151010.PhysicalNodeId;\r
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.engine.common.rev151010.VirtualNodeId;\r
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.intent.rev151010.user.intent.Objects;\r
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.intent.rev151010.user.intent.objects.Connection;\r
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.intent.rev151010.user.intent.objects.Flow;\r
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.intent.rev151010.user.intent.objects.Node;\r
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.intent.rev151010.user.intent.operations.Operation;\r
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.object.rev151010.flow.instance.MatchItem;\r
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.object.rev151010.node.instance.Property;\r
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.object.rev151010.node.instance.SubNode;\r
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.object.rev151010.property.instance.property.values.StringValue;\r
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.operation.rev151010.operation.instance.Action;\r
30 import org.opendaylight.yangtools.yang.binding.DataObject;\r
31 \r
32 import java.util.ArrayList;\r
33 import java.util.List;\r
34 \r
35 /**\r
36  * Implement the common utilities frequently used in\r
37  * the intent resolution.\r
38  *\r
39  * @author Zhigang Ji\r
40  */\r
41 public class IntentResolverUtils {\r
42     /**\r
43      * Check whether the node is an external layer2 group or layer3 group.\r
44      *\r
45      * @param node The node to be checked.\r
46      * @return True if the node is an external layer3 group.\r
47      */\r
48     protected static boolean checkExternalLayer3Group(Node node) {\r
49         PropertyName propertyName = new PropertyName("ac-info-network");\r
50         Property property = getNodeProperty(node.getProperty(), propertyName);\r
51 \r
52         if ( null != property ) {\r
53             String propertyValue = property.getPropertyValues().getStringValue().get(0).getValue();\r
54 \r
55             if ( propertyValue.equals("layer3") ) {\r
56                 return true;\r
57             }\r
58         }\r
59 \r
60         return false;\r
61     }\r
62 \r
63     /**\r
64      * TODO\r
65      *\r
66      * @param physicalHosts TODO\r
67      * @param node TODO\r
68      * @return TODO\r
69      */\r
70     protected static PhysicalHost getPhysicalHost(List<PhysicalHost> physicalHosts, Node node) {\r
71         PhysicalHostId physicalHostId = new PhysicalHostId(node.getNodeId().getValue());\r
72 \r
73         return getPhysicalHost(physicalHosts, physicalHostId);\r
74     }\r
75 \r
76     /**\r
77      * TODO\r
78      *\r
79      * @param properties TODO\r
80      * @param propertyName TODO\r
81      * @return TODO\r
82      */\r
83     protected static Property getNodeProperty(List<Property> properties, PropertyName propertyName) {\r
84         if ( null != properties ) {\r
85             for ( Property property : properties ) {\r
86                 if ( property.getPropertyName().equals(propertyName) ) {\r
87                     return property;\r
88                 }\r
89             }\r
90         }\r
91 \r
92         return null;\r
93     }\r
94 \r
95     /**\r
96      * TODO\r
97      *\r
98      * @param property TODO\r
99      * @return TODO\r
100      */\r
101     protected static PhysicalNodeId generatePhysicalNodeIdFromNodeLocationProperty(Property property) {\r
102         String propertyValue = property.getPropertyValues().getStringValue().get(0).getValue();\r
103 \r
104         return new PhysicalNodeId(propertyValue.substring(0, propertyValue.lastIndexOf(':')));\r
105     }\r
106 \r
107     /**\r
108      * TODO\r
109      *\r
110      * @param intentVnMappingResults TODO\r
111      * @param intentId TODO\r
112      * @return TODO\r
113      */\r
114     protected static IntentVnMappingResult getIntentVnMappingResult(\r
115             List<IntentVnMappingResult> intentVnMappingResults, IntentId intentId) {\r
116         for ( IntentVnMappingResult intentVnMappingResult : intentVnMappingResults ) {\r
117             if ( intentVnMappingResult.getIntentId().equals(intentId) ) {\r
118                 return intentVnMappingResult;\r
119             }\r
120         }\r
121 \r
122         return null;\r
123     }\r
124 \r
125     /**\r
126      * TODO\r
127      *\r
128      * @param virtualNodes TODO\r
129      * @param virtualNodeId TODO\r
130      * @return TODO\r
131      */\r
132     protected static VirtualNode getVirtualNode(List<VirtualNode> virtualNodes,\r
133                                                 VirtualNodeId virtualNodeId) {\r
134         for ( VirtualNode virtualNode : virtualNodes ) {\r
135             if ( virtualNode.getNodeId().equals(virtualNodeId) ) {\r
136                 return virtualNode;\r
137             }\r
138         }\r
139 \r
140         return null;\r
141     }\r
142 \r
143     /**\r
144      * TODO\r
145      *\r
146      * @param nodes TODO\r
147      * @param nodeId TODO\r
148      * @return TODO\r
149      */\r
150     protected static Node getNode(List<Node> nodes, NodeId nodeId) {\r
151         for ( Node node : nodes ) {\r
152             if ( node.getNodeId().equals(nodeId) ) {\r
153                 return node;\r
154             }\r
155         }\r
156 \r
157         return null;\r
158     }\r
159 \r
160     /**\r
161      * TODO\r
162      *\r
163      * @param properties TODO\r
164      * @param propertyName TODO\r
165      * @return TODO\r
166      */\r
167     protected static org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.object.rev151010.connection.instance.Property getConnectionProperty(\r
168             List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.object.rev151010.connection.instance.Property> properties,\r
169             PropertyName propertyName) {\r
170         if ( null != properties ) {\r
171             for ( org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.object.rev151010.connection.instance.Property\r
172                     property : properties ) {\r
173                 if ( property.getPropertyName().equals(propertyName) ) {\r
174                     return property;\r
175                 }\r
176             }\r
177         }\r
178 \r
179         return null;\r
180     }\r
181 \r
182     /**\r
183      * TODO\r
184      *\r
185      * @param subNodes TODO\r
186      * @return TODO\r
187      */\r
188     protected static List<SubNode> sortSubNodes(List<SubNode> subNodes) {\r
189         if ( subNodes.isEmpty() || 1 == subNodes.size() ) {\r
190             return subNodes;\r
191         }\r
192 \r
193         List<SubNode> sortedSubNodes = new ArrayList<SubNode>(subNodes.size());\r
194         sortedSubNodes.addAll(subNodes);\r
195 \r
196         for ( SubNode subNode : subNodes ) {\r
197             sortedSubNodes.set(subNode.getOrder().intValue(), subNode);\r
198         }\r
199 \r
200         return sortedSubNodes;\r
201     }\r
202 \r
203     /**\r
204      * TODO\r
205      *\r
206      * @param subNodes TODO\r
207      * @param nodes TODO\r
208      * @return TODO\r
209      */\r
210     protected static boolean checkAllLayer2OperatingMode(List<SubNode> subNodes, List<Node> nodes) {\r
211         if ( subNodes.isEmpty() ) {\r
212             return false;\r
213         }\r
214 \r
215         Node node;\r
216         PropertyName propertyName = new PropertyName("operating-mode");\r
217         Property property;\r
218         String propertyValue;\r
219 \r
220         for ( SubNode subNode : subNodes ) {\r
221             node = getNode(nodes, subNode.getNodeId());\r
222 \r
223             if ( null == node ) {\r
224                 return false;\r
225             }\r
226 \r
227             property = getNodeProperty(node.getProperty(), propertyName);\r
228 \r
229             if ( null == property ) {\r
230                 return false;\r
231             }\r
232 \r
233             propertyValue = property.getPropertyValues().getStringValue().get(0).getValue();\r
234 \r
235             if ( !propertyValue.equals("layer2") ) {\r
236                 return false;\r
237             }\r
238         }\r
239 \r
240         return true;\r
241     }\r
242 \r
243     /**\r
244      * TODO\r
245      *\r
246      * @param subNodes TODO\r
247      * @param nodes TODO\r
248      * @return TODO\r
249      */\r
250     protected static boolean checkAllLayer3OperatingMode(List<SubNode> subNodes, List<Node> nodes) {\r
251         if ( subNodes.isEmpty() ) {\r
252             return false;\r
253         }\r
254 \r
255         Node node;\r
256         PropertyName propertyName = new PropertyName("operating-mode");\r
257         Property property;\r
258         String propertyValue;\r
259 \r
260         for ( SubNode subNode : subNodes ) {\r
261             node = getNode(nodes, subNode.getNodeId());\r
262 \r
263             if ( null == node ) {\r
264                 return false;\r
265             }\r
266 \r
267             property = getNodeProperty(node.getProperty(), propertyName);\r
268 \r
269             if ( null == property ) {\r
270                 return false;\r
271             }\r
272 \r
273             propertyValue = property.getPropertyValues().getStringValue().get(0).getValue();\r
274 \r
275             if ( !propertyValue.equals("layer3") ) {\r
276                 return false;\r
277             }\r
278         }\r
279 \r
280         return true;\r
281     }\r
282 \r
283     /**\r
284      * TODO\r
285      *\r
286      * @param virtualLinks TODO\r
287      * @param srcVirtualNodeId TODO\r
288      * @param destVirtualNodeId TODO\r
289      * @return TODO\r
290      */\r
291     protected static VirtualLink getVirtualLink(List<VirtualLink> virtualLinks,\r
292                                                 VirtualNodeId srcVirtualNodeId,\r
293                                                 VirtualNodeId destVirtualNodeId) {\r
294         for ( VirtualLink virtualLink : virtualLinks ) {\r
295             if ( virtualLink.getSrcNodeId().equals(srcVirtualNodeId)\r
296                     && virtualLink.getDestNodeId().equals(destVirtualNodeId) ) {\r
297                 return virtualLink;\r
298             }\r
299         }\r
300 \r
301         return null;\r
302     }\r
303 \r
304     /**\r
305      * TODO\r
306      *\r
307      * @param objects TODO\r
308      * @param objectId TODO\r
309      * @return TODO\r
310      */\r
311     protected static DataObject getObject(Objects objects, ObjectId objectId) {\r
312         List<Node> nodes = objects.getNode();\r
313 \r
314         if ( null != nodes ) {\r
315             NodeId nodeId = new NodeId(objectId.getValue());\r
316             Node node = getNode(nodes, nodeId);\r
317 \r
318             if ( null != node ) {\r
319                 return node;\r
320             }\r
321         }\r
322 \r
323         List<Connection> connections = objects.getConnection();\r
324 \r
325         if ( null != connections ) {\r
326             ConnectionId connectionId = new ConnectionId(objectId.getValue());\r
327             Connection connection = getConnection(connections, connectionId);\r
328 \r
329             if ( null != connection ) {\r
330                 return connection;\r
331             }\r
332         }\r
333 \r
334         List<Flow> flows = objects.getFlow();\r
335 \r
336         if ( null != flows ) {\r
337             FlowId flowId = new FlowId(objectId.getValue());\r
338             Flow flow = getFlow(flows, flowId);\r
339 \r
340             if ( null != flow ) {\r
341                 return flow;\r
342             }\r
343         }\r
344 \r
345         return null;\r
346     }\r
347 \r
348     /**\r
349      * TODO\r
350      *\r
351      * @param operations TODO\r
352      * @param operation TODO\r
353      * @return TODO\r
354      */\r
355     protected static List<Operation> getSameTargetObjectOperations(List<Operation> operations,\r
356                                                                    Operation operation) {\r
357         // TODO\r
358 \r
359         return new ArrayList<Operation>(0);\r
360     }\r
361 \r
362     /**\r
363      * TODO\r
364      *\r
365      * @param operations TODO\r
366      * @param operation TODO\r
367      * @param greaterPriorityOperations TODO\r
368      * @param equalPriorityOperations TODO\r
369      */\r
370     protected static void getGreaterAndEqualPriorityOperations(List<Operation> operations, Operation operation,\r
371                                                                List<Operation> greaterPriorityOperations,\r
372                                                                List<Operation> equalPriorityOperations) {\r
373         // TODO\r
374 \r
375         return;\r
376     }\r
377 \r
378     /**\r
379      * TODO\r
380      *\r
381      * @param operations TODO\r
382      * @param operation TODO\r
383      * @return TODO\r
384      */\r
385     protected static Operation getConflictingOperation(List<Operation> operations,\r
386                                                        Operation operation) {\r
387         // TODO\r
388 \r
389         return null;\r
390     }\r
391 \r
392     /**\r
393      * TODO\r
394      *\r
395      * @param operations TODO\r
396      * @param operation TODO\r
397      * @return TODO\r
398      */\r
399     protected static List<Operation> getConflictingOperations(List<Operation> operations,\r
400                                                               Operation operation) {\r
401         // TODO\r
402 \r
403         return null;\r
404     }\r
405 \r
406     /**\r
407      * TODO\r
408      *\r
409      * @param actions TODO\r
410      * @param actionName TODO\r
411      * @return TODO\r
412      */\r
413     protected static Action getAction(List<Action> actions, ActionName actionName) {\r
414         for ( Action action : actions ) {\r
415             if ( action.getActionName().equals(actionName) ) {\r
416                 return action;\r
417             }\r
418         }\r
419 \r
420         return null;\r
421     }\r
422 \r
423     /**\r
424      * TODO\r
425      *\r
426      * @param virtualNodes TODO\r
427      * @param flow TODO\r
428      * @param nodes TODO\r
429      * @param intentVnMappingResults TODO\r
430      * @return TODO\r
431      */\r
432     protected static VirtualNode getSourceVirtualRouterOfFlow(List<VirtualNode> virtualNodes,\r
433                                                               Flow flow, List<Node> nodes,\r
434                                                               List<IntentVnMappingResult> intentVnMappingResults) {\r
435         MatchItemName matchItemName = new MatchItemName("src-ip");\r
436         MatchItem matchItem = getMatchItem(flow.getMatchItem(), matchItemName);\r
437 \r
438         if ( null == matchItem ) {\r
439             return null;\r
440         }\r
441 \r
442         String matchItemValue = matchItem.getMatchItemValue().getStringValue();\r
443         VirtualNode virtualNode = getVirtualRouterWithIpPrefix(virtualNodes,\r
444                 matchItemValue, nodes, intentVnMappingResults);\r
445 \r
446         return virtualNode;\r
447     }\r
448 \r
449     /**\r
450      * TODO\r
451      *\r
452      * @param virtualNodes TODO\r
453      * @param flow TODO\r
454      * @param nodes TODO\r
455      * @param intentVnMappingResults TODO\r
456      * @return TODO\r
457      */\r
458     protected static VirtualNode getDestinationVirtualRouterOfFlow(List<VirtualNode> virtualNodes,\r
459                                                                    Flow flow, List<Node> nodes,\r
460                                                                    List<IntentVnMappingResult> intentVnMappingResults) {\r
461         MatchItemName matchItemName = new MatchItemName("dst-ip");\r
462         MatchItem matchItem = getMatchItem(flow.getMatchItem(), matchItemName);\r
463 \r
464         if ( null == matchItem ) {\r
465             return null;\r
466         }\r
467 \r
468         String matchItemValue = matchItem.getMatchItemValue().getStringValue();\r
469         VirtualNode virtualNode = getVirtualRouterWithIpPrefix(virtualNodes,\r
470                 matchItemValue, nodes, intentVnMappingResults);\r
471 \r
472         return virtualNode;\r
473     }\r
474 \r
475     /**\r
476      * TODO\r
477      *\r
478      * @param virtualResources TODO\r
479      * @return TODO\r
480      */\r
481     protected static List<VirtualResource> sortVirtualResources(List<VirtualResource> virtualResources) {\r
482         if ( virtualResources.isEmpty() || 1 == virtualResources.size() ) {\r
483             return virtualResources;\r
484         }\r
485 \r
486         List<VirtualResource> sortedVirtualResources = new ArrayList<VirtualResource>(virtualResources.size());\r
487         sortedVirtualResources.addAll(virtualResources);\r
488 \r
489         for ( VirtualResource virtualResource : virtualResources ) {\r
490             sortedVirtualResources.set(virtualResource.getOrder().intValue(), virtualResource);\r
491         }\r
492 \r
493         return sortedVirtualResources;\r
494     }\r
495 \r
496     /**\r
497      * TODO\r
498      *\r
499      * @param physicalHosts TODO\r
500      * @param physicalHostId TODO\r
501      * @return TODO\r
502      */\r
503     private static PhysicalHost getPhysicalHost(List<PhysicalHost> physicalHosts,\r
504                                                 PhysicalHostId physicalHostId) {\r
505         for ( PhysicalHost physicalHost : physicalHosts ) {\r
506             if ( physicalHost.getHostId().equals(physicalHostId) ) {\r
507                 return physicalHost;\r
508             }\r
509         }\r
510 \r
511         return null;\r
512     }\r
513 \r
514     /**\r
515      * TODO\r
516      *\r
517      * @param connections TODO\r
518      * @param connectionId TODO\r
519      * @return TODO\r
520      */\r
521     private static Connection getConnection(List<Connection> connections, ConnectionId connectionId) {\r
522         for ( Connection connection : connections ) {\r
523             if ( connection.getConnectionId().equals(connectionId) ) {\r
524                 return connection;\r
525             }\r
526         }\r
527 \r
528         return null;\r
529     }\r
530 \r
531     /**\r
532      * TODO\r
533      *\r
534      * @param flows TODO\r
535      * @param flowId TODO\r
536      * @return TODO\r
537      */\r
538     private static Flow getFlow(List<Flow> flows, FlowId flowId) {\r
539         for ( Flow flow : flows ) {\r
540             if ( flow.getFlowId().equals(flowId) ) {\r
541                 return flow;\r
542             }\r
543         }\r
544 \r
545         return null;\r
546     }\r
547 \r
548     /**\r
549      * TODO\r
550      *\r
551      * @param matchItems TODO\r
552      * @param matchItemName TODO\r
553      * @return TODO\r
554      */\r
555     private static MatchItem getMatchItem(List<MatchItem> matchItems, MatchItemName matchItemName) {\r
556         if ( null != matchItems ) {\r
557             for ( MatchItem matchItem : matchItems ) {\r
558                 if ( matchItem.getMatchItemName().equals(matchItemName) ) {\r
559                     return matchItem;\r
560                 }\r
561             }\r
562         }\r
563 \r
564         return null;\r
565     }\r
566 \r
567     /**\r
568      * TODO\r
569      *\r
570      * @param virtualNodes TODO\r
571      * @param ipPrefix TODO\r
572      * @param nodes TODO\r
573      * @param intentVnMappingResults TODO\r
574      * @return TODO\r
575      */\r
576     private static VirtualNode getVirtualRouterWithIpPrefix(List<VirtualNode> virtualNodes,\r
577                                                             String ipPrefix, List<Node> nodes,\r
578                                                             List<IntentVnMappingResult> intentVnMappingResults) {\r
579         NodeType layer2GroupNodeType = new NodeType("l2-group");\r
580         NodeType externalGroupNodeType = new NodeType("ext-group");\r
581         PropertyName propertyName = new PropertyName("ip-prefix");\r
582         Property property;\r
583         List<StringValue> propertyValues;\r
584         IntentVnMappingResult intentVnMappingResult;\r
585         VirtualResource virtualResource;\r
586         VirtualNodeId virtualNodeId;\r
587         VirtualNode virtualNode;\r
588 \r
589         for ( Node node : nodes ) {\r
590             if ( node.getNodeType().equals(layer2GroupNodeType)\r
591                     || node.getNodeType().equals(externalGroupNodeType) ) {\r
592                 property = getNodeProperty(node.getProperty(), propertyName);\r
593 \r
594                 if ( null != property ) {\r
595                     propertyValues = property.getPropertyValues().getStringValue();\r
596 \r
597                     if ( containPropertyValue(propertyValues, ipPrefix) ) {\r
598                         intentVnMappingResult = getIntentVnMappingResult(intentVnMappingResults,\r
599                                 new IntentId(node.getNodeId().getValue()));\r
600 \r
601                         if ( null == intentVnMappingResult ) {\r
602                             return null;\r
603                         }\r
604 \r
605                         virtualResource = intentVnMappingResult.getVirtualResource().get(0);\r
606 \r
607                         if ( VirtualResource.VirtualResourceType.Vport\r
608                                 == virtualResource.getVirtualResourceType() ) {\r
609                             virtualNodeId = new VirtualNodeId(\r
610                                     virtualResource.getParentVirtualResourceEntityId().getValue());\r
611                         } else if ( VirtualResource.VirtualResourceType.Vnode\r
612                                 == virtualResource.getVirtualResourceType() ) {\r
613                             virtualNodeId = new VirtualNodeId(\r
614                                     virtualResource.getVirtualResourceEntityId().getValue());\r
615                         } else {\r
616                             return null;\r
617                         }\r
618 \r
619                         virtualNode = getVirtualNode(virtualNodes, virtualNodeId);\r
620 \r
621                         if ( null == virtualNode ) {\r
622                             return null;\r
623                         }\r
624 \r
625                         if ( VirtualNode.NodeType.Vrouter == virtualNode.getNodeType() ) {\r
626                             return virtualNode;\r
627                         }\r
628                     }\r
629                 }\r
630             }\r
631         }\r
632 \r
633         return null;\r
634     }\r
635 \r
636     /**\r
637      * TODO\r
638      *\r
639      * @param propertyValues TODO\r
640      * @param propertyValue TODO\r
641      * @return TODO\r
642      */\r
643     private static boolean containPropertyValue(List<StringValue> propertyValues, String propertyValue) {\r
644         for ( StringValue stringValue : propertyValues ) {\r
645             if ( stringValue.getValue().equals(propertyValue) ) {\r
646                 return true;\r
647             }\r
648         }\r
649 \r
650         return false;\r
651     }\r
652 }\r