2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.controller.sal.restconf.impl;
10 import com.google.common.base.Function;
11 import com.google.common.base.Objects;
12 import com.google.common.base.Optional;
13 import com.google.common.base.Preconditions;
14 import com.google.common.base.Predicate;
15 import com.google.common.base.Splitter;
16 import com.google.common.base.Strings;
17 import com.google.common.collect.BiMap;
18 import com.google.common.collect.FluentIterable;
19 import com.google.common.collect.HashBiMap;
20 import com.google.common.collect.Iterables;
21 import com.google.common.collect.Lists;
23 import java.io.UnsupportedEncodingException;
25 import java.net.URLDecoder;
26 import java.net.URLEncoder;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collections;
30 import java.util.Comparator;
31 import java.util.HashMap;
32 import java.util.List;
35 import java.util.concurrent.ConcurrentHashMap;
37 import javax.ws.rs.core.Response.Status;
39 import org.opendaylight.controller.sal.core.api.mount.MountInstance;
40 import org.opendaylight.controller.sal.core.api.mount.MountService;
41 import org.opendaylight.controller.sal.rest.impl.RestUtil;
42 import org.opendaylight.controller.sal.rest.impl.RestconfProvider;
43 import org.opendaylight.controller.sal.restconf.impl.InstanceIdWithSchemaNode;
44 import org.opendaylight.controller.sal.restconf.impl.ResponseException;
45 import org.opendaylight.controller.sal.restconf.impl.RestCodec;
46 import org.opendaylight.yangtools.concepts.Codec;
47 import org.opendaylight.yangtools.yang.common.QName;
48 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier;
49 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdentifierBuilder;
50 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
51 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
52 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument;
53 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
54 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
55 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
56 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
57 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
58 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
59 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
60 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
61 import org.opendaylight.yangtools.yang.model.api.Module;
62 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
63 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
64 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
65 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
66 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
70 public class ControllerContext implements SchemaContextListener {
71 private final static Logger LOG = LoggerFactory.getLogger( ControllerContext.class );
73 private final static ControllerContext INSTANCE = new ControllerContext();
75 private final static String NULL_VALUE = "null";
77 private final static String MOUNT_MODULE = "yang-ext";
79 private final static String MOUNT_NODE = "mount";
81 public final static String MOUNT = "yang-ext:mount";
83 private final static String URI_ENCODING_CHAR_SET = "ISO-8859-1";
85 private final BiMap<URI, String> uriToModuleName = HashBiMap.<URI, String> create();
87 private final Map<String, URI> moduleNameToUri = uriToModuleName.inverse();
89 private final Map<QName, RpcDefinition> qnameToRpc = new ConcurrentHashMap<>();
91 private volatile SchemaContext globalSchema;
92 private volatile MountService mountService;
94 public void setGlobalSchema( final SchemaContext globalSchema ) {
95 this.globalSchema = globalSchema;
98 public void setMountService( final MountService mountService ) {
99 this.mountService = mountService;
102 private ControllerContext() {
105 public static ControllerContext getInstance() {
106 return ControllerContext.INSTANCE;
109 private void checkPreconditions() {
110 if( globalSchema == null ) {
111 throw new ResponseException( Status.SERVICE_UNAVAILABLE, RestconfProvider.NOT_INITALIZED_MSG );
115 public void setSchemas( final SchemaContext schemas ) {
116 this.onGlobalContextUpdated( schemas );
119 public InstanceIdWithSchemaNode toInstanceIdentifier( final String restconfInstance ) {
120 return this.toIdentifier( restconfInstance, false );
123 public InstanceIdWithSchemaNode toMountPointIdentifier( final String restconfInstance ) {
124 return this.toIdentifier( restconfInstance, true );
127 private InstanceIdWithSchemaNode toIdentifier( final String restconfInstance,
128 final boolean toMountPointIdentifier ) {
129 this.checkPreconditions();
131 Iterable<String> split = Splitter.on( "/" ).split( restconfInstance );
132 final ArrayList<String> encodedPathArgs = Lists.<String> newArrayList( split );
133 final List<String> pathArgs = this.urlPathArgsDecode( encodedPathArgs );
134 this.omitFirstAndLastEmptyString( pathArgs );
135 if( pathArgs.isEmpty() ) {
139 String first = pathArgs.iterator().next();
140 final String startModule = ControllerContext.toModuleName( first );
141 if( startModule == null ) {
142 throw new ResponseException( Status.BAD_REQUEST,
143 "First node in URI has to be in format \"moduleName:nodeName\"" );
146 InstanceIdentifierBuilder builder = InstanceIdentifier.builder();
147 Module latestModule = this.getLatestModule( globalSchema, startModule );
148 InstanceIdWithSchemaNode iiWithSchemaNode = this.collectPathArguments( builder, pathArgs,
149 latestModule, null, toMountPointIdentifier );
151 if( iiWithSchemaNode == null ) {
152 throw new ResponseException( Status.BAD_REQUEST, "URI has bad format" );
155 return iiWithSchemaNode;
158 private List<String> omitFirstAndLastEmptyString( final List<String> list ) {
159 if( list.isEmpty() ) {
163 String head = list.iterator().next();
164 if( head.isEmpty() ) {
168 if( list.isEmpty() ) {
172 String last = list.get( list.size() - 1 );
173 if( last.isEmpty() ) {
174 list.remove( list.size() - 1 );
180 private Module getLatestModule( final SchemaContext schema, final String moduleName ) {
181 Preconditions.checkArgument( schema != null );
182 Preconditions.checkArgument( moduleName != null && !moduleName.isEmpty() );
184 Predicate<Module> filter = new Predicate<Module>() {
186 public boolean apply( Module m ) {
187 return Objects.equal( m.getName(), moduleName );
191 Iterable<Module> modules = Iterables.filter( schema.getModules(), filter );
192 return this.filterLatestModule( modules );
195 private Module filterLatestModule( final Iterable<Module> modules ) {
196 Module latestModule = modules.iterator().hasNext() ? modules.iterator().next() : null;
197 for( final Module module : modules ) {
198 if( module.getRevision().after( latestModule.getRevision() ) ) {
199 latestModule = module;
205 public Module findModuleByName( final String moduleName ) {
206 this.checkPreconditions();
207 Preconditions.checkArgument( moduleName != null && !moduleName.isEmpty() );
208 return this.getLatestModule( globalSchema, moduleName );
211 public Module findModuleByName( final MountInstance mountPoint, final String moduleName ) {
212 Preconditions.checkArgument( moduleName != null && mountPoint != null );
214 final SchemaContext mountPointSchema = mountPoint.getSchemaContext();
215 return mountPointSchema == null ? null : this.getLatestModule( mountPointSchema, moduleName );
218 public Module findModuleByNamespace( final URI namespace ) {
219 this.checkPreconditions();
220 Preconditions.checkArgument( namespace != null );
222 final Set<Module> moduleSchemas = globalSchema.findModuleByNamespace( namespace );
223 return moduleSchemas == null ? null : this.filterLatestModule( moduleSchemas );
226 public Module findModuleByNamespace( final MountInstance mountPoint, final URI namespace ) {
227 Preconditions.checkArgument( namespace != null && mountPoint != null );
229 final SchemaContext mountPointSchema = mountPoint.getSchemaContext();
230 Set<Module> moduleSchemas = mountPointSchema == null ? null :
231 mountPointSchema.findModuleByNamespace( namespace );
232 return moduleSchemas == null ? null : this.filterLatestModule( moduleSchemas );
235 public Module findModuleByNameAndRevision( final QName module ) {
236 this.checkPreconditions();
237 Preconditions.checkArgument( module != null && module.getLocalName() != null &&
238 module.getRevision() != null );
240 return globalSchema.findModuleByName( module.getLocalName(), module.getRevision() );
243 public Module findModuleByNameAndRevision( final MountInstance mountPoint, final QName module ) {
244 this.checkPreconditions();
245 Preconditions.checkArgument( module != null && module.getLocalName() != null &&
246 module.getRevision() != null && mountPoint != null );
248 SchemaContext schemaContext = mountPoint.getSchemaContext();
249 return schemaContext == null ? null :
250 schemaContext.findModuleByName( module.getLocalName(), module.getRevision() );
253 public DataNodeContainer getDataNodeContainerFor( final InstanceIdentifier path ) {
254 this.checkPreconditions();
256 final List<PathArgument> elements = path.getPath();
257 PathArgument head = elements.iterator().next();
258 final QName startQName = head.getNodeType();
259 final Module initialModule = globalSchema.findModuleByNamespaceAndRevision(
260 startQName.getNamespace(), startQName.getRevision() );
261 DataNodeContainer node = initialModule;
262 for( final PathArgument element : elements ) {
263 QName _nodeType = element.getNodeType();
264 final DataSchemaNode potentialNode = ControllerContext.childByQName( node, _nodeType );
265 if( potentialNode == null || !this.isListOrContainer( potentialNode ) ) {
268 node = (DataNodeContainer) potentialNode;
274 public String toFullRestconfIdentifier( final InstanceIdentifier path ) {
275 this.checkPreconditions();
277 final List<PathArgument> elements = path.getPath();
278 final StringBuilder builder = new StringBuilder();
279 PathArgument head = elements.iterator().next();
280 final QName startQName = head.getNodeType();
281 final Module initialModule = globalSchema.findModuleByNamespaceAndRevision(
282 startQName.getNamespace(), startQName.getRevision() );
283 DataNodeContainer node = initialModule;
284 for( final PathArgument element : elements ) {
285 QName _nodeType = element.getNodeType();
286 final DataSchemaNode potentialNode = ControllerContext.childByQName( node, _nodeType );
287 if( !this.isListOrContainer( potentialNode ) ) {
290 node = ((DataNodeContainer) potentialNode);
291 builder.append( this.convertToRestconfIdentifier( element, node ) );
294 return builder.toString();
297 public String findModuleNameByNamespace( final URI namespace ) {
298 this.checkPreconditions();
300 String moduleName = this.uriToModuleName.get( namespace );
301 if( moduleName == null ) {
302 final Module module = this.findModuleByNamespace( namespace );
303 if( module != null ) {
304 moduleName = module.getName();
305 this.uriToModuleName.put( namespace, moduleName );
312 public String findModuleNameByNamespace( final MountInstance mountPoint, final URI namespace ) {
313 final Module module = this.findModuleByNamespace( mountPoint, namespace );
314 return module == null ? null : module.getName();
317 public URI findNamespaceByModuleName( final String moduleName ) {
318 URI namespace = this.moduleNameToUri.get( moduleName );
319 if( namespace == null ) {
320 Module module = this.findModuleByName( moduleName );
321 if( module != null ) {
322 URI _namespace = module.getNamespace();
323 namespace = _namespace;
324 this.uriToModuleName.put( namespace, moduleName );
330 public URI findNamespaceByModuleName( final MountInstance mountPoint, final String moduleName ) {
331 final Module module = this.findModuleByName( mountPoint, moduleName );
332 return module == null ? null : module.getNamespace();
335 public Set<Module> getAllModules( final MountInstance mountPoint ) {
336 this.checkPreconditions();
338 SchemaContext schemaContext = mountPoint == null ? null : mountPoint.getSchemaContext();
339 return schemaContext == null ? null : schemaContext.getModules();
342 public Set<Module> getAllModules() {
343 this.checkPreconditions();
344 return globalSchema.getModules();
347 public CharSequence toRestconfIdentifier( final QName qname ) {
348 this.checkPreconditions();
350 String module = this.uriToModuleName.get( qname.getNamespace() );
351 if( module == null ) {
352 final Module moduleSchema = globalSchema.findModuleByNamespaceAndRevision(
353 qname.getNamespace(), qname.getRevision() );
354 if( moduleSchema == null ) {
358 this.uriToModuleName.put( qname.getNamespace(), moduleSchema.getName() );
359 module = moduleSchema.getName();
362 StringBuilder builder = new StringBuilder();
363 builder.append( module );
364 builder.append( ":" );
365 builder.append( qname.getLocalName() );
366 return builder.toString();
369 public CharSequence toRestconfIdentifier( final MountInstance mountPoint, final QName qname ) {
370 if( mountPoint == null ) {
374 SchemaContext schemaContext = mountPoint.getSchemaContext();
376 final Module moduleSchema = schemaContext.findModuleByNamespaceAndRevision(
377 qname.getNamespace(), qname.getRevision() );
378 if( moduleSchema == null ) {
382 StringBuilder builder = new StringBuilder();
383 builder.append( moduleSchema.getName() );
384 builder.append( ":" );
385 builder.append( qname.getLocalName() );
386 return builder.toString();
389 private static DataSchemaNode childByQName( final ChoiceNode container, final QName name ) {
390 for( final ChoiceCaseNode caze : container.getCases() ) {
391 final DataSchemaNode ret = ControllerContext.childByQName( caze, name );
400 private static DataSchemaNode childByQName( final ChoiceCaseNode container, final QName name ) {
401 return container.getDataChildByName( name );
404 private static DataSchemaNode childByQName( final ContainerSchemaNode container, final QName name ) {
405 return ControllerContext.dataNodeChildByQName( container, name );
408 private static DataSchemaNode childByQName( final ListSchemaNode container, final QName name ) {
409 return ControllerContext.dataNodeChildByQName( container, name );
412 private static DataSchemaNode childByQName( final Module container, final QName name ) {
413 return ControllerContext.dataNodeChildByQName( container, name );
416 private static DataSchemaNode childByQName( final DataSchemaNode container, final QName name ) {
420 private static DataSchemaNode dataNodeChildByQName( final DataNodeContainer container, final QName name ) {
421 DataSchemaNode ret = container.getDataChildByName( name );
423 for( final DataSchemaNode node : container.getChildNodes() ) {
424 if( (node instanceof ChoiceCaseNode) ) {
425 final ChoiceCaseNode caseNode = ((ChoiceCaseNode) node);
426 DataSchemaNode childByQName = ControllerContext.childByQName( caseNode, name );
427 if( childByQName != null ) {
436 private String toUriString( final Object object ) throws UnsupportedEncodingException {
437 return object == null ? "" :
438 URLEncoder.encode( object.toString(), ControllerContext.URI_ENCODING_CHAR_SET );
441 private InstanceIdWithSchemaNode collectPathArguments( final InstanceIdentifierBuilder builder,
442 final List<String> strings, final DataNodeContainer parentNode, final MountInstance mountPoint,
443 final boolean returnJustMountPoint ) {
444 Preconditions.<List<String>> checkNotNull( strings );
446 if( parentNode == null ) {
450 if( strings.isEmpty() ) {
451 return new InstanceIdWithSchemaNode( builder.toInstance(),
452 ((DataSchemaNode) parentNode), mountPoint );
455 String head = strings.iterator().next();
456 final String nodeName = this.toNodeName( head );
457 final String moduleName = ControllerContext.toModuleName( head );
459 DataSchemaNode targetNode = null;
460 if( !Strings.isNullOrEmpty( moduleName ) ) {
461 if( Objects.equal( moduleName, ControllerContext.MOUNT_MODULE ) &&
462 Objects.equal( nodeName, ControllerContext.MOUNT_NODE ) ) {
463 if( mountPoint != null ) {
464 throw new ResponseException( Status.BAD_REQUEST,
465 "Restconf supports just one mount point in URI." );
468 if( mountService == null ) {
469 throw new ResponseException( Status.SERVICE_UNAVAILABLE,
470 "MountService was not found. Finding behind mount points does not work." );
473 final InstanceIdentifier partialPath = builder.toInstance();
474 final MountInstance mount = mountService.getMountPoint( partialPath );
475 if( mount == null ) {
476 LOG.debug( "Instance identifier to missing mount point: {}", partialPath );
477 throw new ResponseException( Status.BAD_REQUEST,
478 "Mount point does not exist." );
481 final SchemaContext mountPointSchema = mount.getSchemaContext();
482 if( mountPointSchema == null ) {
483 throw new ResponseException( Status.BAD_REQUEST,
484 "Mount point does not contain any schema with modules." );
487 if( returnJustMountPoint ) {
488 InstanceIdentifier instance = InstanceIdentifier.builder().toInstance();
489 return new InstanceIdWithSchemaNode( instance, mountPointSchema, mount );
492 if( strings.size() == 1 ) {
493 InstanceIdentifier instance = InstanceIdentifier.builder().toInstance();
494 return new InstanceIdWithSchemaNode( instance, mountPointSchema, mount );
497 final String moduleNameBehindMountPoint = toModuleName( strings.get( 1 ) );
498 if( moduleNameBehindMountPoint == null ) {
499 throw new ResponseException( Status.BAD_REQUEST,
500 "First node after mount point in URI has to be in format \"moduleName:nodeName\"" );
503 final Module moduleBehindMountPoint = this.getLatestModule( mountPointSchema,
504 moduleNameBehindMountPoint );
505 if( moduleBehindMountPoint == null ) {
506 throw new ResponseException( Status.BAD_REQUEST,
507 "URI has bad format. \"" + moduleName +
508 "\" module does not exist in mount point." );
511 List<String> subList = strings.subList( 1, strings.size() );
512 return this.collectPathArguments( InstanceIdentifier.builder(), subList, moduleBehindMountPoint,
513 mount, returnJustMountPoint );
516 Module module = null;
517 if( mountPoint == null ) {
518 module = this.getLatestModule( globalSchema, moduleName );
519 if( module == null ) {
520 throw new ResponseException( Status.BAD_REQUEST,
521 "URI has bad format. \"" + moduleName + "\" module does not exist." );
525 SchemaContext schemaContext = mountPoint.getSchemaContext();
526 module = schemaContext == null ? null :
527 this.getLatestModule( schemaContext, moduleName );
528 if( module == null ) {
529 throw new ResponseException( Status.BAD_REQUEST,
530 "URI has bad format. \"" + moduleName +
531 "\" module does not exist in mount point." );
535 targetNode = this.findInstanceDataChildByNameAndNamespace(
536 parentNode, nodeName, module.getNamespace() );;
537 if( targetNode == null ) {
538 throw new ResponseException( Status.BAD_REQUEST,
539 "URI has bad format. Possible reasons:\n" +
540 "1. \"" + head + "\" was not found in parent data node.\n" +
541 "2. \"" + head + "\" is behind mount point. Then it should be in format \"/" +
542 MOUNT + "/" + head + "\"." );
545 final List<DataSchemaNode> potentialSchemaNodes =
546 this.findInstanceDataChildrenByName( parentNode, nodeName );
547 if( potentialSchemaNodes.size() > 1 ) {
548 final StringBuilder strBuilder = new StringBuilder();
549 for( final DataSchemaNode potentialNodeSchema : potentialSchemaNodes ) {
550 strBuilder.append( " " )
551 .append( potentialNodeSchema.getQName().getNamespace() )
555 throw new ResponseException( Status.BAD_REQUEST,
556 "URI has bad format. Node \"" + nodeName +
557 "\" is added as augment from more than one module. " +
558 "Therefore the node must have module name and it has to be in format \"moduleName:nodeName\"." +
559 "\nThe node is added as augment from modules with namespaces:\n" +
560 strBuilder.toString() );
563 if( potentialSchemaNodes.isEmpty() ) {
564 throw new ResponseException( Status.BAD_REQUEST, "URI has bad format. \"" + nodeName +
565 "\" was not found in parent data node.\n" );
568 targetNode = potentialSchemaNodes.iterator().next();
571 if( !this.isListOrContainer( targetNode ) ) {
572 throw new ResponseException( Status.BAD_REQUEST,
573 "URI has bad format. Node \"" + head +
574 "\" must be Container or List yang type." );
578 if( (targetNode instanceof ListSchemaNode) ) {
579 final ListSchemaNode listNode = ((ListSchemaNode) targetNode);
580 final int keysSize = listNode.getKeyDefinition().size();
581 if( (strings.size() - consumed) < keysSize ) {
582 throw new ResponseException( Status.BAD_REQUEST, "Missing key for list \"" +
583 listNode.getQName().getLocalName() + "\"." );
586 final List<String> uriKeyValues = strings.subList( consumed, consumed + keysSize );
587 final HashMap<QName, Object> keyValues = new HashMap<QName, Object>();
589 for( final QName key : listNode.getKeyDefinition() ) {
591 final String uriKeyValue = uriKeyValues.get( i );
592 if( uriKeyValue.equals( NULL_VALUE ) ) {
593 throw new ResponseException( Status.BAD_REQUEST,
594 "URI has bad format. List \"" + listNode.getQName().getLocalName() +
595 "\" cannot contain \"null\" value as a key." );
598 this.addKeyValue( keyValues, listNode.getDataChildByName( key ),
599 uriKeyValue, mountPoint );
604 consumed = consumed + i;
605 builder.nodeWithKey( targetNode.getQName(), keyValues );
608 builder.node( targetNode.getQName() );
611 if( (targetNode instanceof DataNodeContainer) ) {
612 final List<String> remaining = strings.subList( consumed, strings.size() );
613 return this.collectPathArguments( builder, remaining,
614 ((DataNodeContainer) targetNode), mountPoint, returnJustMountPoint );
617 return new InstanceIdWithSchemaNode( builder.toInstance(), targetNode, mountPoint );
620 public DataSchemaNode findInstanceDataChildByNameAndNamespace( final DataNodeContainer container,
621 final String name, final URI namespace ) {
622 Preconditions.<URI> checkNotNull( namespace );
624 final List<DataSchemaNode> potentialSchemaNodes = this.findInstanceDataChildrenByName( container, name );
626 Predicate<DataSchemaNode> filter = new Predicate<DataSchemaNode>() {
628 public boolean apply( DataSchemaNode node ) {
629 return Objects.equal( node.getQName().getNamespace(), namespace );
633 Iterable<DataSchemaNode> result = Iterables.filter( potentialSchemaNodes, filter );
634 return Iterables.getFirst( result, null );
637 public List<DataSchemaNode> findInstanceDataChildrenByName( final DataNodeContainer container,
638 final String name ) {
639 Preconditions.<DataNodeContainer> checkNotNull( container );
640 Preconditions.<String> checkNotNull( name );
642 List<DataSchemaNode> instantiatedDataNodeContainers = new ArrayList<DataSchemaNode>();
643 this.collectInstanceDataNodeContainers( instantiatedDataNodeContainers, container, name );
644 return instantiatedDataNodeContainers;
647 private void collectInstanceDataNodeContainers( final List<DataSchemaNode> potentialSchemaNodes,
648 final DataNodeContainer container, final String name ) {
650 Set<DataSchemaNode> childNodes = container.getChildNodes();
652 Predicate<DataSchemaNode> filter = new Predicate<DataSchemaNode>() {
654 public boolean apply( DataSchemaNode node ) {
655 return Objects.equal( node.getQName().getLocalName(), name );
659 Iterable<DataSchemaNode> nodes = Iterables.filter( childNodes, filter );
661 // Can't combine this loop with the filter above because the filter is lazily-applied
662 // by Iterables.filter.
663 for( final DataSchemaNode potentialNode : nodes ) {
664 if( this.isInstantiatedDataSchema( potentialNode ) ) {
665 potentialSchemaNodes.add( potentialNode );
669 Iterable<ChoiceNode> choiceNodes = Iterables.<ChoiceNode> filter( container.getChildNodes(),
672 final Function<ChoiceNode, Set<ChoiceCaseNode>> choiceFunction =
673 new Function<ChoiceNode, Set<ChoiceCaseNode>>() {
675 public Set<ChoiceCaseNode> apply( final ChoiceNode node ) {
676 return node.getCases();
680 Iterable<Set<ChoiceCaseNode>> map = Iterables.<ChoiceNode, Set<ChoiceCaseNode>> transform(
681 choiceNodes, choiceFunction );
683 final Iterable<ChoiceCaseNode> allCases = Iterables.<ChoiceCaseNode> concat( map );
684 for( final ChoiceCaseNode caze : allCases ) {
685 this.collectInstanceDataNodeContainers( potentialSchemaNodes, caze, name );
689 public boolean isInstantiatedDataSchema( final DataSchemaNode node ) {
690 return node instanceof LeafSchemaNode || node instanceof LeafListSchemaNode ||
691 node instanceof ContainerSchemaNode || node instanceof ListSchemaNode;
694 private void addKeyValue( final HashMap<QName, Object> map, final DataSchemaNode node,
695 final String uriValue, final MountInstance mountPoint ) {
696 Preconditions.<String> checkNotNull( uriValue );
697 Preconditions.checkArgument( (node instanceof LeafSchemaNode) );
699 final String urlDecoded = urlPathArgDecode( uriValue );
700 final TypeDefinition<? extends Object> typedef = ((LeafSchemaNode) node).getType();
701 Codec<Object, Object> codec = RestCodec.from( typedef, mountPoint );
703 Object decoded = codec == null ? null : codec.deserialize( urlDecoded );
704 String additionalInfo = "";
705 if( decoded == null ) {
706 TypeDefinition<? extends Object> baseType = RestUtil.resolveBaseTypeFrom( typedef );
707 if( (baseType instanceof IdentityrefTypeDefinition) ) {
708 decoded = this.toQName( urlDecoded );
709 additionalInfo = "For key which is of type identityref it should be in format module_name:identity_name.";
713 if( decoded == null ) {
714 throw new ResponseException( Status.BAD_REQUEST, uriValue + " from URI can\'t be resolved. " +
718 map.put( node.getQName(), decoded );
721 private static String toModuleName( final String str ) {
722 Preconditions.<String> checkNotNull( str );
723 if( str.contains( ":" ) ) {
724 final String[] args = str.split( ":" );
725 if( args.length == 2 ) {
732 private String toNodeName( final String str ) {
733 if( str.contains( ":" ) ) {
734 final String[] args = str.split( ":" );
735 if( args.length == 2 ) {
742 private QName toQName( final String name ) {
743 final String module = toModuleName( name );
744 final String node = this.toNodeName( name );
745 Set<Module> modules = globalSchema.getModules();
747 final Comparator<Module> comparator = new Comparator<Module>() {
749 public int compare( final Module o1, final Module o2 ) {
750 return o1.getRevision().compareTo( o2.getRevision() );
754 List<Module> sorted = new ArrayList<Module>( modules );
755 Collections.<Module> sort( new ArrayList<Module>( modules ), comparator );
757 final Function<Module, QName> transform = new Function<Module, QName>() {
759 public QName apply( final Module m ) {
760 return QName.create( m.getNamespace(), m.getRevision(), m.getName() );
764 final Predicate<QName> findFirst = new Predicate<QName>() {
766 public boolean apply( final QName qn ) {
767 return Objects.equal( module, qn.getLocalName() );
771 Optional<QName> namespace = FluentIterable.from( sorted )
772 .transform( transform )
773 .firstMatch( findFirst );
774 return namespace.isPresent() ? QName.create( namespace.get(), node ) : null;
777 private boolean isListOrContainer( final DataSchemaNode node ) {
778 return node instanceof ListSchemaNode || node instanceof ContainerSchemaNode;
781 public RpcDefinition getRpcDefinition( final String name ) {
782 final QName validName = this.toQName( name );
783 return validName == null ? null : this.qnameToRpc.get( validName );
787 public void onGlobalContextUpdated( final SchemaContext context ) {
788 if( context != null ) {
789 this.qnameToRpc.clear();
790 this.setGlobalSchema( context );
791 Set<RpcDefinition> _operations = context.getOperations();
792 for( final RpcDefinition operation : _operations ) {
794 this.qnameToRpc.put( operation.getQName(), operation );
800 public List<String> urlPathArgsDecode( final List<String> strings ) {
802 List<String> decodedPathArgs = new ArrayList<String>();
803 for( final String pathArg : strings ) {
804 String _decode = URLDecoder.decode( pathArg, URI_ENCODING_CHAR_SET );
805 decodedPathArgs.add( _decode );
807 return decodedPathArgs;
809 catch( UnsupportedEncodingException e ) {
810 throw new ResponseException( Status.BAD_REQUEST,
811 "Invalid URL path '" + strings + "': " + e.getMessage() );
815 public String urlPathArgDecode( final String pathArg ) {
816 if( pathArg != null ) {
818 return URLDecoder.decode( pathArg, URI_ENCODING_CHAR_SET );
820 catch( UnsupportedEncodingException e ) {
821 throw new ResponseException( Status.BAD_REQUEST,
822 "Invalid URL path arg '" + pathArg + "': " + e.getMessage() );
829 private CharSequence convertToRestconfIdentifier( final PathArgument argument,
830 final DataNodeContainer node ) {
831 if( argument instanceof NodeIdentifier && node instanceof ContainerSchemaNode ) {
832 return convertToRestconfIdentifier( (NodeIdentifier) argument, (ContainerSchemaNode) node );
834 else if( argument instanceof NodeIdentifierWithPredicates && node instanceof ListSchemaNode ) {
835 return convertToRestconfIdentifier( (NodeIdentifierWithPredicates) argument, (ListSchemaNode) node );
837 else if( argument != null && node != null ) {
838 throw new IllegalArgumentException(
839 "Conversion of generic path argument is not supported" );
842 throw new IllegalArgumentException( "Unhandled parameter types: "
843 + Arrays.<Object> asList( argument, node ).toString() );
847 private CharSequence convertToRestconfIdentifier( final NodeIdentifier argument,
848 final ContainerSchemaNode node ) {
849 StringBuilder builder = new StringBuilder();
850 builder.append( "/" );
851 QName nodeType = argument.getNodeType();
852 builder.append( this.toRestconfIdentifier( nodeType ) );
853 return builder.toString();
856 private CharSequence convertToRestconfIdentifier( final NodeIdentifierWithPredicates argument,
857 final ListSchemaNode node ) {
858 QName nodeType = argument.getNodeType();
859 final CharSequence nodeIdentifier = this.toRestconfIdentifier( nodeType );
860 final Map<QName, Object> keyValues = argument.getKeyValues();
862 StringBuilder builder = new StringBuilder();
863 builder.append( "/" );
864 builder.append( nodeIdentifier );
865 builder.append( "/" );
867 List<QName> keyDefinition = node.getKeyDefinition();
868 boolean hasElements = false;
869 for( final QName key : keyDefinition ) {
874 builder.append( "/" );
878 builder.append( this.toUriString( keyValues.get( key ) ) );
879 } catch( UnsupportedEncodingException e ) {
880 LOG.error( "Error parsing URI: " + keyValues.get( key ), e );
885 return builder.toString();
888 private static DataSchemaNode childByQName( final Object container, final QName name ) {
889 if( container instanceof ChoiceCaseNode ) {
890 return childByQName( (ChoiceCaseNode) container, name );
892 else if( container instanceof ChoiceNode ) {
893 return childByQName( (ChoiceNode) container, name );
895 else if( container instanceof ContainerSchemaNode ) {
896 return childByQName( (ContainerSchemaNode) container, name );
898 else if( container instanceof ListSchemaNode ) {
899 return childByQName( (ListSchemaNode) container, name );
901 else if( container instanceof DataSchemaNode ) {
902 return childByQName( (DataSchemaNode) container, name );
904 else if( container instanceof Module ) {
905 return childByQName( (Module) container, name );
908 throw new IllegalArgumentException( "Unhandled parameter types: "
909 + Arrays.<Object> asList( container, name ).toString() );