From: Ed Warnicke Date: Mon, 23 Jun 2014 11:23:06 +0000 (+0000) Subject: Merge "Implement basic ShardTransactionChain#CloseTransactionChain" X-Git-Tag: release/helium~628 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=ebd9aba9e844229de613a2b60b5d21119fcee968;hp=397fd5485d0041b98623732d7210cba2ecbdd808 Merge "Implement basic ShardTransactionChain#CloseTransactionChain" --- diff --git a/opendaylight/commons/opendaylight/pom.xml b/opendaylight/commons/opendaylight/pom.xml index 73ae6de241..990276468b 100644 --- a/opendaylight/commons/opendaylight/pom.xml +++ b/opendaylight/commons/opendaylight/pom.xml @@ -36,12 +36,19 @@ 0.4.2-SNAPSHOT 0.4.2-SNAPSHOT 0.4.2-SNAPSHOT - 0.0.3-SNAPSHOT + 7.0.32.v201211201336 + 7.0.32.v201211201952 + 7.0.32.v201211201952 + 0.0.3-SNAPSHOT 1.7 + 7.0.32.v201211201952 + 7.0.32.v201211081135 1.2.2 0.1.2-SNAPSHOT 2.4 + 7.0.32.v201211201952 + 7.0.32.v201211081135 3.1 0.0.2-SNAPSHOT 3.0.1 @@ -49,6 +56,8 @@ 0.5.2-SNAPSHOT 1.4.2-SNAPSHOT 1.0.2-SNAPSHOT + 7.0.32.v201211081135 + 7.0.32.v201211201952 2.3.2 0.5.2-SNAPSHOT 0.2.5-SNAPSHOT @@ -556,47 +565,47 @@ orbit org.apache.catalina - 7.0.32.v201211201336 + ${commons.catalina} orbit org.apache.catalina.ha - 7.0.32.v201211201952 + ${commons.catalina.ha} orbit org.apache.catalina.tribes - 7.0.32.v201211201952 + ${commons.catalina.tribes} orbit org.apache.coyote - 7.0.32.v201211201952 + ${commons.coyote} orbit org.apache.el - 7.0.32.v201211081135 + ${commons.el} orbit org.apache.jasper - 7.0.32.v201211201952 + ${commons.jasper} orbit org.apache.juli.extras - 7.0.32.v201211081135 + ${commons.juli.version} orbit org.apache.tomcat.api - 7.0.32.v201211081135 + ${commons.tomcat.api} orbit org.apache.tomcat.util - 7.0.32.v201211201952 + ${commons.tomcat.util} org.aopalliance diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/JavaAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/JavaAttribute.java index 5b0196cdda..fac4d57432 100644 --- a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/JavaAttribute.java +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/JavaAttribute.java @@ -185,7 +185,9 @@ public class JavaAttribute extends AbstractAttribute implements TypedAttribute { OpenType innerCompositeType; if(isDerivedType(innerTypeBaseType, innerType)) { - innerCompositeType = getCompositeType(innerTypeBaseType, baseInnerTypeDefinition); + innerCompositeType = baseInnerTypeDefinition instanceof UnionTypeDefinition ? + getCompositeTypeForUnion(baseInnerTypeDefinition) : + getCompositeType(innerTypeBaseType, baseInnerTypeDefinition); } else { innerCompositeType = SimpleTypeResolver.getSimpleType(innerType); } diff --git a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java index ebc56928a2..d76c094851 100644 --- a/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java +++ b/opendaylight/connectionmanager/implementation/src/main/java/org/opendaylight/controller/connectionmanager/internal/ConnectionManager.java @@ -261,7 +261,7 @@ public class ConnectionManager implements IConnectionManager, Map params) { if (connectionService == null) return null; - Node node = connectionService.connect(connectionIdentifier, params); + Node node = connectionService.connect(type, connectionIdentifier, params); AbstractScheme scheme = schemes.get(activeScheme); if (scheme != null && node != null) scheme.addNode(node); diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index 3916e05496..7160acba81 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -1320,5 +1320,18 @@ + + docs + + false + + + + org.opendaylight.controller + swagger-ui + 0.0.1-SNAPSHOT + + + diff --git a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml index d872bfd47b..8b07ce3a33 100644 --- a/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml +++ b/opendaylight/distribution/opendaylight/src/main/resources/configuration/initial/01-md-sal.xml @@ -68,6 +68,20 @@ runtime-mapping-singleton + + prefix:binding-forwarded-data-broker + binding-async-data-broker + + + dom:dom-broker-osgi-registry + dom-broker + + + binding:binding-dom-mapping-service + runtime-mapping-singleton + + + @@ -122,6 +136,14 @@ + + binding:binding-async-data-broker + + binding-data-broker + /modules/module[type='binding-forwarded-data-broker'][name='binding-async-data-broker'] + + + dom:dom-async-data-broker diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/FlowProgrammerAdapter.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/FlowProgrammerAdapter.java index e5a9d3e5db..ba7377e8ff 100644 --- a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/FlowProgrammerAdapter.java +++ b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/FlowProgrammerAdapter.java @@ -163,6 +163,7 @@ public class FlowProgrammerAdapter implements IPluginInFlowProgrammerService, Sa @Override public void onFlowRemoved(final FlowRemoved notification) { + // notified upon remove flow rpc successfully invoked if (notification == null) { return; } @@ -190,7 +191,25 @@ public class FlowProgrammerAdapter implements IPluginInFlowProgrammerService, Sa @Override public void onSwitchFlowRemoved(final SwitchFlowRemoved notification) { - // FIXME: unfinished? + // notified upon remove flow message from device arrives + if (notification == null) { + return; + } + + final NodeRef node = notification.getNode(); + if (node == null) { + LOG.debug("Notification {} has not node, ignoring it", notification); + return; + } + + Node adNode; + try { + adNode = NodeMapping.toADNode(notification.getNode()); + } catch (ConstructionException e) { + LOG.warn("Failed to construct AD node for {}, ignoring notification", node, e); + return; + } + flowProgrammerPublisher.flowRemoved(adNode, ToSalConversionsUtils.toFlow(notification, adNode)); } @Override diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/NodeMapping.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/NodeMapping.java index ba86ad99fb..f5701bfaf2 100644 --- a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/NodeMapping.java +++ b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/NodeMapping.java @@ -124,6 +124,14 @@ public final class NodeMapping { return nodeConnectorId.getValue(); } + public static NodeId toAdNodeId(final NodeConnectorId nodeConnectorId) { + NodeId nodeId = null; + if (nodeConnectorId != null) { + nodeId = new NodeId(nodeConnectorId.getValue().replaceFirst(":[0-9]+$", "")); + } + return nodeId; + } + public static NodeConnectorId toControllerNodeConnectorId(final NodeId node) { return new NodeConnectorId(node.getValue() + ":" + 4294967293L); } diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/ToSalConversionsUtils.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/ToSalConversionsUtils.java index da3477ee45..97a25bf71c 100644 --- a/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/ToSalConversionsUtils.java +++ b/opendaylight/md-sal/compatibility/sal-compatibility/src/main/java/org/opendaylight/controller/sal/compatibility/ToSalConversionsUtils.java @@ -55,6 +55,7 @@ import org.opendaylight.controller.sal.core.Node; import org.opendaylight.controller.sal.core.NodeConnector; import org.opendaylight.controller.sal.flowprogrammer.Flow; import org.opendaylight.controller.sal.match.Match; +import org.opendaylight.controller.sal.match.MatchType; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Dscp; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix; @@ -94,8 +95,11 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.acti import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.Address; import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.address.Ipv4; import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.address.address.Ipv6; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SwitchFlowRemoved; +import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.GenericFlowAttributes; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCase; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.EtherType; import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanPcp; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.MacAddressFilter; @@ -114,18 +118,53 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.TcpMatch; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.layer._4.match.UdpMatch; import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.vlan.match.fields.VlanId; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.google.common.net.InetAddresses; public class ToSalConversionsUtils { + private static final Logger LOG = LoggerFactory.getLogger(ToSalConversionsUtils.class); + private ToSalConversionsUtils() { } public static Flow toFlow(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow source, Node node) { final Flow target = new Flow(); + genericFlowToAdFlow(source, target); + + target.setMatch(toMatch(source.getMatch())); + + List actions = getAction(source); + if (actions != null) { + target.setActions(actionFrom(actions, node)); + } + + return target; + } + + /** + * @param source notification, missing instructions + * @param node corresponding node where the flow change occured + * @return ad-sal node, build from given data + */ + public static Flow toFlow(SwitchFlowRemoved source, Node node) { + final Flow target = new Flow(); + genericFlowToAdFlow(source, target); + + target.setMatch(toMatch(source.getMatch())); + + return target; + } + /** + * @param source + * @param target + */ + private static void genericFlowToAdFlow(GenericFlowAttributes source, + final Flow target) { Integer hardTimeout = source.getHardTimeout(); if (hardTimeout != null) { target.setHardTimeout(hardTimeout.shortValue()); @@ -140,16 +179,7 @@ public class ToSalConversionsUtils { if (priority != null) { target.setPriority(priority.shortValue()); } - - target.setMatch(toMatch(source.getMatch())); - - List actions = getAction(source); - if (actions != null) { - target.setActions(actionFrom(actions, node)); - } - target.setId(source.getCookie().getValue().longValue()); - return target; } public static List getAction( @@ -356,7 +386,7 @@ public class ToSalConversionsUtils { return nodeConnector; } - public static Match toMatch(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match source) { + public static Match toMatch(org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match source) { Match target = new Match(); if (source != null) { fillFrom(target, source.getVlanMatch()); @@ -364,11 +394,30 @@ public class ToSalConversionsUtils { fillFrom(target, source.getLayer3Match()); fillFrom(target, source.getLayer4Match()); fillFrom(target, source.getIpMatch()); + fillFrom(target, source.getInPort()); } return target; } + /** + * @param target + * @param inPort + */ + private static void fillFrom(Match target, NodeConnectorId inPort) { + if (inPort != null) { + String inPortValue = inPort.getValue(); + if (inPortValue != null) { + try { + target.setField(MatchType.IN_PORT, NodeMapping.toADNodeConnector(inPort, + NodeMapping.toAdNodeId(inPort))); + } catch (ConstructionException e) { + LOG.warn("nodeConnector construction failed", e); + } + } + } + } + private static void fillFrom(Match target, VlanMatch vlanMatch) { if (vlanMatch != null) { VlanId vlanId = vlanMatch.getVlanId(); diff --git a/opendaylight/md-sal/compatibility/sal-compatibility/src/test/java/org/opendaylight/controller/sal/compatibility/test/NodeMappingTest.java b/opendaylight/md-sal/compatibility/sal-compatibility/src/test/java/org/opendaylight/controller/sal/compatibility/test/NodeMappingTest.java index 041924af33..b418e6dc25 100644 --- a/opendaylight/md-sal/compatibility/sal-compatibility/src/test/java/org/opendaylight/controller/sal/compatibility/test/NodeMappingTest.java +++ b/opendaylight/md-sal/compatibility/sal-compatibility/src/test/java/org/opendaylight/controller/sal/compatibility/test/NodeMappingTest.java @@ -11,6 +11,7 @@ import org.junit.Assert; import org.junit.Test; import org.opendaylight.controller.sal.compatibility.NodeMapping; import org.opendaylight.controller.sal.core.MacAddress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; /** @@ -55,4 +56,19 @@ public class NodeMappingTest { } } + /** + * Test method for + * {@link org.opendaylight.controller.sal.compatibility.NodeMapping#toAdNodeId(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId)} + * . + */ + @Test + public void testToAdNodeId() { + NodeId observed; + observed = NodeMapping.toAdNodeId(null); + Assert.assertNull(observed); + + observed = NodeMapping.toAdNodeId(new NodeConnectorId("MD_SAL|openflow:5:2")); + Assert.assertEquals("MD_SAL|openflow:5", observed.getValue()); + } + } diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataBroker.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataBroker.java index c6a9efe21c..7eee5c8b62 100644 --- a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataBroker.java +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingDataBroker.java @@ -13,7 +13,7 @@ import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -public interface BindingDataBroker extends AsyncDataBroker, DataObject, BindingDataChangeListener>{ +public interface BindingDataBroker extends AsyncDataBroker, DataObject, BindingDataChangeListener>, BindingService { @Override BindingDataReadTransaction newReadOnlyTransaction(); diff --git a/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingService.java b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingService.java new file mode 100644 index 0000000000..ccce73c7d7 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-api/src/main/java/org/opendaylight/controller/md/sal/binding/api/BindingService.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.md.sal.binding.api; + +import org.opendaylight.controller.sal.binding.api.BindingAwareService; + +/** + * + * Marker interface for MD-SAL services which are available for users of MD-SAL. + * + * BindingService is marker interface for infrastructure services provided by + * the SAL. These services may be session-specific, and wrapped by custom + * delegator patterns in order to introduce additional semantics / checks + * to the system. + * + * This interface extends {@link BindingAwareService}, order to be make + * new services available via + * {@link org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext} + * and via + * {@link org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ConsumerContext} + * + */ +public interface BindingService extends BindingAwareService { + +} diff --git a/opendaylight/md-sal/sal-binding-broker/pom.xml b/opendaylight/md-sal/sal-binding-broker/pom.xml index 2470ba5133..7fc467670d 100644 --- a/opendaylight/md-sal/sal-binding-broker/pom.xml +++ b/opendaylight/md-sal/sal-binding-broker/pom.xml @@ -142,7 +142,7 @@ org.opendaylight.controller.md.sal.binding.impl, org.opendaylight.controller.sal.binding.osgi.*, - org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.binding.impl.rev131028 + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.binding.impl.rev131028.* diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModule.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModule.java new file mode 100644 index 0000000000..17cd67a857 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModule.java @@ -0,0 +1,75 @@ +package org.opendaylight.controller.config.yang.md.sal.binding.impl; + +import java.util.Collection; +import java.util.Collections; + +import org.opendaylight.controller.md.sal.binding.impl.ForwardedBindingDataBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; +import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; +import org.opendaylight.controller.sal.core.api.Provider; +import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; +import org.osgi.framework.BundleContext; + +public class BindingAsyncDataBrokerImplModule extends + org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractBindingAsyncDataBrokerImplModule implements + Provider { + private BundleContext bundleContext; + + public BindingAsyncDataBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, + final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + super(identifier, dependencyResolver); + } + + public BindingAsyncDataBrokerImplModule( + final org.opendaylight.controller.config.api.ModuleIdentifier identifier, + final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, + final org.opendaylight.controller.config.yang.md.sal.binding.impl.BindingAsyncDataBrokerImplModule oldModule, + final java.lang.AutoCloseable oldInstance) { + super(identifier, dependencyResolver, oldModule, oldInstance); + } + + @Override + public void customValidation() { + // add custom validation form module attributes here. + } + + @Override + public java.lang.AutoCloseable createInstance() { + Broker domBroker = getDomAsyncBrokerDependency(); + BindingIndependentMappingService mappingService = getBindingMappingServiceDependency(); + + // FIXME: Switch this to DOM Broker registration which would not require + // BundleContext when API are updated. + ProviderSession session = domBroker.registerProvider(this, getBundleContext()); + DOMDataBroker domDataBroker = session.getService(DOMDataBroker.class); + SchemaService schemaService = session.getService(SchemaService.class); + return new ForwardedBindingDataBroker(domDataBroker, mappingService, schemaService); + } + + // FIXME: Remove this when DOM Broker registration would not require + // BundleContext + @Deprecated + private BundleContext getBundleContext() { + return bundleContext; + } + + // FIXME: Remove this when DOM Broker registration would not require + // BundleContext + @Deprecated + void setBundleContext(final BundleContext bundleContext) { + this.bundleContext = bundleContext; + } + + @Override + public Collection getProviderFunctionality() { + return Collections.emptySet(); + } + + @Override + public void onSessionInitiated(final ProviderSession arg0) { + // intentional NOOP + } + +} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModuleFactory.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModuleFactory.java new file mode 100644 index 0000000000..763e6ad3ca --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingAsyncDataBrokerImplModuleFactory.java @@ -0,0 +1,37 @@ +/* +* Generated file +* +* Generated from: yang module name: opendaylight-sal-binding-broker-impl yang module local name: binding-forwarded-data-broker +* Generated by: org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator +* Generated at: Fri May 16 17:18:18 CEST 2014 +* +* Do not modify this file unless it is present under src/main directory +*/ +package org.opendaylight.controller.config.yang.md.sal.binding.impl; + +import org.opendaylight.controller.config.api.DependencyResolver; +import org.osgi.framework.BundleContext; + +public class BindingAsyncDataBrokerImplModuleFactory extends org.opendaylight.controller.config.yang.md.sal.binding.impl.AbstractBindingAsyncDataBrokerImplModuleFactory { + + + + + @Override + public BindingAsyncDataBrokerImplModule instantiateModule(final String instanceName, + final DependencyResolver dependencyResolver, final BindingAsyncDataBrokerImplModule oldModule, + final AutoCloseable oldInstance, final BundleContext bundleContext) { + BindingAsyncDataBrokerImplModule module = super.instantiateModule(instanceName, dependencyResolver, oldModule, oldInstance, bundleContext); + module.setBundleContext(bundleContext); + return module; + } + + @Override + public BindingAsyncDataBrokerImplModule instantiateModule(final String instanceName, + final DependencyResolver dependencyResolver, final BundleContext bundleContext) { + // TODO Auto-generated method stub + BindingAsyncDataBrokerImplModule module = super.instantiateModule(instanceName, dependencyResolver, bundleContext); + module.setBundleContext(bundleContext); + return module; + } +} diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingBrokerImplModule.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingBrokerImplModule.java index 44a508c0a0..188272fb60 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingBrokerImplModule.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/BindingBrokerImplModule.java @@ -31,14 +31,14 @@ public final class BindingBrokerImplModule extends private BundleContext bundleContext; - public BindingBrokerImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, - org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { + public BindingBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, + final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver) { super(identifier, dependencyResolver); } - public BindingBrokerImplModule(org.opendaylight.controller.config.api.ModuleIdentifier identifier, - org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, - BindingBrokerImplModule oldModule, java.lang.AutoCloseable oldInstance) { + public BindingBrokerImplModule(final org.opendaylight.controller.config.api.ModuleIdentifier identifier, + final org.opendaylight.controller.config.api.DependencyResolver dependencyResolver, + final BindingBrokerImplModule oldModule, final java.lang.AutoCloseable oldInstance) { super(identifier, dependencyResolver, oldModule, oldInstance); } @@ -63,23 +63,24 @@ public final class BindingBrokerImplModule extends private RootBindingAwareBroker createStandaloneBroker() { RootBindingAwareBroker broker = new RootBindingAwareBroker(getIdentifier().getInstanceName()); - broker.setDataBroker(getDataBrokerDependency()); + broker.setLegacyDataBroker(getDataBrokerDependency()); broker.setNotificationBroker(getNotificationServiceDependency()); broker.setRpcBroker(new RpcProviderRegistryImpl(broker.getIdentifier())); + // FIXME: Also set Async Data Broker return broker; } private RootBindingAwareBroker createForwardedBroker() { DomForwardedBindingBrokerImpl broker = new DomForwardedBindingBrokerImpl(getIdentifier().getInstanceName()); - broker.setDataBroker(getDataBrokerDependency()); + broker.setLegacyDataBroker(getDataBrokerDependency()); broker.setNotificationBroker(getNotificationServiceDependency()); broker.setRpcBroker(new RpcProviderRegistryImpl(broker.getIdentifier())); broker.getMountManager().setDataCommitExecutor(SingletonHolder.getDefaultCommitExecutor()); broker.getMountManager().setNotificationExecutor(SingletonHolder.getDefaultNotificationExecutor()); - + // FIXME: Also set Async Data Broker DomForwardingUtils.reuseForwardingFrom(broker, broker.getDataBroker()); broker.startForwarding(); return broker; @@ -89,7 +90,7 @@ public final class BindingBrokerImplModule extends return bundleContext; } - public void setBundleContext(BundleContext bundleContext) { + public void setBundleContext(final BundleContext bundleContext) { this.bundleContext = bundleContext; } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModule.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModule.java index 647ca85671..7467e544fd 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModule.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/config/yang/md/sal/binding/impl/ForwardedCompatibleDataBrokerImplModule.java @@ -59,10 +59,9 @@ public final class ForwardedCompatibleDataBrokerImplModule extends Broker domBroker = getDomAsyncBrokerDependency(); ProviderSession session = domBroker.registerProvider(this, getBundleContext()); DOMDataBroker domDataBroker = session.getService(DOMDataBroker.class); + SchemaService schemaService = session.getService(SchemaService.class); ForwardedBackwardsCompatibleDataBroker dataBroker = new ForwardedBackwardsCompatibleDataBroker(domDataBroker, - mappingService, listeningExecutor); - - session.getService(SchemaService.class).registerSchemaServiceListener(dataBroker); + mappingService, schemaService,listeningExecutor); dataBroker.setConnector(BindingDomConnectorDeployer.createConnector(getBindingMappingServiceDependency())); dataBroker.setDomProviderContext(session); diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java index 2a866a94b3..b09cd1c80a 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/AbstractForwardedDataBroker.java @@ -23,6 +23,7 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener; import org.opendaylight.controller.sal.binding.impl.connect.dom.BindingIndependentConnector; import org.opendaylight.controller.sal.binding.impl.forward.DomForwardedBroker; import org.opendaylight.controller.sal.core.api.Broker.ProviderSession; +import org.opendaylight.controller.sal.core.api.model.SchemaService; import org.opendaylight.yangtools.concepts.AbstractListenerRegistration; import org.opendaylight.yangtools.concepts.Delegator; import org.opendaylight.yangtools.concepts.ListenerRegistration; @@ -33,6 +34,7 @@ import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMapping import org.opendaylight.yangtools.yang.data.impl.codec.DeserializationException; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; +import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,7 +42,7 @@ import com.google.common.base.Objects; import com.google.common.base.Optional; public abstract class AbstractForwardedDataBroker implements Delegator, DomForwardedBroker, - SchemaContextListener { + SchemaContextListener, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(AbstractForwardedDataBroker.class); // The Broker to whom we do all forwarding @@ -53,12 +55,14 @@ public abstract class AbstractForwardedDataBroker implements Delegator schemaListenerRegistration; protected AbstractForwardedDataBroker(final DOMDataBroker domDataBroker, - final BindingIndependentMappingService mappingService) { + final BindingIndependentMappingService mappingService,final SchemaService schemaService) { this.domDataBroker = domDataBroker; this.mappingService = mappingService; this.codec = new BindingToNormalizedNodeCodec(mappingService); + this.schemaListenerRegistration = schemaService.registerSchemaServiceListener(this); } protected BindingToNormalizedNodeCodec getCodec() { @@ -286,4 +290,9 @@ public abstract class AbstractForwardedDataBroker implements Delegator, CommitHandlerRegistrationImpl> commitHandlers = new ConcurrentHashMap<>(); - private final ListenerRegistry fakeRegistry = ListenerRegistry.create(); private final ListeningExecutorService executorService; public ForwardedBackwardsCompatibleDataBroker(final DOMDataBroker domDataBroker, - final BindingIndependentMappingService mappingService, final ListeningExecutorService executor) { - super(domDataBroker, mappingService); + final BindingIndependentMappingService mappingService, final SchemaService schemaService,final ListeningExecutorService executor) { + super(domDataBroker, mappingService,schemaService); executorService = executor; LOG.info("ForwardedBackwardsCompatibleBroker started."); } @@ -129,12 +129,6 @@ public class ForwardedBackwardsCompatibleDataBroker extends AbstractForwardedDat throw new UnsupportedOperationException("Data reader contract is not supported."); } - @Override - public void close() throws Exception { - // TODO Auto-generated method stub - - } - public ListenableFuture> commit(final ForwardedBackwardsCompatibleTransacion tx) { final List, DataObject>> subTrans = new ArrayList<>(); diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBindingDataBroker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBindingDataBroker.java index fb06b130ce..5ab088e20e 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBindingDataBroker.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/md/sal/binding/impl/ForwardedBindingDataBroker.java @@ -18,6 +18,7 @@ import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker; import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction; import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction; import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction; +import org.opendaylight.controller.sal.core.api.model.SchemaService; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.common.RpcResult; @@ -41,8 +42,8 @@ import com.google.common.util.concurrent.ListenableFuture; */ public class ForwardedBindingDataBroker extends AbstractForwardedDataBroker implements BindingDataBroker { - public ForwardedBindingDataBroker(final DOMDataBroker domDataBroker, final BindingIndependentMappingService mappingService) { - super(domDataBroker, mappingService); + public ForwardedBindingDataBroker(final DOMDataBroker domDataBroker, final BindingIndependentMappingService mappingService, final SchemaService schemaService) { + super(domDataBroker, mappingService,schemaService); } @Override diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend index 3fef544f81..00c9f1eb91 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/codegen/impl/RuntimeCodeGenerator.xtend @@ -16,6 +16,7 @@ import org.opendaylight.yangtools.yang.binding.util.BindingReflections import org.opendaylight.yangtools.yang.binding.util.ClassLoaderUtils import static extension org.opendaylight.controller.sal.binding.codegen.RuntimeCodeSpecification.* +import org.opendaylight.yangtools.yang.binding.RpcService class RuntimeCodeGenerator extends AbstractRuntimeCodeGenerator { @@ -28,7 +29,7 @@ class RuntimeCodeGenerator extends AbstractRuntimeCodeGenerator { val proxyName = iface.directProxyName; val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(proxyName) if(potentialClass != null) { - return potentialClass.newInstance; + return potentialClass.newInstance as RpcService; } val supertype = iface.asCtClass val createdCls = createClass(iface.directProxyName, supertype) [ @@ -53,7 +54,7 @@ class RuntimeCodeGenerator extends AbstractRuntimeCodeGenerator { ''' ] ] - return createdCls.toClass(iface.classLoader).newInstance + return createdCls.toClass(iface.classLoader).newInstance as RpcService ] } @@ -63,7 +64,7 @@ class RuntimeCodeGenerator extends AbstractRuntimeCodeGenerator { val routerName = iface.routerName; val potentialClass = ClassLoaderUtils.tryToLoadClassWithTCCL(routerName) if(potentialClass != null) { - return potentialClass.newInstance; + return potentialClass.newInstance as RpcService; } val targetCls = createClass(iface.routerName, supertype) [ @@ -106,7 +107,7 @@ class RuntimeCodeGenerator extends AbstractRuntimeCodeGenerator { ''' ] ] - return targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance + return targetCls.toClass(iface.classLoader,iface.protectionDomain).newInstance as RpcService ]; } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RootBindingAwareBroker.java b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RootBindingAwareBroker.java index 75d44db9d1..8acad1b2d7 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RootBindingAwareBroker.java +++ b/opendaylight/md-sal/sal-binding-broker/src/main/java/org/opendaylight/controller/sal/binding/impl/RootBindingAwareBroker.java @@ -7,7 +7,9 @@ */ package org.opendaylight.controller.sal.binding.impl; -import com.google.common.collect.ImmutableClassToInstanceMap; +import static com.google.common.base.Preconditions.checkState; + +import org.opendaylight.controller.md.sal.binding.api.BindingDataBroker; import org.opendaylight.controller.md.sal.binding.util.AbstractBindingSalProviderInstance; import org.opendaylight.controller.md.sal.binding.util.BindingContextUtils; import org.opendaylight.controller.md.sal.common.api.routing.RouteChangeListener; @@ -32,13 +34,13 @@ import org.opendaylight.yangtools.yang.binding.RpcService; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import static com.google.common.base.Preconditions.checkState; + +import com.google.common.collect.ImmutableClassToInstanceMap; public class RootBindingAwareBroker implements // Mutable, // Identifiable, // - BindingAwareBroker, AutoCloseable, - RpcProviderRegistry { + BindingAwareBroker, AutoCloseable, RpcProviderRegistry { private final static Logger LOG = LoggerFactory.getLogger(RootBindingAwareBroker.class); @@ -50,7 +52,9 @@ public class RootBindingAwareBroker implements // private NotificationProviderService notificationBroker; - private DataProviderService dataBroker; + private DataProviderService legacyDataBroker; + + private BindingDataBroker dataBroker; private MountPointManagerImpl mountManager; @@ -58,7 +62,7 @@ public class RootBindingAwareBroker implements // return mountManager; } - public void setMountManager(MountPointManagerImpl mountManager) { + public void setMountManager(final MountPointManagerImpl mountManager) { this.mountManager = mountManager; } @@ -66,11 +70,12 @@ public class RootBindingAwareBroker implements // private ImmutableClassToInstanceMap supportedProviderServices; - public RootBindingAwareBroker(String instanceName) { + public RootBindingAwareBroker(final String instanceName) { this.identifier = instanceName; mountManager = new MountPointManagerImpl(); } + @Override public String getIdentifier() { return identifier; } @@ -80,7 +85,7 @@ public class RootBindingAwareBroker implements // } public DataProviderService getDataBroker() { - return this.dataBroker; + return this.legacyDataBroker; } public NotificationProviderService getNotificationBroker() { @@ -95,16 +100,16 @@ public class RootBindingAwareBroker implements // return rpcBroker; } - public void setRpcBroker(RpcProviderRegistry rpcBroker) { + public void setRpcBroker(final RpcProviderRegistry rpcBroker) { this.rpcBroker = rpcBroker; } - public void setNotificationBroker(NotificationProviderService notificationBroker) { + public void setNotificationBroker(final NotificationProviderService notificationBroker) { this.notificationBroker = notificationBroker; } - public void setDataBroker(DataProviderService dataBroker) { - this.dataBroker = dataBroker; + public void setLegacyDataBroker(final DataProviderService dataBroker) { + this.legacyDataBroker = dataBroker; } public void start() { @@ -113,29 +118,30 @@ public class RootBindingAwareBroker implements // controllerRoot = new RootSalInstance(getRpcProviderRegistry(), getNotificationBroker(), getDataBroker()); + ImmutableClassToInstanceMap.Builder consBuilder = ImmutableClassToInstanceMap.builder(); - supportedConsumerServices = ImmutableClassToInstanceMap. builder() - .put(NotificationService.class, getRoot()) // - .put(DataBrokerService.class, getRoot()) // - .put(RpcConsumerRegistry.class, getRoot()) // - .put(MountService.class, mountManager).build(); - + consBuilder.put(NotificationService.class, getRoot()); + consBuilder.put(DataBrokerService.class, getRoot()); + consBuilder.put(RpcConsumerRegistry.class, getRoot()); + if(dataBroker != null) { + consBuilder.put(BindingDataBroker.class, dataBroker); + } + consBuilder.put(MountService.class, mountManager).build(); + supportedConsumerServices = consBuilder.build(); supportedProviderServices = ImmutableClassToInstanceMap. builder() - .putAll(supportedConsumerServices) - .put(NotificationProviderService.class, getRoot()) // - .put(DataProviderService.class, getRoot()) // - .put(RpcProviderRegistry.class, getRoot()) // + .putAll(supportedConsumerServices).put(NotificationProviderService.class, getRoot()) + .put(DataProviderService.class, getRoot()).put(RpcProviderRegistry.class, getRoot()) .put(MountProviderService.class, mountManager).build(); } @Override - public ConsumerContext registerConsumer(BindingAwareConsumer consumer, BundleContext ctx) { + public ConsumerContext registerConsumer(final BindingAwareConsumer consumer, final BundleContext ctx) { checkState(supportedConsumerServices != null, "Broker is not initialized."); return BindingContextUtils.createConsumerContextAndInitialize(consumer, supportedConsumerServices); } @Override - public ProviderContext registerProvider(BindingAwareProvider provider, BundleContext ctx) { + public ProviderContext registerProvider(final BindingAwareProvider provider, final BundleContext ctx) { checkState(supportedProviderServices != null, "Broker is not initialized."); return BindingContextUtils.createProviderContextAndInitialize(provider, supportedProviderServices); } @@ -146,34 +152,38 @@ public class RootBindingAwareBroker implements // } @Override - public RoutedRpcRegistration addRoutedRpcImplementation(Class type, T implementation) - throws IllegalStateException { + public RoutedRpcRegistration addRoutedRpcImplementation(final Class type, + final T implementation) throws IllegalStateException { return getRoot().addRoutedRpcImplementation(type, implementation); } @Override - public RpcRegistration addRpcImplementation(Class type, T implementation) + public RpcRegistration addRpcImplementation(final Class type, final T implementation) throws IllegalStateException { return getRoot().addRpcImplementation(type, implementation); } @Override - public T getRpcService(Class module) { + public T getRpcService(final Class module) { return getRoot().getRpcService(module); } + @Override public >> ListenerRegistration registerRouteChangeListener( - L arg0) { + final L arg0) { return getRoot().registerRouteChangeListener(arg0); } - public class RootSalInstance extends AbstractBindingSalProviderInstance { - public RootSalInstance(RpcProviderRegistry rpcRegistry, NotificationProviderService notificationBroker, - DataProviderService dataBroker) { + public RootSalInstance(final RpcProviderRegistry rpcRegistry, + final NotificationProviderService notificationBroker, final DataProviderService dataBroker) { super(rpcRegistry, notificationBroker, dataBroker); } } + + public void setDataBroker(final BindingDataBroker asyncDataBroker) { + dataBroker = asyncDataBroker; + } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang b/opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang index 428025a58d..4456dea77f 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang +++ b/opendaylight/md-sal/sal-binding-broker/src/main/yang/opendaylight-binding-broker-impl.yang @@ -42,6 +42,12 @@ module opendaylight-sal-binding-broker-impl { config:provided-service sal:binding-data-consumer-broker; config:java-name-prefix ForwardedCompatibleDataBrokerImpl; } + + identity binding-forwarded-data-broker { + base config:module-type; + config:provided-service sal:binding-async-data-broker; + config:java-name-prefix BindingAsyncDataBrokerImpl; + } identity binding-rpc-broker { base config:module-type; @@ -62,6 +68,26 @@ module opendaylight-sal-binding-broker-impl { config:java-name-prefix RuntimeMapping; } + grouping dom-forwarding-component { + container dom-async-broker { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity dom:dom-broker-osgi-registry; + } + } + } + + container binding-mapping-service { + uses config:service-ref { + refine type { + mandatory true; + config:required-identity binding-dom-mapping-service; + } + } + } + } + augment "/config:modules/config:module/config:configuration" { case binding-broker-impl { when "/config:modules/config:module/config:type = 'binding-broker-impl'"; @@ -123,22 +149,15 @@ module opendaylight-sal-binding-broker-impl { case binding-data-compatible-broker { when "/config:modules/config:module/config:type = 'binding-data-compatible-broker'"; - container dom-async-broker { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity dom:dom-broker-osgi-registry; - } - } - } - - container binding-mapping-service { - uses config:service-ref { - refine type { - mandatory true; - config:required-identity binding-dom-mapping-service; - } - } + uses dom-forwarding-component; + } + } + + augment "/config:modules/config:module/config:configuration" { + case binding-forwarded-data-broker { + when "/config:modules/config:module/config:type = 'binding-forwarded-data-broker'"; + container binding-forwarded-data-broker { + uses dom-forwarding-component; } } } diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java index ae65c8889a..623b2fdd63 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java @@ -48,7 +48,6 @@ import org.opendaylight.controller.sal.dom.broker.impl.DataStoreStatsWrapper; import org.opendaylight.controller.sal.dom.broker.impl.HashMapDataStore; import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareDataStoreAdapter; import org.opendaylight.controller.sal.dom.broker.impl.SchemaAwareRpcBroker; -import org.opendaylight.controller.sal.dom.broker.impl.SchemaContextProvider; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.sal.binding.generator.impl.RuntimeGeneratedMappingServiceImpl; import org.opendaylight.yangtools.yang.common.QName; @@ -57,7 +56,6 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.impl.codec.BindingIndependentMappingService; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; import org.reflections.Reflections; import org.reflections.scanners.ResourcesScanner; @@ -70,7 +68,7 @@ import com.google.common.collect.ImmutableClassToInstanceMap; import com.google.common.collect.ImmutableMap; import com.google.common.util.concurrent.ListeningExecutorService; -public class BindingTestContext implements AutoCloseable, SchemaContextProvider { +public class BindingTestContext implements AutoCloseable { public static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier TREE_ROOT = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier .builder().toInstance(); @@ -101,22 +99,19 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider private MountPointManagerImpl biMountImpl; - private SchemaContext schemaContext; + private ImmutableMap newDatastores; private BackwardsCompatibleDataBroker biCompatibleBroker; - private final List schemaListeners = new ArrayList<>(); - private DataProviderService baData; private DOMDataBroker newDOMDataBroker; - @Override - public SchemaContext getSchemaContext() { - return schemaContext; - } + private final MockSchemaService mockSchemaService = new MockSchemaService(); + + public DOMDataBroker getDomAsyncDataBroker() { return newDOMDataBroker; @@ -128,6 +123,7 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider this.startWithSchema = startWithSchema; } + @Deprecated public void startDomDataStore() { checkState(dataStore == null, "DataStore already started."); checkState(biDataImpl != null, "Dom Data Broker not present"); @@ -140,7 +136,7 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider } else { dataStore = schemaAwareDataStore; } - + mockSchemaService.registerSchemaServiceListener(schemaAwareDataStore); biDataImpl.registerConfigurationReader(TREE_ROOT, dataStore); biDataImpl.registerOperationalReader(TREE_ROOT, dataStore); biDataImpl.registerCommitHandler(TREE_ROOT, dataStore); @@ -166,9 +162,9 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider biCompatibleBroker = new BackwardsCompatibleDataBroker(newDOMDataBroker); - schemaListeners.add(configStore); - schemaListeners.add(operStore); - schemaListeners.add(biCompatibleBroker); + mockSchemaService.registerSchemaServiceListener(configStore); + mockSchemaService.registerSchemaServiceListener(operStore); + mockSchemaService.registerSchemaServiceListener(biCompatibleBroker); biDataLegacyBroker = biCompatibleBroker; } @@ -188,7 +184,7 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider baBrokerImpl.getMountManager().setDataCommitExecutor(executor); baBrokerImpl.getMountManager().setNotificationExecutor(executor); baBrokerImpl.setRpcBroker(new RpcProviderRegistryImpl("test")); - baBrokerImpl.setDataBroker(baData); + baBrokerImpl.setLegacyDataBroker(baData); baBrokerImpl.setNotificationBroker(baNotifyImpl); baBrokerImpl.start(); } @@ -268,20 +264,11 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider public void startBindingToDomMappingService() { checkState(classPool != null, "ClassPool needs to be present"); mappingServiceImpl = new RuntimeGeneratedMappingServiceImpl(classPool); + mockSchemaService.registerSchemaServiceListener(mappingServiceImpl); } public void updateYangSchema(final String[] files) { - schemaContext = getContext(files); - - if (schemaAwareDataStore != null) { - schemaAwareDataStore.onGlobalContextUpdated(schemaContext); - } - if (mappingServiceImpl != null) { - mappingServiceImpl.onGlobalContextUpdated(schemaContext); - } - for(SchemaContextListener listener : schemaListeners) { - listener.onGlobalContextUpdated(schemaContext); - } + mockSchemaService.changeSchema(getContext(files)); } public static String[] getAllYangFilesOnClasspath() { @@ -340,8 +327,7 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider } public void startNewBindingDataBroker() { - ForwardedBackwardsCompatibleDataBroker forwarded = new ForwardedBackwardsCompatibleDataBroker(newDOMDataBroker, mappingServiceImpl, executor); - schemaListeners.add(forwarded); + ForwardedBackwardsCompatibleDataBroker forwarded = new ForwardedBackwardsCompatibleDataBroker(newDOMDataBroker, mappingServiceImpl,mockSchemaService, executor); baData = forwarded; } @@ -353,7 +339,7 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider private void startDomBroker() { checkState(executor != null); biBrokerImpl = new BrokerImpl(); - biBrokerImpl.setRouter(new SchemaAwareRpcBroker("/", this)); + biBrokerImpl.setRouter(new SchemaAwareRpcBroker("/", mockSchemaService)); } @@ -429,4 +415,6 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider public MountProvisionService getDomMountProviderService() { return biMountImpl; } + + } diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/MockSchemaService.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/MockSchemaService.java new file mode 100644 index 0000000000..c8acbcd994 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/MockSchemaService.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.binding.test.util; + +import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.opendaylight.controller.sal.dom.broker.impl.SchemaContextProvider; +import org.opendaylight.yangtools.concepts.ListenerRegistration; +import org.opendaylight.yangtools.concepts.util.ListenerRegistry; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaServiceListener; + +@SuppressWarnings("deprecation") +public final class MockSchemaService implements SchemaService, SchemaContextProvider { + + private SchemaContext schemaContext; + + ListenerRegistry listeners = ListenerRegistry.create(); + + @Override + public void addModule(final Module module) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized SchemaContext getGlobalContext() { + return schemaContext; + } + + @Override + public synchronized SchemaContext getSessionContext() { + return schemaContext; + } + + @Override + public ListenerRegistration registerSchemaServiceListener( + final SchemaServiceListener listener) { + return listeners.register(listener); + } + + @Override + public void removeModule(final Module module) { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized SchemaContext getSchemaContext() { + return schemaContext; + } + + public synchronized void changeSchema(final SchemaContext newContext) { + schemaContext = newContext; + for (ListenerRegistration listener : listeners) { + listener.getInstance().onGlobalContextUpdated(schemaContext); + } + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-binding-config/src/main/yang/opendaylight-md-sal-binding.yang b/opendaylight/md-sal/sal-binding-config/src/main/yang/opendaylight-md-sal-binding.yang index 38f0da3ccc..4a2ec8a063 100644 --- a/opendaylight/md-sal/sal-binding-config/src/main/yang/opendaylight-md-sal-binding.yang +++ b/opendaylight/md-sal/sal-binding-config/src/main/yang/opendaylight-md-sal-binding.yang @@ -22,6 +22,11 @@ module opendaylight-md-sal-binding { base "config:service-type"; config:java-class "org.opendaylight.controller.sal.binding.api.data.DataProviderService"; } + + identity binding-async-data-broker { + base "config:service-type"; + config:java-class "org.opendaylight.controller.md.sal.binding.api.BindingDataBroker"; + } identity binding-data-consumer-broker { base "config:service-type"; diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java index e78f2b32df..4da727f5c2 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicator.java @@ -10,17 +10,20 @@ package org.opendaylight.controller.sal.connect.netconf.listener; import java.util.ArrayDeque; import java.util.Collections; import java.util.Iterator; +import java.util.List; import java.util.Queue; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.api.NetconfTerminationReason; +import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; import org.opendaylight.controller.netconf.client.NetconfClientDispatcher; import org.opendaylight.controller.netconf.client.NetconfClientSession; import org.opendaylight.controller.netconf.client.NetconfClientSessionListener; import org.opendaylight.controller.netconf.client.conf.NetconfReconnectingClientConfiguration; import org.opendaylight.controller.netconf.util.xml.XmlElement; -import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; import org.opendaylight.controller.netconf.util.xml.XmlUtil; import org.opendaylight.controller.sal.common.util.RpcErrors; import org.opendaylight.controller.sal.common.util.Rpcs; @@ -35,6 +38,8 @@ import org.opendaylight.yangtools.yang.common.RpcResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.google.common.base.Strings; +import com.google.common.collect.Lists; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -45,12 +50,9 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener, private static final Logger logger = LoggerFactory.getLogger(NetconfDeviceCommunicator.class); - private static final RpcResult FAILED_RPC_RESULT = new FailedRpcResult<>(RpcErrors.getRpcError( - null, null, null, RpcError.ErrorSeverity.ERROR, "Netconf session disconnected", - RpcError.ErrorType.PROTOCOL, null)); - private final RemoteDevice remoteDevice; private final RemoteDeviceId id; + private final Lock sessionLock = new ReentrantLock(); public NetconfDeviceCommunicator(final RemoteDeviceId id, final RemoteDevice remoteDevice) { @@ -62,14 +64,21 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener, private NetconfClientSession session; @Override - public synchronized void onSessionUp(final NetconfClientSession session) { - logger.debug("{}: Session established", id); - this.session = session; + public void onSessionUp(final NetconfClientSession session) { + sessionLock.lock(); + try { + logger.debug("{}: Session established", id); + this.session = session; - final NetconfSessionCapabilities netconfSessionCapabilities = NetconfSessionCapabilities.fromNetconfSession(session); - logger.trace("{}: Session advertised capabilities: {}", id, netconfSessionCapabilities); + final NetconfSessionCapabilities netconfSessionCapabilities = + NetconfSessionCapabilities.fromNetconfSession(session); + logger.trace("{}: Session advertised capabilities: {}", id, netconfSessionCapabilities); - remoteDevice.onRemoteSessionUp(netconfSessionCapabilities, this); + remoteDevice.onRemoteSessionUp(netconfSessionCapabilities, this); + } + finally { + sessionLock.unlock(); + } } public void initializeRemoteConnection(final NetconfClientDispatcher dispatch, @@ -77,37 +86,75 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener, dispatch.createReconnectingClient(config); } - private synchronized void tearDown(final Exception e) { - remoteDevice.onRemoteSessionDown(); - session = null; + private void tearDown( String reason ) { + List>> futuresToCancel = Lists.newArrayList(); + sessionLock.lock(); + try { + if( session != null ) { + session = null; + + /* + * Walk all requests, check if they have been executing + * or cancelled and remove them from the queue. + */ + final Iterator it = requests.iterator(); + while (it.hasNext()) { + final Request r = it.next(); + if (r.future.isUncancellable()) { + futuresToCancel.add( r.future ); + it.remove(); + } else if (r.future.isCancelled()) { + // This just does some house-cleaning + it.remove(); + } + } - /* - * Walk all requests, check if they have been executing - * or cancelled and remove them from the queue. - */ - final Iterator it = requests.iterator(); - while (it.hasNext()) { - final Request r = it.next(); - if (r.future.isUncancellable()) { - r.future.setException(e); - it.remove(); - } else if (r.future.isCancelled()) { - // This just does some house-cleaning - it.remove(); + remoteDevice.onRemoteSessionDown(); + } + } + finally { + sessionLock.unlock(); + } + + // Notify pending request futures outside of the sessionLock to avoid unnecessarily + // blocking the caller. + for( UncancellableFuture> future: futuresToCancel ) { + if( Strings.isNullOrEmpty( reason ) ) { + future.set( createSessionDownRpcResult() ); + } else { + future.set( createErrorRpcResult( RpcError.ErrorType.TRANSPORT, reason ) ); } } } + private RpcResult createSessionDownRpcResult() + { + return createErrorRpcResult( RpcError.ErrorType.TRANSPORT, + String.format( "The netconf session to %1$s is disconnected", id.getName() ) ); + } + + private RpcResult createErrorRpcResult( RpcError.ErrorType errorType, String message ) + { + return new FailedRpcResult( RpcErrors.getRpcError( null, + NetconfDocumentedException.ErrorTag.operation_failed.getTagValue(), + null, RpcError.ErrorSeverity.ERROR, message, errorType, null ) ); + } + @Override public void onSessionDown(final NetconfClientSession session, final Exception e) { logger.warn("{}: Session went down", id, e); - tearDown(e); + tearDown( null ); } @Override public void onSessionTerminated(final NetconfClientSession session, final NetconfTerminationReason reason) { logger.warn("{}: Session terminated {}", id, reason); - tearDown(new RuntimeException(reason.getErrorMessage())); + tearDown( reason.getErrorMessage() ); + } + + @Override + public void close() { + tearDown( String.format( "The netconf session to %1$s has been closed", id.getName() ) ); } @Override @@ -123,73 +170,109 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener, } } - private synchronized void processMessage(final NetconfMessage message) { - final Request r = requests.peek(); - if (r.future.isUncancellable()) { - requests.poll(); + private void processMessage(final NetconfMessage message) { + Request request = null; + sessionLock.lock(); + try { + request = requests.peek(); + if (request.future.isUncancellable()) { + requests.poll(); + } + else { + request = null; + logger.warn("{}: Ignoring unsolicited message {}", id, msgToS(message)); + } + } + finally { + sessionLock.unlock(); + } + + if( request != null ) { logger.debug("{}: Message received {}", id, message); if(logger.isTraceEnabled()) { - logger.trace("{}: Matched request: {} to response: {}", id, msgToS(r.request), msgToS(message)); + logger.trace( "{}: Matched request: {} to response: {}", id, + msgToS( request.request ), msgToS( message ) ); } try { - NetconfMessageTransformUtil.checkValidReply(r.request, message); - } catch (final IllegalStateException e) { - logger.warn("{}: Invalid request-reply match, reply message contains different message-id, request: {}, response: {}", id, - msgToS(r.request), msgToS(message), e); - r.future.setException(e); + NetconfMessageTransformUtil.checkValidReply( request.request, message ); + } + catch (final NetconfDocumentedException e) { + logger.warn( "{}: Invalid request-reply match, reply message contains different message-id, request: {}, response: {}", + id, msgToS( request.request ), msgToS( message ), e ); + + request.future.set( new FailedRpcResult( + NetconfMessageTransformUtil.toRpcError( e ) ) ); return; } try { NetconfMessageTransformUtil.checkSuccessReply(message); - } catch (NetconfDocumentedException | IllegalStateException e) { - logger.warn("{}: Error reply from remote device, request: {}, response: {}", id, - msgToS(r.request), msgToS(message), e); - r.future.setException(e); + } + catch( NetconfDocumentedException e ) { + logger.warn( "{}: Error reply from remote device, request: {}, response: {}", id, + msgToS( request.request ), msgToS( message ), e ); + + request.future.set( new FailedRpcResult( + NetconfMessageTransformUtil.toRpcError( e ) ) ); return; } - r.future.set(Rpcs.getRpcResult(true, message, Collections.emptySet())); - } else { - logger.warn("{}: Ignoring unsolicited message {}", id, msgToS(message)); + request.future.set(Rpcs.getRpcResult( true, message, Collections.emptySet() ) ); } } - @Override - public void close() { - tearDown(new RuntimeException("Closed")); - } - private static String msgToS(final NetconfMessage msg) { return XmlUtil.toString(msg.getDocument()); } @Override - public synchronized ListenableFuture> sendRequest(final NetconfMessage message, final QName rpc) { + public ListenableFuture> sendRequest( + final NetconfMessage message, final QName rpc) { + sessionLock.lock(); + try { + return sendRequestWithLock( message, rpc ); + } + finally { + sessionLock.unlock(); + } + } + + private ListenableFuture> sendRequestWithLock( + final NetconfMessage message, final QName rpc) { if(logger.isTraceEnabled()) { logger.trace("{}: Sending message {}", id, msgToS(message)); } if (session == null) { logger.warn("{}: Session is disconnected, failing RPC request {}", id, message); - return Futures.immediateFuture(FAILED_RPC_RESULT); + return Futures.immediateFuture( createSessionDownRpcResult() ); } - final Request req = new Request(new UncancellableFuture>(true), message, rpc); + final Request req = new Request( new UncancellableFuture>(true), + message ); requests.add(req); session.sendMessage(req.request).addListener(new FutureListener() { @Override public void operationComplete(final Future future) throws Exception { - if (!future.isSuccess()) { + if( !future.isSuccess() ) { // We expect that a session down will occur at this point - logger.debug("{}: Failed to send request {}", id, XmlUtil.toString(req.request.getDocument()), future.cause()); - req.future.setException(future.cause()); - } else { - logger.trace("{}: Finished sending request {}", id, req.request); + logger.debug( "{}: Failed to send request {}", id, + XmlUtil.toString(req.request.getDocument()), future.cause() ); + + if( future.cause() != null ) { + req.future.set( createErrorRpcResult( RpcError.ErrorType.TRANSPORT, + future.cause().getLocalizedMessage() ) ); + } else { + req.future.set( createSessionDownRpcResult() ); // assume session is down + } + req.future.setException( future.cause() ); + } + else { + logger.trace( "Finished sending request {}", req.request ); } } }); @@ -215,12 +298,11 @@ public class NetconfDeviceCommunicator implements NetconfClientSessionListener, private static final class Request { final UncancellableFuture> future; final NetconfMessage request; - final QName rpc; - private Request(final UncancellableFuture> future, final NetconfMessage request, final QName rpc) { + private Request(final UncancellableFuture> future, + final NetconfMessage request) { this.future = future; this.request = request; - this.rpc = rpc; } } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java index 1284d6d1ce..08a5822d36 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/util/NetconfMessageTransformUtil.java @@ -12,14 +12,17 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import javax.annotation.Nullable; import org.opendaylight.controller.netconf.api.NetconfDocumentedException; import org.opendaylight.controller.netconf.api.NetconfMessage; import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil; -import org.opendaylight.controller.netconf.util.xml.XmlUtil; +import org.opendaylight.controller.sal.common.util.RpcErrors; import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcError.ErrorSeverity; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.Node; @@ -36,6 +39,7 @@ import org.w3c.dom.Element; import com.google.common.base.Predicate; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Sets; @@ -99,20 +103,68 @@ public class NetconfMessageTransformUtil { return new CompositeNodeTOImpl(argument.getNodeType(), null, list); } - public static void checkValidReply(final NetconfMessage input, final NetconfMessage output) { + public static void checkValidReply(final NetconfMessage input, final NetconfMessage output) + throws NetconfDocumentedException { final String inputMsgId = input.getDocument().getDocumentElement().getAttribute("message-id"); final String outputMsgId = output.getDocument().getDocumentElement().getAttribute("message-id"); if(inputMsgId.equals(outputMsgId) == false) { - final String requestXml = XmlUtil.toString(input.getDocument()); - final String responseXml = XmlUtil.toString(output.getDocument()); - throw new IllegalStateException(String.format("Rpc request and reply message IDs must be same. Request: %s, response: %s", requestXml, responseXml)); + Map errorInfo = ImmutableMap.builder() + .put( "actual-message-id", outputMsgId ) + .put( "expected-message-id", inputMsgId ) + .build(); + + throw new NetconfDocumentedException( "Response message contained unknown \"message-id\"", + null, NetconfDocumentedException.ErrorType.protocol, + NetconfDocumentedException.ErrorTag.bad_attribute, + NetconfDocumentedException.ErrorSeverity.error, errorInfo ); } } public static void checkSuccessReply(final NetconfMessage output) throws NetconfDocumentedException { if(NetconfMessageUtil.isErrorMessage(output)) { - throw new IllegalStateException(String.format("Response contains error: %s", XmlUtil.toString(output.getDocument()))); + throw NetconfDocumentedException.fromXMLDocument( output.getDocument() ); + } + } + + public static RpcError toRpcError( NetconfDocumentedException ex ) + { + StringBuilder infoBuilder = new StringBuilder(); + Map errorInfo = ex.getErrorInfo(); + if( errorInfo != null ) + { + for( Entry e: errorInfo.entrySet() ) { + infoBuilder.append( '<' ).append( e.getKey() ).append( '>' ).append( e.getValue() ) + .append( "' ); + + } + } + + return RpcErrors.getRpcError( null, ex.getErrorTag().getTagValue(), infoBuilder.toString(), + toRpcErrorSeverity( ex.getErrorSeverity() ), ex.getLocalizedMessage(), + toRpcErrorType( ex.getErrorType() ), ex.getCause() ); + } + + private static ErrorSeverity toRpcErrorSeverity( NetconfDocumentedException.ErrorSeverity severity ) { + switch( severity ) { + case warning: + return RpcError.ErrorSeverity.WARNING; + default: + return RpcError.ErrorSeverity.ERROR; + } + } + + private static RpcError.ErrorType toRpcErrorType( NetconfDocumentedException.ErrorType type ) + { + switch( type ) { + case protocol: + return RpcError.ErrorType.PROTOCOL; + case rpc: + return RpcError.ErrorType.RPC; + case transport: + return RpcError.ErrorType.TRANSPORT; + default: + return RpcError.ErrorType.APPLICATION; } } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java index 5ac32b5b3c..c1b9f7b47b 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTest.java @@ -26,7 +26,7 @@ import java.util.concurrent.Executors; import org.junit.Test; import org.mockito.Mockito; import org.opendaylight.controller.netconf.api.NetconfMessage; -import org.opendaylight.controller.netconf.util.xml.XmlNetconfConstants; +import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants; import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.controller.sal.connect.api.MessageTransformer; import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator; diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java new file mode 100644 index 0000000000..391bf9c6a4 --- /dev/null +++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfDeviceCommunicatorTest.java @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.sal.connect.netconf.listener; + +import io.netty.channel.ChannelFuture; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; + +import java.io.ByteArrayInputStream; +import java.util.Collection; +import java.util.Collections; +import java.util.UUID; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.same; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.RPC_REPLY_KEY; +import static org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants.URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0; + +import org.apache.commons.lang3.StringUtils; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.api.NetconfTerminationReason; +import org.opendaylight.controller.netconf.client.NetconfClientSession; +import org.opendaylight.controller.sal.connect.api.RemoteDevice; +import org.opendaylight.controller.sal.connect.api.RemoteDeviceCommunicator; +import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil; +import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.RpcError; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import com.google.common.base.Strings; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.ListenableFuture; + +public class NetconfDeviceCommunicatorTest { + + @Mock + NetconfClientSession mockSession; + + @Mock + RemoteDevice mockDevice; + + NetconfDeviceCommunicator communicator; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks( this ); + + communicator = new NetconfDeviceCommunicator( new RemoteDeviceId( "test" ), mockDevice ); + } + + @SuppressWarnings("unchecked") + void setupSession() + { + doReturn( Collections.emptySet() ).when( mockSession ).getServerCapabilities(); + doNothing().when( mockDevice ).onRemoteSessionUp( any( NetconfSessionCapabilities.class ), + any( RemoteDeviceCommunicator.class ) ); + communicator.onSessionUp( mockSession ); + } + + private ListenableFuture> sendRequest() throws Exception { + return sendRequest( UUID.randomUUID().toString() ); + } + + @SuppressWarnings("unchecked") + private ListenableFuture> sendRequest( String messageID ) throws Exception { + Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + Element element = doc.createElement( "request" ); + element.setAttribute( "message-id", messageID ); + doc.appendChild( element ); + NetconfMessage message = new NetconfMessage( doc ); + + ChannelFuture mockChannelFuture = mock( ChannelFuture.class ); + doReturn( mockChannelFuture ).when( mockChannelFuture ) + .addListener( any( (GenericFutureListener.class ) ) ); + doReturn( mockChannelFuture ).when( mockSession ).sendMessage( same( message ) ); + + ListenableFuture> resultFuture = + communicator.sendRequest( message, QName.create( "mock rpc" ) ); + + assertNotNull( "ListenableFuture is null", resultFuture ); + return resultFuture; + } + + @Test + public void testOnSessionUp() { + String testCapability = "urn:opendaylight:params:xml:ns:test?module=test-module&revision=2014-06-02"; + Collection serverCapabilities = + Sets.newHashSet( NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString(), + NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString(), + testCapability ); + doReturn( serverCapabilities ).when( mockSession ).getServerCapabilities(); + + ArgumentCaptor netconfSessionCapabilities = + ArgumentCaptor.forClass( NetconfSessionCapabilities.class ); + doNothing().when( mockDevice ).onRemoteSessionUp( netconfSessionCapabilities.capture(), eq( communicator ) ); + + communicator.onSessionUp( mockSession ); + + verify( mockSession ).getServerCapabilities(); + verify( mockDevice ).onRemoteSessionUp( netconfSessionCapabilities.capture(), eq( communicator ) ); + + NetconfSessionCapabilities actualCapabilites = netconfSessionCapabilities.getValue(); + assertEquals( "containsCapability", true, actualCapabilites.containsCapability( + NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString() ) ); + assertEquals( "containsCapability", true, actualCapabilites.containsCapability( testCapability ) ); + assertEquals( "getModuleBasedCaps", Sets.newHashSet( + QName.create( "urn:opendaylight:params:xml:ns:test", "2014-06-02", "test-module" )), + actualCapabilites.getModuleBasedCaps() ); + assertEquals( "isRollbackSupported", true, actualCapabilites.isRollbackSupported() ); + assertEquals( "isMonitoringSupported", true, actualCapabilites.isMonitoringSupported() ); + } + + @SuppressWarnings("unchecked") + @Test(timeout=5000) + public void testOnSessionDown() throws Exception { + setupSession(); + + ListenableFuture> resultFuture1 = sendRequest(); + ListenableFuture> resultFuture2 = sendRequest(); + + doNothing().when( mockDevice ).onRemoteSessionDown(); + + communicator.onSessionDown( mockSession, new Exception( "mock ex" ) ); + + verifyErrorRpcResult( resultFuture1.get(), RpcError.ErrorType.TRANSPORT, "operation-failed" ); + verifyErrorRpcResult( resultFuture2.get(), RpcError.ErrorType.TRANSPORT, "operation-failed" ); + + verify( mockDevice ).onRemoteSessionDown(); + + reset( mockDevice ); + + communicator.onSessionDown( mockSession, new Exception( "mock ex" ) ); + + verify( mockDevice, never() ).onRemoteSessionDown(); + } + + @Test + public void testOnSessionTerminated() throws Exception { + setupSession(); + + ListenableFuture> resultFuture = sendRequest(); + + doNothing().when( mockDevice ).onRemoteSessionDown(); + + String reasonText = "testing terminate"; + NetconfTerminationReason reason = new NetconfTerminationReason( reasonText ); + communicator.onSessionTerminated( mockSession, reason ); + + RpcError rpcError = verifyErrorRpcResult( resultFuture.get(), RpcError.ErrorType.TRANSPORT, + "operation-failed" ); + assertEquals( "RpcError message", reasonText, rpcError.getMessage() ); + + verify( mockDevice ).onRemoteSessionDown(); + } + + @Test + public void testClose() throws Exception { + communicator.close(); + verify( mockDevice, never() ).onRemoteSessionDown(); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Test + public void testSendRequest() throws Exception { + setupSession(); + + NetconfMessage message = new NetconfMessage( + DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() ); + QName rpc = QName.create( "mock rpc" ); + + ArgumentCaptor futureListener = + ArgumentCaptor.forClass( GenericFutureListener.class ); + + ChannelFuture mockChannelFuture = mock( ChannelFuture.class ); + doReturn( mockChannelFuture ).when( mockChannelFuture ).addListener( futureListener.capture() ); + doReturn( mockChannelFuture ).when( mockSession ).sendMessage( same( message ) ); + + ListenableFuture> resultFuture = communicator.sendRequest( message, rpc ); + + verify( mockSession ).sendMessage( same( message ) ); + + assertNotNull( "ListenableFuture is null", resultFuture ); + + verify( mockChannelFuture ).addListener( futureListener.capture() ); + Future operationFuture = mock( Future.class ); + doReturn( true ).when( operationFuture ).isSuccess(); + doReturn( true ).when( operationFuture ).isDone(); + futureListener.getValue().operationComplete( operationFuture ); + + try { + resultFuture.get( 1, TimeUnit.MILLISECONDS ); // verify it's not cancelled or has an error set + } + catch( TimeoutException e ) {} // expected + } + + @Test + public void testSendRequestWithNoSession() throws Exception { + NetconfMessage message = new NetconfMessage( + DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() ); + QName rpc = QName.create( "mock rpc" ); + + ListenableFuture> resultFuture = communicator.sendRequest( message, rpc ); + + assertNotNull( "ListenableFuture is null", resultFuture ); + + // Should have an immediate result + RpcResult rpcResult = resultFuture.get( 3, TimeUnit.MILLISECONDS ); + + verifyErrorRpcResult( rpcResult, RpcError.ErrorType.TRANSPORT, "operation-failed" ); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Test + public void testSendRequestWithWithSendFailure() throws Exception { + setupSession(); + + NetconfMessage message = new NetconfMessage( + DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument() ); + QName rpc = QName.create( "mock rpc" ); + + ArgumentCaptor futureListener = + ArgumentCaptor.forClass( GenericFutureListener.class ); + + ChannelFuture mockChannelFuture = mock( ChannelFuture.class ); + doReturn( mockChannelFuture ).when( mockChannelFuture ).addListener( futureListener.capture() ); + doReturn( mockChannelFuture ).when( mockSession ).sendMessage( same( message ) ); + + ListenableFuture> resultFuture = communicator.sendRequest( message, rpc ); + + assertNotNull( "ListenableFuture is null", resultFuture ); + + verify( mockChannelFuture ).addListener( futureListener.capture() ); + + Future operationFuture = mock( Future.class ); + doReturn( false ).when( operationFuture ).isSuccess(); + doReturn( true ).when( operationFuture ).isDone(); + doReturn( new Exception( "mock error" ) ).when( operationFuture ).cause(); + futureListener.getValue().operationComplete( operationFuture ); + + // Should have an immediate result + RpcResult rpcResult = resultFuture.get( 3, TimeUnit.MILLISECONDS ); + + RpcError rpcError = verifyErrorRpcResult( rpcResult, RpcError.ErrorType.TRANSPORT, "operation-failed" ); + assertEquals( "RpcError message contains \"mock error\"", true, + rpcError.getMessage().contains( "mock error" ) ); + } + + private NetconfMessage createSuccessResponseMessage( String messageID ) throws ParserConfigurationException { + Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument(); + Element rpcReply = doc.createElementNS( URN_IETF_PARAMS_XML_NS_NETCONF_BASE_1_0, RPC_REPLY_KEY ); + rpcReply.setAttribute( "message-id", messageID ); + Element element = doc.createElementNS( "ns", "data" ); + element.setTextContent( messageID ); + rpcReply.appendChild( element ); + doc.appendChild( rpcReply ); + + return new NetconfMessage( doc ); + } + + @Test + public void testOnSuccessfulResponseMessage() throws Exception { + setupSession(); + + String messageID1 = UUID.randomUUID().toString(); + ListenableFuture> resultFuture1 = sendRequest( messageID1 ); + + String messageID2 = UUID.randomUUID().toString(); + ListenableFuture> resultFuture2 = sendRequest( messageID2 ); + + communicator.onMessage( mockSession, createSuccessResponseMessage( messageID1 ) ); + communicator.onMessage( mockSession, createSuccessResponseMessage( messageID2 ) ); + + verifyResponseMessage( resultFuture1.get(), messageID1 ); + verifyResponseMessage( resultFuture2.get(), messageID2 ); + } + + @Test + public void testOnResponseMessageWithError() throws Exception { + setupSession(); + + String messageID = UUID.randomUUID().toString(); + ListenableFuture> resultFuture = sendRequest( messageID ); + + communicator.onMessage( mockSession, createErrorResponseMessage( messageID ) ); + + RpcError rpcError = verifyErrorRpcResult( resultFuture.get(), RpcError.ErrorType.RPC, + "missing-attribute" ); + assertEquals( "RpcError message", "Missing attribute", rpcError.getMessage() ); + + String errorInfo = rpcError.getInfo(); + assertNotNull( "RpcError info is null", errorInfo ); + assertEquals( "Error info contains \"foo\"", true, + errorInfo.contains( "foo" ) ); + assertEquals( "Error info contains \"bar\"", true, + errorInfo.contains( "bar" ) ); + } + + @Test + public void testOnResponseMessageWithWrongMessageID() throws Exception { + setupSession(); + + String messageID = UUID.randomUUID().toString(); + ListenableFuture> resultFuture = sendRequest( messageID ); + + communicator.onMessage( mockSession, createSuccessResponseMessage( UUID.randomUUID().toString() ) ); + + RpcError rpcError = verifyErrorRpcResult( resultFuture.get(), RpcError.ErrorType.PROTOCOL, + "bad-attribute" ); + assertEquals( "RpcError message non-empty", true, + !Strings.isNullOrEmpty( rpcError.getMessage() ) ); + + String errorInfo = rpcError.getInfo(); + assertNotNull( "RpcError info is null", errorInfo ); + assertEquals( "Error info contains \"actual-message-id\"", true, + errorInfo.contains( "actual-message-id" ) ); + assertEquals( "Error info contains \"expected-message-id\"", true, + errorInfo.contains( "expected-message-id" ) ); + } + + private NetconfMessage createErrorResponseMessage( String messageID ) throws Exception { + String xmlStr = + "" + + " " + + " rpc" + + " missing-attribute" + + " error" + + " Missing attribute" + + " " + + " foo" + + " bar" + + " " + + " " + + ""; + + ByteArrayInputStream bis = new ByteArrayInputStream( xmlStr.getBytes() ); + Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse( bis ); + return new NetconfMessage( doc ); + } + + private void verifyResponseMessage( RpcResult rpcResult, String dataText ) { + assertNotNull( "RpcResult is null", rpcResult ); + assertEquals( "isSuccessful", true, rpcResult.isSuccessful() ); + NetconfMessage messageResult = rpcResult.getResult(); + assertNotNull( "getResult", messageResult ); +// List> nodes = messageResult.getSimpleNodesByName( +// QName.create( URI.create( "ns" ), null, "data" ) ); +// assertNotNull( "getSimpleNodesByName", nodes ); +// assertEquals( "List> size", 1, nodes.size() ); +// assertEquals( "SimpleNode value", dataText, nodes.iterator().next().getValue() ); + } + + private RpcError verifyErrorRpcResult( RpcResult rpcResult, + RpcError.ErrorType expErrorType, String expErrorTag ) { + assertNotNull( "RpcResult is null", rpcResult ); + assertEquals( "isSuccessful", false, rpcResult.isSuccessful() ); + assertNotNull( "RpcResult errors is null", rpcResult.getErrors() ); + assertEquals( "Errors size", 1, rpcResult.getErrors().size() ); + RpcError rpcError = rpcResult.getErrors().iterator().next(); + assertEquals( "getErrorSeverity", RpcError.ErrorSeverity.ERROR, rpcError.getSeverity() ); + assertEquals( "getErrorType", expErrorType, rpcError.getErrorType() ); + assertEquals( "getErrorTag", expErrorTag, rpcError.getTag() ); + assertTrue( "getMessage is empty", StringUtils.isNotEmpty( rpcError.getMessage() ) ); + return rpcError; + } +} diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java index 4d9b198795..056be72d4e 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/rest/api/RestconfService.java @@ -107,13 +107,15 @@ public interface RestconfService { @Path("/config/{identifier:.+}") @Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML}) - public StructuredData readConfigurationData(@Encoded @PathParam("identifier") String identifier); + public StructuredData readConfigurationData(@Encoded @PathParam("identifier") String identifier, + @Context UriInfo depth); @GET @Path("/operational/{identifier:.+}") @Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML}) - public StructuredData readOperationalData(@Encoded @PathParam("identifier") String identifier); + public StructuredData readOperationalData(@Encoded @PathParam("identifier") String identifier, + @Context UriInfo depth); @PUT @Path("/config/{identifier:.+}") diff --git a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java index c0ce90e15d..9700d48bc2 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java +++ b/opendaylight/md-sal/sal-rest-connector/src/main/java/org/opendaylight/controller/sal/restconf/impl/RestconfImpl.java @@ -8,6 +8,13 @@ */ package org.opendaylight.controller.sal.restconf.impl; +import com.google.common.base.Objects; +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import java.net.URI; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -20,12 +27,10 @@ import java.util.HashMap; import java.util.List; import java.util.Set; import java.util.concurrent.Future; - import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; - import org.apache.commons.lang3.StringUtils; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.sal.core.api.mount.MountInstance; @@ -49,6 +54,7 @@ import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.InstanceIdent import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode; import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; import org.opendaylight.yangtools.yang.data.impl.NodeFactory; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; @@ -67,13 +73,6 @@ import org.opendaylight.yangtools.yang.model.util.EmptyType; import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder; import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder; -import com.google.common.base.Objects; -import com.google.common.base.Preconditions; -import com.google.common.base.Splitter; -import com.google.common.base.Strings; -import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; - public class RestconfImpl implements RestconfService { private final static RestconfImpl INSTANCE = new RestconfImpl(); @@ -252,7 +251,8 @@ public class RestconfImpl implements RestconfService { operationsAsData.add(immutableSimpleNode); String name = module.getName(); - LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName, null); + LeafSchemaNodeBuilder leafSchemaNodeBuilder = new LeafSchemaNodeBuilder(name, 0, rpcQName, + SchemaPath.create(true, QName.create("dummy"))); final LeafSchemaNodeBuilder fakeRpcSchemaNode = leafSchemaNodeBuilder; fakeRpcSchemaNode.setAugmenting(true); @@ -566,7 +566,7 @@ public class RestconfImpl implements RestconfService { } @Override - public StructuredData readConfigurationData(final String identifier) { + public StructuredData readConfigurationData(final String identifier, UriInfo info) { final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier); CompositeNode data = null; MountInstance mountPoint = iiWithData.getMountPoint(); @@ -577,11 +577,57 @@ public class RestconfImpl implements RestconfService { data = broker.readConfigurationData(iiWithData.getInstanceIdentifier()); } + data = pruneDataAtDepth( data, parseDepthParameter( info ) ); return new StructuredData(data, iiWithData.getSchemaNode(), iiWithData.getMountPoint()); } + @SuppressWarnings("unchecked") + private > T pruneDataAtDepth( T node, Integer depth ) { + if( depth == null ) { + return node; + } + + if( node instanceof CompositeNode ) { + ImmutableList.Builder> newChildNodes = ImmutableList.> builder(); + if( depth > 1 ) { + for( Node childNode: ((CompositeNode)node).getValue() ) { + newChildNodes.add( pruneDataAtDepth( childNode, depth - 1 ) ); + } + } + + return (T) ImmutableCompositeNode.create( node.getNodeType(), newChildNodes.build() ); + } + else { // SimpleNode + return node; + } + } + + private Integer parseDepthParameter( UriInfo info ) { + String param = info.getQueryParameters( false ).getFirst( "depth" ); + if( Strings.isNullOrEmpty( param ) || "unbounded".equals( param ) ) { + return null; + } + + try { + Integer depth = Integer.valueOf( param ); + if( depth < 1 ) { + throw new RestconfDocumentedException( new RestconfError( + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, "Invalid depth parameter: " + depth, + null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) ); + } + + return depth; + } + catch( NumberFormatException e ) { + throw new RestconfDocumentedException( new RestconfError( + ErrorType.PROTOCOL, ErrorTag.INVALID_VALUE, + "Invalid depth parameter: " + e.getMessage(), + null, "The depth parameter must be an integer > 1 or \"unbounded\"" ) ); + } + } + @Override - public StructuredData readOperationalData(final String identifier) { + public StructuredData readOperationalData(final String identifier, UriInfo info) { final InstanceIdWithSchemaNode iiWithData = this.controllerContext.toInstanceIdentifier(identifier); CompositeNode data = null; MountInstance mountPoint = iiWithData.getMountPoint(); @@ -592,6 +638,7 @@ public class RestconfImpl implements RestconfService { data = broker.readOperationalData(iiWithData.getInstanceIdentifier()); } + data = pruneDataAtDepth( data, parseDepthParameter( info ) ); return new StructuredData(data, iiWithData.getSchemaNode(), mountPoint); } diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonNotExistingLeafTypeTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonNotExistingLeafTypeTest.java index 42e1e3f739..24dba17c90 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonNotExistingLeafTypeTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/cnsn/to/json/test/CnSnToJsonNotExistingLeafTypeTest.java @@ -12,15 +12,14 @@ import static org.junit.Assert.assertTrue; import java.io.IOException; import java.util.Collections; - import javax.ws.rs.WebApplicationException; - import org.junit.BeforeClass; import org.junit.Test; import org.opendaylight.controller.sal.rest.impl.StructuredDataToJsonProvider; import org.opendaylight.controller.sal.restconf.impl.test.DummyType; import org.opendaylight.controller.sal.restconf.impl.test.TestUtils; import org.opendaylight.controller.sal.restconf.impl.test.YangAndXmlAndDataSchemaLoader; +import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.ModifyAction; import org.opendaylight.yangtools.yang.data.api.MutableCompositeNode; @@ -28,6 +27,7 @@ import org.opendaylight.yangtools.yang.data.api.MutableSimpleNode; import org.opendaylight.yangtools.yang.data.impl.NodeFactory; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder; import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder; import org.slf4j.Logger; @@ -65,9 +65,9 @@ public class CnSnToJsonNotExistingLeafTypeTest extends YangAndXmlAndDataSchemaLo private DataSchemaNode prepareDataSchemaNode() { ContainerSchemaNodeBuilder contBuild = new ContainerSchemaNodeBuilder("module", 1, TestUtils.buildQName("cont", - "simple:uri", "2012-12-17"), null); + "simple:uri", "2012-12-17"), SchemaPath.create(true, QName.create("dummy"))); LeafSchemaNodeBuilder leafBuild = new LeafSchemaNodeBuilder("module", 2, TestUtils.buildQName("lf1", - "simple:uri", "2012-12-17"), null); + "simple:uri", "2012-12-17"), SchemaPath.create(true, QName.create("dummy"))); leafBuild.setType(new DummyType()); leafBuild.setConfiguration(true); diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/MediaTypesTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/MediaTypesTest.java index 2037fd4862..319603dfc1 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/MediaTypesTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/MediaTypesTest.java @@ -23,6 +23,7 @@ import java.io.UnsupportedEncodingException; import javax.ws.rs.client.Entity; import javax.ws.rs.core.Application; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.UriInfo; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; @@ -96,21 +97,21 @@ public class MediaTypesTest extends JerseyTest { String uriPrefix = "/config/"; String uriPath = "ietf-interfaces:interfaces"; String uri = uriPrefix + uriPath; - when(restconfService.readConfigurationData(uriPath)).thenReturn(null); + when(restconfService.readConfigurationData(eq(uriPath), any(UriInfo.class))).thenReturn(null); get(uri, Draft02.MediaTypes.DATA+JSON); - verify(restconfService, times(1)).readConfigurationData(uriPath); + verify(restconfService, times(1)).readConfigurationData(eq(uriPath), any(UriInfo.class)); get(uri, Draft02.MediaTypes.DATA+XML); - verify(restconfService, times(2)).readConfigurationData(uriPath); + verify(restconfService, times(2)).readConfigurationData(eq(uriPath), any(UriInfo.class)); get(uri, MediaType.APPLICATION_JSON); - verify(restconfService, times(3)).readConfigurationData(uriPath); + verify(restconfService, times(3)).readConfigurationData(eq(uriPath), any(UriInfo.class)); get(uri, MediaType.APPLICATION_XML); - verify(restconfService, times(4)).readConfigurationData(uriPath); + verify(restconfService, times(4)).readConfigurationData(eq(uriPath), any(UriInfo.class)); get(uri, MediaType.TEXT_XML); - verify(restconfService, times(5)).readConfigurationData(uriPath); + verify(restconfService, times(5)).readConfigurationData(eq(uriPath), any(UriInfo.class)); // negative tests get(uri, MediaType.TEXT_PLAIN); - verify(restconfService, times(5)).readConfigurationData(uriPath); + verify(restconfService, times(5)).readConfigurationData(eq(uriPath), any(UriInfo.class)); } @Test @@ -118,21 +119,21 @@ public class MediaTypesTest extends JerseyTest { String uriPrefix = "/operational/"; String uriPath = "ietf-interfaces:interfaces"; String uri = uriPrefix + uriPath; - when(restconfService.readOperationalData(uriPath)).thenReturn(null); + when(restconfService.readOperationalData(eq(uriPath), any(UriInfo.class))).thenReturn(null); get(uri, Draft02.MediaTypes.DATA+JSON); - verify(restconfService, times(1)).readOperationalData(uriPath); + verify(restconfService, times(1)).readOperationalData(eq(uriPath), any(UriInfo.class)); get(uri, Draft02.MediaTypes.DATA+XML); - verify(restconfService, times(2)).readOperationalData(uriPath); + verify(restconfService, times(2)).readOperationalData(eq(uriPath), any(UriInfo.class)); get(uri, MediaType.APPLICATION_JSON); - verify(restconfService, times(3)).readOperationalData(uriPath); + verify(restconfService, times(3)).readOperationalData(eq(uriPath), any(UriInfo.class)); get(uri, MediaType.APPLICATION_XML); - verify(restconfService, times(4)).readOperationalData(uriPath); + verify(restconfService, times(4)).readOperationalData(eq(uriPath), any(UriInfo.class)); get(uri, MediaType.TEXT_XML); - verify(restconfService, times(5)).readOperationalData(uriPath); + verify(restconfService, times(5)).readOperationalData(eq(uriPath), any(UriInfo.class)); // negative tests get(uri, MediaType.TEXT_PLAIN); - verify(restconfService, times(5)).readOperationalData(uriPath); + verify(restconfService, times(5)).readOperationalData(eq(uriPath), any(UriInfo.class)); } @Test diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java index 893622f60a..f0a232fba6 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestGetOperationTest.java @@ -7,15 +7,17 @@ */ package org.opendaylight.controller.sal.restconf.impl.test; -import static junit.framework.Assert.assertNotNull; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.FileNotFoundException; +import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; @@ -24,17 +26,23 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.ws.rs.core.Application; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.junit.BeforeClass; import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; import org.opendaylight.controller.sal.core.api.mount.MountInstance; import org.opendaylight.controller.sal.core.api.mount.MountService; import org.opendaylight.controller.sal.rest.impl.JsonToCompositeNodeProvider; @@ -45,6 +53,7 @@ import org.opendaylight.controller.sal.rest.impl.XmlToCompositeNodeProvider; import org.opendaylight.controller.sal.restconf.impl.BrokerFacade; import org.opendaylight.controller.sal.restconf.impl.CompositeNodeWrapper; import org.opendaylight.controller.sal.restconf.impl.ControllerContext; +import org.opendaylight.controller.sal.restconf.impl.RestconfDocumentedException; import org.opendaylight.controller.sal.restconf.impl.RestconfImpl; import org.opendaylight.controller.sal.restconf.impl.SimpleNodeWrapper; import org.opendaylight.yangtools.yang.common.QName; @@ -52,10 +61,28 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; +import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; public class RestGetOperationTest extends JerseyTest { + static class NodeData { + Object key; + Object data; // List for a CompositeNode, value Object for a SimpleNode + + NodeData( Object key, Object data ) { + this.key = key; + this.data = data; + } + } + private static BrokerFacade brokerFacade; private static RestconfImpl restconfImpl; private static SchemaContext schemaContextYangsIetf; @@ -588,4 +615,314 @@ public class RestGetOperationTest extends JerseyTest { return null; } + @Test + public void getDataWithUriDepthParameterTest() throws UnsupportedEncodingException { + + ControllerContext.getInstance().setGlobalSchema( schemaContextModules ); + + CompositeNode depth1Cont = toCompositeNode( + toCompositeNodeData( toNestedQName( "depth1-cont" ), + toCompositeNodeData( toNestedQName( "depth2-cont1" ), + toCompositeNodeData( toNestedQName( "depth3-cont1" ), + toCompositeNodeData( toNestedQName( "depth4-cont1" ), + toSimpleNodeData( toNestedQName( "depth5-leaf1" ), "depth5-leaf1-value" ) + ), + toSimpleNodeData( toNestedQName( "depth4-leaf1" ), "depth4-leaf1-value" ) + ), + toSimpleNodeData( toNestedQName( "depth3-leaf1" ), "depth3-leaf1-value" ) + ), + toCompositeNodeData( toNestedQName( "depth2-cont2" ), + toCompositeNodeData( toNestedQName( "depth3-cont2" ), + toCompositeNodeData( toNestedQName( "depth4-cont2" ), + toSimpleNodeData( toNestedQName( "depth5-leaf2" ), "depth5-leaf2-value" ) + ), + toSimpleNodeData( toNestedQName( "depth4-leaf2" ), "depth4-leaf2-value" ) + ), + toSimpleNodeData( toNestedQName( "depth3-leaf2" ), "depth3-leaf2-value" ) + ), + toSimpleNodeData( toNestedQName( "depth2-leaf1" ), "depth2-leaf1-value" ) + ) ); + + when( brokerFacade.readConfigurationData( any( InstanceIdentifier.class ) ) ) + .thenReturn( depth1Cont ); + + // Test config with depth 1 + + Response response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "1" ) + .request( "application/xml" ).get(); + + verifyXMLResponse( response, expectEmptyContainer( "depth1-cont" ) ); + + // Test config with depth 2 + + response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "2" ) + .request( "application/xml" ).get(); + +// String xml="depth2-leaf1-value"; +// Response mr=mock(Response.class); +// when(mr.getEntity()).thenReturn( new java.io.StringBufferInputStream(xml) ); + + verifyXMLResponse( response, + expectContainer( "depth1-cont", + expectEmptyContainer( "depth2-cont1" ), + expectEmptyContainer( "depth2-cont2" ), + expectLeaf( "depth2-leaf1", "depth2-leaf1-value" ) + ) ); + + // Test config with depth 3 + + response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "3" ) + .request( "application/xml" ).get(); + + verifyXMLResponse( response, + expectContainer( "depth1-cont", + expectContainer( "depth2-cont1", + expectEmptyContainer( "depth3-cont1" ), + expectLeaf( "depth3-leaf1", "depth3-leaf1-value" ) + ), + expectContainer( "depth2-cont2", + expectEmptyContainer( "depth3-cont2" ), + expectLeaf( "depth3-leaf2", "depth3-leaf2-value" ) + ), + expectLeaf( "depth2-leaf1", "depth2-leaf1-value" ) + ) ); + + // Test config with depth 4 + + response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "4" ) + .request( "application/xml" ).get(); + + verifyXMLResponse( response, + expectContainer( "depth1-cont", + expectContainer( "depth2-cont1", + expectContainer( "depth3-cont1", + expectEmptyContainer( "depth4-cont1" ), + expectLeaf( "depth4-leaf1", "depth4-leaf1-value" ) + ), + expectLeaf( "depth3-leaf1", "depth3-leaf1-value" ) + ), + expectContainer( "depth2-cont2", + expectContainer( "depth3-cont2", + expectEmptyContainer( "depth4-cont2" ), + expectLeaf( "depth4-leaf2", "depth4-leaf2-value" ) + ), + expectLeaf( "depth3-leaf2", "depth3-leaf2-value" ) + ), + expectLeaf( "depth2-leaf1", "depth2-leaf1-value" ) + ) ); + + // Test config with depth 5 + + response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "5" ) + .request( "application/xml" ).get(); + + verifyXMLResponse( response, + expectContainer( "depth1-cont", + expectContainer( "depth2-cont1", + expectContainer( "depth3-cont1", + expectContainer( "depth4-cont1", + expectLeaf( "depth5-leaf1", "depth5-leaf1-value" ) + ), + expectLeaf( "depth4-leaf1", "depth4-leaf1-value" ) + ), + expectLeaf( "depth3-leaf1", "depth3-leaf1-value" ) + ), + expectContainer( "depth2-cont2", + expectContainer( "depth3-cont2", + expectContainer( "depth4-cont2", + expectLeaf( "depth5-leaf2", "depth5-leaf2-value" ) + ), + expectLeaf( "depth4-leaf2", "depth4-leaf2-value" ) + ), + expectLeaf( "depth3-leaf2", "depth3-leaf2-value" ) + ), + expectLeaf( "depth2-leaf1", "depth2-leaf1-value" ) + ) ); + + // Test config with depth unbounded + + response = target( "/config/nested-module:depth1-cont" ).queryParam( "depth", "unbounded" ) + .request( "application/xml" ).get(); + + verifyXMLResponse( response, + expectContainer( "depth1-cont", + expectContainer( "depth2-cont1", + expectContainer( "depth3-cont1", + expectContainer( "depth4-cont1", + expectLeaf( "depth5-leaf1", "depth5-leaf1-value" ) + ), + expectLeaf( "depth4-leaf1", "depth4-leaf1-value" ) + ), + expectLeaf( "depth3-leaf1", "depth3-leaf1-value" ) + ), + expectContainer( "depth2-cont2", + expectContainer( "depth3-cont2", + expectContainer( "depth4-cont2", + expectLeaf( "depth5-leaf2", "depth5-leaf2-value" ) + ), + expectLeaf( "depth4-leaf2", "depth4-leaf2-value" ) + ), + expectLeaf( "depth3-leaf2", "depth3-leaf2-value" ) + ), + expectLeaf( "depth2-leaf1", "depth2-leaf1-value" ) + ) ); + + // Test operational + + CompositeNode depth2Cont1 = toCompositeNode( + toCompositeNodeData( toNestedQName( "depth2-cont1" ), + toCompositeNodeData( toNestedQName( "depth3-cont1" ), + toCompositeNodeData( toNestedQName( "depth4-cont1" ), + toSimpleNodeData( toNestedQName( "depth5-leaf1" ), "depth5-leaf1-value" ) + ), + toSimpleNodeData( toNestedQName( "depth4-leaf1" ), "depth4-leaf1-value" ) + ), + toSimpleNodeData( toNestedQName( "depth3-leaf1" ), "depth3-leaf1-value" ) + ) ); + + when( brokerFacade.readOperationalData( any( InstanceIdentifier.class ) ) ) + .thenReturn( depth2Cont1 ); + + response = target( "/operational/nested-module:depth1-cont/depth2-cont1" ) + .queryParam( "depth", "3" ).request( "application/xml" ).get(); + + verifyXMLResponse( response, + expectContainer( "depth2-cont1", + expectContainer( "depth3-cont1", + expectEmptyContainer( "depth4-cont1" ), + expectLeaf( "depth4-leaf1", "depth4-leaf1-value" ) + ), + expectLeaf( "depth3-leaf1", "depth3-leaf1-value" ) + ) ); + } + + @Test + public void getDataWithInvalidDepthParameterTest() { + + ControllerContext.getInstance().setGlobalSchema( schemaContextModules ); + + final MultivaluedMap paramMap = new MultivaluedHashMap<>(); + paramMap.putSingle( "depth", "1o" ); + UriInfo mockInfo = mock( UriInfo.class ); + when( mockInfo.getQueryParameters( false ) ).thenAnswer( + new Answer>() { + @Override + public MultivaluedMap answer( InvocationOnMock invocation ) { + return paramMap; + } + } ); + + getDataWithInvalidDepthParameterTest( mockInfo ); + + paramMap.putSingle( "depth", "0" ); + getDataWithInvalidDepthParameterTest( mockInfo ); + + paramMap.putSingle( "depth", "-1" ); + getDataWithInvalidDepthParameterTest( mockInfo ); + } + + private void getDataWithInvalidDepthParameterTest( UriInfo uriInfo ) { + try { + restconfImpl.readConfigurationData( "nested-module:depth1-cont", uriInfo ); + fail( "Expected RestconfDocumentedException" ); + } + catch( RestconfDocumentedException e ) { + assertTrue( "Unexpected error message: " + e.getErrors().get( 0 ).getErrorMessage(), + e.getErrors().get( 0 ).getErrorMessage().contains( "depth" ) ); + } + } + + private void verifyXMLResponse( Response response, NodeData nodeData ) { + + Document doc = TestUtils.loadDocumentFrom( (InputStream) response.getEntity() ); + assertNotNull( "Could not parse XML document", doc ); + + //System.out.println(TestUtils.getDocumentInPrintableForm( doc )); + + verifyContainerElement( doc.getDocumentElement(), nodeData ); + } + + @SuppressWarnings("unchecked") + private void verifyContainerElement( Element element, NodeData nodeData ) { + + assertEquals( "Element local name", nodeData.key, element.getNodeName() ); + + NodeList childNodes = element.getChildNodes(); + if( nodeData.data == null ) { // empty container + assertTrue( "Expected no child elements for \"" + element.getNodeName() + "\"", + childNodes.getLength() == 0 ); + return; + } + + Map expChildMap = Maps.newHashMap(); + for( NodeData expChild: (List)nodeData.data ) { + expChildMap.put( expChild.key.toString(), expChild ); + } + + for( int i = 0; i < childNodes.getLength(); i++ ) { + org.w3c.dom.Node actualChild = childNodes.item( i ); + if( !( actualChild instanceof Element ) ) { + continue; + } + + Element actualElement = (Element)actualChild; + NodeData expChild = expChildMap.remove( actualElement.getNodeName() ); + assertNotNull( "Unexpected child element for parent \"" + element.getNodeName() + + "\": " + actualElement.getNodeName(), expChild ); + + if( expChild.data == null || expChild.data instanceof List ) { + verifyContainerElement( actualElement, expChild ); + } + else { + assertEquals( "Text content for element: " + actualElement.getNodeName(), + expChild.data, actualElement.getTextContent() ); + } + } + + if( !expChildMap.isEmpty() ) { + fail( "Missing elements for parent \"" + element.getNodeName() + + "\": " + expChildMap.keySet() ); + } + } + + private NodeData expectContainer( String name, NodeData... childData ) { + return new NodeData( name, Lists.newArrayList( childData ) ); + } + + private NodeData expectEmptyContainer( String name ) { + return new NodeData( name, null ); + } + + private NodeData expectLeaf( String name, Object value ) { + return new NodeData( name, value ); + } + + private QName toNestedQName( String localName ) { + return QName.create( "urn:nested:module", "2014-06-3", localName ); + } + + @SuppressWarnings("unchecked") + private CompositeNode toCompositeNode( NodeData nodeData ) { + CompositeNodeBuilder builder = ImmutableCompositeNode.builder(); + builder.setQName( (QName) nodeData.key ); + + for( NodeData child: (List)nodeData.data ) { + if( child.data instanceof List ) { + builder.add( toCompositeNode( child ) ); + } + else { + builder.addLeaf( (QName) child.key, child.data ); + } + } + + return builder.toInstance(); + } + + private NodeData toCompositeNodeData( QName key, NodeData... childData ) { + return new NodeData( key, Lists.newArrayList( childData ) ); + } + + private NodeData toSimpleNodeData( QName key, Object value ) { + return new NodeData( key, value ); + } } diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java index 3f984c293b..e146cf8f4f 100644 --- a/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java +++ b/opendaylight/md-sal/sal-rest-connector/src/test/java/org/opendaylight/controller/sal/restconf/impl/test/RestconfDocumentedExceptionMapperTest.java @@ -34,6 +34,7 @@ import javax.ws.rs.core.Application; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriInfo; import javax.xml.namespace.NamespaceContext; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; @@ -220,7 +221,7 @@ public class RestconfDocumentedExceptionMapperTest extends JerseyTest { void stageMockEx( final RestconfDocumentedException ex ) { reset( mockRestConf ); - when( mockRestConf.readOperationalData( any( String.class ) ) ).thenThrow( ex ); + when( mockRestConf.readOperationalData( any( String.class ), any( UriInfo.class ) ) ).thenThrow( ex ); } void testJsonResponse( final RestconfDocumentedException ex, final Status expStatus, final ErrorType expErrorType, @@ -776,8 +777,8 @@ public class RestconfDocumentedExceptionMapperTest extends JerseyTest { // The StructuredDataToJsonProvider should throw a RestconfDocumentedException with no data - when( mockRestConf.readOperationalData( any( String.class ) ) ) - .thenReturn( new StructuredData( null, null, null ) ); + when( mockRestConf.readOperationalData( any( String.class ), any( UriInfo.class ) ) ) + .thenReturn( new StructuredData( null, null, null ) ); Response resp = target("/operational/foo").request( MediaType.APPLICATION_JSON ).get(); diff --git a/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/nested-module.yang b/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/nested-module.yang new file mode 100644 index 0000000000..590743c9ca --- /dev/null +++ b/opendaylight/md-sal/sal-rest-connector/src/test/resources/modules/nested-module.yang @@ -0,0 +1,47 @@ +module nested-module { + namespace "urn:nested:module"; + prefix "nested"; + revision "2014-06-3"; + + container depth1-cont { + container depth2-cont1 { + container depth3-cont1 { + container depth4-cont1 { + leaf depth5-leaf1 { + type string; + } + } + + leaf depth4-leaf1 { + type string; + } + } + + leaf depth3-leaf1 { + type string; + } + } + + container depth2-cont2 { + container depth3-cont2 { + container depth4-cont2 { + leaf depth5-leaf2 { + type string; + } + } + + leaf depth4-leaf2 { + type string; + } + } + + leaf depth3-leaf2 { + type string; + } + } + + leaf depth2-leaf1 { + type string; + } + } +} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-docgen/pom.xml b/opendaylight/md-sal/sal-rest-docgen/pom.xml index 8954c05811..79a3434c49 100644 --- a/opendaylight/md-sal/sal-rest-docgen/pom.xml +++ b/opendaylight/md-sal/sal-rest-docgen/pom.xml @@ -33,6 +33,13 @@ guava + + org.apache.commons + commons-lang3 + 3.3.2 + + + org.jboss.resteasy @@ -86,6 +93,11 @@ junit test + + org.mockito + mockito-all + test + diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/DocProvider.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/DocProvider.java index 934412d95a..31f4253318 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/DocProvider.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/DocProvider.java @@ -9,11 +9,17 @@ package org.opendaylight.controller.sal.rest.doc; import java.util.Collection; import java.util.Collections; +import java.util.LinkedList; +import java.util.List; import org.opendaylight.controller.sal.core.api.Broker; import org.opendaylight.controller.sal.core.api.Provider; import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.opendaylight.controller.sal.core.api.mount.MountProvisionService; +import org.opendaylight.controller.sal.core.api.mount.MountProvisionService.MountProvisionListener; import org.opendaylight.controller.sal.rest.doc.impl.ApiDocGenerator; +import org.opendaylight.controller.sal.rest.doc.mountpoints.MountPointSwagger; +import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; @@ -22,24 +28,37 @@ import org.osgi.util.tracker.ServiceTrackerCustomizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class DocProvider implements BundleActivator, ServiceTrackerCustomizer, Provider, AutoCloseable { +public class DocProvider implements BundleActivator, ServiceTrackerCustomizer, + Provider, AutoCloseable { - private static final Logger _logger = LoggerFactory.getLogger(DocProvider.class); + private final Logger _logger = LoggerFactory.getLogger(DocProvider.class); private ServiceTracker brokerServiceTracker; private BundleContext bundleContext; private Broker.ProviderSession session; + private final List toClose = new LinkedList<>(); + @Override public void close() throws Exception { stop(bundleContext); } @Override - public void onSessionInitiated(final Broker.ProviderSession providerSession) { + public void onSessionInitiated(Broker.ProviderSession providerSession) { SchemaService schemaService = providerSession.getService(SchemaService.class); ApiDocGenerator.getInstance().setSchemaService(schemaService); + MountProvisionService mountService = providerSession + .getService(MountProvisionService.class); + ListenerRegistration registration = mountService + .registerProvisionListener(MountPointSwagger.getInstance()); + MountPointSwagger.getInstance().setGlobalSchema(schemaService); + synchronized (toClose) { + toClose.add(registration); + } + MountPointSwagger.getInstance().setMountService(mountService); + _logger.debug("Restconf API Explorer started"); } @@ -49,42 +68,45 @@ public class DocProvider implements BundleActivator, ServiceTrackerCustomizer
(context, Broker.class, this); + brokerServiceTracker = new ServiceTracker(context, Broker.class, this); brokerServiceTracker.open(); } @Override - public void stop(final BundleContext context) throws Exception { - if (brokerServiceTracker != null) { + public void stop(BundleContext context) throws Exception { + if (brokerServiceTracker != null) brokerServiceTracker.close(); - } - if (session != null) { + if (session != null) session.close(); + + synchronized (toClose) { + for (AutoCloseable close : toClose) { + close.close(); + } } } @Override - public Broker addingService(final ServiceReference reference) { + public Broker addingService(ServiceReference reference) { Broker broker = bundleContext.getService(reference); session = broker.registerProvider(this, bundleContext); return broker; } @Override - public void modifiedService(final ServiceReference reference, final Broker service) { - if (session != null) { + public void modifiedService(ServiceReference reference, Broker service) { + if (session != null) session.close(); - } Broker broker = bundleContext.getService(reference); session = broker.registerProvider(this, bundleContext); } @Override - public void removedService(final ServiceReference reference, final Broker service) { + public void removedService(ServiceReference reference, Broker service) { bundleContext.ungetService(reference); } } diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/api/ApiDocService.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/api/ApiDocService.java index 3542d6aadc..2646a6a245 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/api/ApiDocService.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/api/ApiDocService.java @@ -16,47 +16,83 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; /** - * This service generates swagger - * (See https://helloreverb.com/developers/swagger) - * compliant documentation for RESTCONF APIs. The output of this is used by embedded Swagger UI. + * This service generates swagger (See https://helloreverb.com/developers/swagger) compliant documentation for + * RESTCONF APIs. The output of this is used by embedded Swagger UI. */ @Path("/") public interface ApiDocService { - /** - * Generates index document for Swagger UI. This document lists out all modules with link to get APIs for - * each module. The API for each module is served by getDocByModule() method. - * - * @param uriInfo - * @return - */ - @GET - @Produces(MediaType.APPLICATION_JSON) - public Response getRootDoc(@Context javax.ws.rs.core.UriInfo uriInfo); - - /** - * Generates Swagger compliant document listing APIs for module. - * - * @param module - * @param revision - * @param uriInfo - * @return - */ - @GET - @Path("/{module},{revision}") - @Produces(MediaType.APPLICATION_JSON) - public Response getDocByModule(@PathParam("module") String module, - @PathParam("revision") String revision, - @Context javax.ws.rs.core.UriInfo uriInfo); - - /** - * Redirects to embedded swagger ui. - * - * @param uriInfo - * @return - */ - @GET - @Path("/ui") - @Produces(MediaType.TEXT_HTML) - public Response getApiExplorer(@Context javax.ws.rs.core.UriInfo uriInfo); + /** + * Generates index document for Swagger UI. This document lists out all + * modules with link to get APIs for each module. The API for each module is + * served by getDocByModule() method. + * + * @param uriInfo + * @return + */ + @GET + @Produces(MediaType.APPLICATION_JSON) + public Response getRootDoc(@Context javax.ws.rs.core.UriInfo uriInfo); + + /** + * Generates Swagger compliant document listing APIs for module. + * + * @param module + * @param revision + * @param uriInfo + * @return + */ + @GET + @Path("/{module}({revision})") + @Produces(MediaType.APPLICATION_JSON) + public Response getDocByModule(@PathParam("module") String module, + @PathParam("revision") String revision, @Context javax.ws.rs.core.UriInfo uriInfo); + + /** + * Redirects to embedded swagger ui. + * + * @param uriInfo + * @return + */ + @GET + @Path("/ui") + @Produces(MediaType.TEXT_HTML) + public Response getApiExplorer(@Context javax.ws.rs.core.UriInfo uriInfo); + + /** + * Generates index document for Swagger UI. This document lists out all + * modules with link to get APIs for each module. The API for each module is + * served by getDocByModule() method. + * + * @param uriInfo + * @return + */ + @GET + @Path("/mounts") + @Produces(MediaType.APPLICATION_JSON) + public Response getListOfMounts(@Context javax.ws.rs.core.UriInfo uriInfo); + + @GET + @Path("/mounts/{instance}") + @Produces(MediaType.APPLICATION_JSON) + public Response getMountRootDoc(@PathParam("instance") String instanceNum, + @Context javax.ws.rs.core.UriInfo uriInfo); + + /** + * Generates Swagger compliant document listing APIs for module. + * + * @param module + * @param revision + * @param uriInfo + * @return + */ + @GET + @Path("/mounts/{instance}/{module}({revision})") + @Produces(MediaType.APPLICATION_JSON) + public Response getMountDocByModule(@PathParam("instance") String instanceNum, + @PathParam("module") String module, @PathParam("revision") String revision, + @Context javax.ws.rs.core.UriInfo uriInfo); + } diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ApiDocGenerator.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ApiDocGenerator.java index bcd11bcb06..82409d2e40 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ApiDocGenerator.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ApiDocGenerator.java @@ -7,76 +7,44 @@ */ package org.opendaylight.controller.sal.rest.doc.impl; -import java.io.IOException; -import java.net.URI; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - import javax.ws.rs.core.UriInfo; -import org.json.JSONException; -import org.json.JSONObject; import org.opendaylight.controller.sal.core.api.model.SchemaService; -import org.opendaylight.controller.sal.rest.doc.model.builder.OperationBuilder; -import org.opendaylight.controller.sal.rest.doc.swagger.Api; import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration; -import org.opendaylight.controller.sal.rest.doc.swagger.Operation; -import org.opendaylight.controller.sal.rest.doc.swagger.Parameter; -import org.opendaylight.controller.sal.rest.doc.swagger.Resource; import org.opendaylight.controller.sal.rest.doc.swagger.ResourceList; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; -import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; -import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; -import org.opendaylight.yangtools.yang.model.api.Module; -import org.opendaylight.yangtools.yang.model.api.RpcDefinition; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule; import com.google.common.base.Preconditions; /** - * This class gathers all yang defined {@link Module}s and generates Swagger compliant documentation. + * This class gathers all yang defined {@link Module}s and generates Swagger + * compliant documentation. */ -public class ApiDocGenerator { +public class ApiDocGenerator extends BaseYangSwaggerGenerator { - private static final Logger _logger = LoggerFactory.getLogger(ApiDocGenerator.class); + private static Logger _logger = LoggerFactory.getLogger(ApiDocGenerator.class); private static final ApiDocGenerator INSTANCE = new ApiDocGenerator(); - private final ObjectMapper mapper = new ObjectMapper(); - private final ModelGenerator jsonConverter = new ModelGenerator(); - private SchemaService schemaService; - private static final String API_VERSION = "1.0.0"; - private static final String SWAGGER_VERSION = "1.2"; - private static final String RESTCONF_CONTEXT_ROOT = "restconf"; - private final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); - - //For now its {@link HashMap}. It will be changed to thread-safe Map when schema change listener is implemented. - private final Map MODULE_DOC_CACHE = new HashMap(); + public ResourceList getResourceListing(UriInfo uriInfo) { + Preconditions.checkState(schemaService != null); + SchemaContext schemaContext = schemaService.getGlobalContext(); + Preconditions.checkState(schemaContext != null); + return super.getResourceListing(uriInfo, schemaContext, ""); + } - private ApiDocGenerator(){ - mapper.registerModule(new JsonOrgModule()); - mapper.configure(SerializationFeature.INDENT_OUTPUT, true); + public ApiDeclaration getApiDeclaration(String module, String revision, UriInfo uriInfo) { + SchemaContext schemaContext = schemaService.getGlobalContext(); + Preconditions.checkState(schemaContext != null); + return super.getApiDeclaration(module, revision, uriInfo, schemaContext, ""); } /** * Returns singleton instance + * * @return */ public static ApiDocGenerator getInstance() { @@ -87,240 +55,7 @@ public class ApiDocGenerator { * * @param schemaService */ - public void setSchemaService(final SchemaService schemaService) { + public void setSchemaService(SchemaService schemaService) { this.schemaService = schemaService; } - /** - * - * @param uriInfo - * @return list of modules converted to swagger compliant resource list. - */ - public ResourceList getResourceListing(final UriInfo uriInfo) { - - Preconditions.checkState(schemaService != null); - SchemaContext schemaContext = schemaService.getGlobalContext(); - Preconditions.checkState(schemaContext != null); - - Set modules = schemaContext.getModules(); - - ResourceList resourceList = new ResourceList(); - resourceList.setApiVersion(API_VERSION); - resourceList.setSwaggerVersion(SWAGGER_VERSION); - - List resources = new ArrayList<>(modules.size()); - _logger.info("Modules found [{}]", modules.size()); - - for (Module module : modules) { - Resource resource = new Resource(); - String revisionString = SIMPLE_DATE_FORMAT.format(module.getRevision()); - - _logger.debug("Working on [{},{}]...", module.getName(), revisionString); - ApiDeclaration doc = getApiDeclaration(module.getName(), revisionString, uriInfo); - - if (doc != null) { - URI uri = uriInfo.getRequestUriBuilder(). - path(generateCacheKey(module.getName(), revisionString)). - build(); - - resource.setPath(uri.toASCIIString()); - resources.add(resource); - } else { - _logger.debug("Could not generate doc for {},{}", module.getName(), revisionString); - } - } - - resourceList.setApis(resources); - - return resourceList; - } - - public ApiDeclaration getApiDeclaration(final String module, final String revision, final UriInfo uriInfo) { - - //Lookup cache - String cacheKey = generateCacheKey(module, revision); - - if (MODULE_DOC_CACHE.containsKey(cacheKey)) { - _logger.debug("Serving from cache for {}", cacheKey); - return MODULE_DOC_CACHE.get(cacheKey); - } - - Date rev = null; - try { - rev = SIMPLE_DATE_FORMAT.parse(revision); - } catch (ParseException e) { - throw new IllegalArgumentException(e); - } - - SchemaContext schemaContext = schemaService.getGlobalContext(); - Preconditions.checkState(schemaContext != null); - - Module m = schemaContext.findModuleByName(module, rev); - Preconditions.checkArgument(m != null, "Could not find module by name,revision: " + module + "," + revision); - - String basePath = new StringBuilder(uriInfo.getBaseUri().getScheme()) - .append("://") - .append(uriInfo.getBaseUri().getHost()) - .append(":") - .append(uriInfo.getBaseUri().getPort()) - .append("/") - .append(RESTCONF_CONTEXT_ROOT) - .toString(); - - ApiDeclaration doc = getSwaggerDocSpec(m, basePath); - MODULE_DOC_CACHE.put(cacheKey, doc); - return doc; - } - - public ApiDeclaration getSwaggerDocSpec(final Module m, final String basePath) { - ApiDeclaration doc = new ApiDeclaration(); - doc.setApiVersion(API_VERSION); - doc.setSwaggerVersion(SWAGGER_VERSION); - doc.setBasePath(basePath); - doc.setProduces(Arrays.asList("application/json", "application/xml")); - - List apis = new ArrayList(); - - Set dataSchemaNodes = m.getChildNodes(); - _logger.debug("child nodes size [{}]", dataSchemaNodes.size()); - for (DataSchemaNode node : dataSchemaNodes) { - if ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode)) { - - _logger.debug("Is Configuration node [{}] [{}]", node.isConfiguration(), node.getQName().getLocalName()); - - List pathParams = null; - if (node.isConfiguration()) { - pathParams = new ArrayList(); - String resourcePath = "/config/" + m.getName() + ":"; - addApis(node, apis, resourcePath, pathParams, true); - - } - - pathParams = new ArrayList(); - String resourcePath = "/operational/" + m.getName() + ":"; - addApis(node, apis, resourcePath, pathParams, false); - } - } - - Set rpcs = m.getRpcs(); - for (RpcDefinition rpcDefinition : rpcs) { - String resourcePath = "/operations/" + m.getName() + ":"; - addRpcs(rpcDefinition, apis, resourcePath); - - } - _logger.debug("Number of APIs found [{}]", apis.size()); - doc.setApis(apis); - JSONObject models = null; - - try { - models = jsonConverter.convertToJsonSchema(m); - doc.setModels(models); - _logger.debug(mapper.writeValueAsString(doc)); - } catch (IOException | JSONException e) { - e.printStackTrace(); - } - - return doc; - } - - private String generateCacheKey(final Module m) { - return generateCacheKey(m.getName(), SIMPLE_DATE_FORMAT.format(m.getRevision())); - } - - private String generateCacheKey(final String module, final String revision) { - return module + "," + revision; - } - - private void addApis(final DataSchemaNode node, - final List apis, - final String parentPath, - final List parentPathParams, - final boolean addConfigApi) { - - Api api = new Api(); - List pathParams = new ArrayList(parentPathParams); - - String resourcePath = parentPath + createPath(node, pathParams) + "/"; - _logger.debug("Adding path: [{}]", resourcePath); - api.setPath(resourcePath); - api.setOperations(operations(node, pathParams, addConfigApi)); - apis.add(api); - if ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode)) { - DataNodeContainer schemaNode = (DataNodeContainer) node; - Set dataSchemaNodes = schemaNode.getChildNodes(); - - for (DataSchemaNode childNode : dataSchemaNodes) { - addApis(childNode, apis, resourcePath, pathParams, addConfigApi); - } - } - - } - - private void addRpcs(final RpcDefinition rpcDefn, final List apis, final String parentPath) { - Api rpc = new Api(); - String resourcePath = parentPath + rpcDefn.getQName().getLocalName(); - rpc.setPath(resourcePath); - - Operation operationSpec = new Operation(); - operationSpec.setMethod("POST"); - operationSpec.setNotes(rpcDefn.getDescription()); - operationSpec.setNickname(rpcDefn.getQName().getLocalName()); - rpc.setOperations(Arrays.asList(operationSpec)); - - apis.add(rpc); - } - - /** - * @param node - * @param pathParams - * @return - */ - private List operations(final DataSchemaNode node, final List pathParams, final boolean isConfig) { - List operations = new ArrayList<>(); - - OperationBuilder.Get getBuilder = new OperationBuilder.Get(node); - operations.add(getBuilder.pathParams(pathParams).build()); - - if (isConfig) { - OperationBuilder.Post postBuilder = new OperationBuilder.Post(node); - operations.add(postBuilder.pathParams(pathParams).build()); - - OperationBuilder.Put putBuilder = new OperationBuilder.Put(node); - operations.add(putBuilder.pathParams(pathParams).build()); - - OperationBuilder.Delete deleteBuilder = new OperationBuilder.Delete(node); - operations.add(deleteBuilder.pathParams(pathParams).build()); - } - return operations; - } - - private String createPath(final DataSchemaNode schemaNode, final List pathParams) { - ArrayList pathListParams = new ArrayList(); - StringBuilder path = new StringBuilder(); - QName _qName = schemaNode.getQName(); - String localName = _qName.getLocalName(); - path.append(localName); - - if ((schemaNode instanceof ListSchemaNode)) { - final List listKeys = ((ListSchemaNode) schemaNode).getKeyDefinition(); - for (final QName listKey : listKeys) { - { - DataSchemaNode _dataChildByName = ((DataNodeContainer) schemaNode).getDataChildByName(listKey); - pathListParams.add(((LeafSchemaNode) _dataChildByName)); - - String pathParamIdentifier = new StringBuilder("/{").append(listKey.getLocalName()).append("}").toString(); - path.append(pathParamIdentifier); - - Parameter pathParam = new Parameter(); - pathParam.setName(listKey.getLocalName()); - pathParam.setDescription(_dataChildByName.getDescription()); - pathParam.setType("string"); - pathParam.setParamType("path"); - - pathParams.add(pathParam); - } - } - } - return path.toString(); - } - } diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ApiDocServiceImpl.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ApiDocServiceImpl.java index 241c8b84be..c95f41c0bd 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ApiDocServiceImpl.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ApiDocServiceImpl.java @@ -7,66 +7,118 @@ */ package org.opendaylight.controller.sal.rest.doc.impl; -import org.opendaylight.controller.sal.rest.doc.api.ApiDocService; -import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration; -import org.opendaylight.controller.sal.rest.doc.swagger.ResourceList; +import java.io.ByteArrayOutputStream; +import java.io.OutputStreamWriter; +import java.util.Map.Entry; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriInfo; +import org.json.JSONWriter; +import org.opendaylight.controller.sal.rest.doc.api.ApiDocService; +import org.opendaylight.controller.sal.rest.doc.mountpoints.MountPointSwagger; +import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration; +import org.opendaylight.controller.sal.rest.doc.swagger.ResourceList; + /** - * This service generates swagger - * (See https://helloreverb.com/developers/swagger) - * compliant documentation for RESTCONF APIs. The output of this is used by embedded Swagger UI. + * This service generates swagger (See https://helloreverb.com/developers/swagger) compliant documentation for + * RESTCONF APIs. The output of this is used by embedded Swagger UI. + * + * NOTE: These API's need to be synchronized due to bug 1198. Thread access to + * the SchemaContext is not synchronized properly and thus you can end up with + * missing definitions without this synchronization. There are likely otherways + * to work around this limitation, but given that this API is a dev only tool + * and not dependent UI, this was the fastest work around. + * */ public class ApiDocServiceImpl implements ApiDocService { - private static final ApiDocService INSTANCE = new ApiDocServiceImpl(); + private static final ApiDocService INSTANCE = new ApiDocServiceImpl(); + + public static ApiDocService getInstance() { + return INSTANCE; + } + + /** + * Generates index document for Swagger UI. This document lists out all + * modules with link to get APIs for each module. The API for each module is + * served by getDocByModule() method. + * + * @param uriInfo + * @return + */ + @Override + public synchronized Response getRootDoc(UriInfo uriInfo) { + ApiDocGenerator generator = ApiDocGenerator.getInstance(); + ResourceList rootDoc = generator.getResourceListing(uriInfo); + + return Response.ok(rootDoc).build(); + } - public static ApiDocService getInstance(){ - return INSTANCE; - } + /** + * Generates Swagger compliant document listing APIs for module. + * + * @param module + * @param revision + * @param uriInfo + * @return + */ + @Override + public synchronized Response getDocByModule(String module, String revision, UriInfo uriInfo) { + ApiDocGenerator generator = ApiDocGenerator.getInstance(); - /** - * Generates index document for Swagger UI. This document lists out all modules with link to get APIs for - * each module. The API for each module is served by getDocByModule() method. - * - * @param uriInfo - * @return - */ - @Override - public Response getRootDoc(UriInfo uriInfo) { + ApiDeclaration doc = generator.getApiDeclaration(module, revision, uriInfo); + return Response.ok(doc).build(); + } - ApiDocGenerator generator = ApiDocGenerator.getInstance(); - ResourceList rootDoc = generator.getResourceListing(uriInfo); + /** + * Redirects to embedded swagger ui. + * + * @param uriInfo + * @return + */ + @Override + public synchronized Response getApiExplorer(UriInfo uriInfo) { + return Response + .seeOther(uriInfo.getBaseUriBuilder().path("../explorer/index.html").build()) + .build(); + } - return Response.ok(rootDoc).build(); - } + @Override + public synchronized Response getListOfMounts(UriInfo uriInfo) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (OutputStreamWriter streamWriter = new OutputStreamWriter(baos)) { + JSONWriter writer = new JSONWriter(streamWriter); + writer.array(); + for (Entry entry : MountPointSwagger.getInstance() + .getInstanceIdentifiers().entrySet()) { + writer.object(); + writer.key("instance").value(entry.getKey()); + writer.key("id").value(entry.getValue()); + writer.endObject(); + } + writer.endArray(); + } catch (Exception e) { + return Response.status(500).entity(e.getMessage()).build(); + } + return Response.status(200).entity(baos.toString()).build(); + } - /** - * Generates Swagger compliant document listing APIs for module. - * - * @param module - * @param revision - * @param uriInfo - * @return - */ - @Override - public Response getDocByModule(String module, String revision, UriInfo uriInfo) { - ApiDocGenerator generator = ApiDocGenerator.getInstance(); + @Override + public synchronized Response getMountRootDoc(String instanceNum, UriInfo uriInfo) { + ResourceList resourceList = MountPointSwagger.getInstance().getResourceList(uriInfo, + Long.parseLong(instanceNum)); + return Response.ok(resourceList).build(); + } - ApiDeclaration doc = generator.getApiDeclaration(module, revision, uriInfo); - return Response.ok(doc).build(); - } + @Override + public synchronized Response getMountDocByModule(String instanceNum, String module, + String revision, UriInfo uriInfo) { + ApiDeclaration api = MountPointSwagger.getInstance().getMountPointApi(uriInfo, + Long.parseLong(instanceNum), module, revision); + return Response.ok(api).build(); + } - /** - * Redirects to embedded swagger ui. - * - * @param uriInfo - * @return - */ - @Override - public Response getApiExplorer(UriInfo uriInfo) { - return Response.seeOther(uriInfo.getBaseUriBuilder().path("../explorer/index.html").build()).build(); - } } diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/BaseYangSwaggerGenerator.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/BaseYangSwaggerGenerator.java new file mode 100644 index 0000000000..68d31de8da --- /dev/null +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/BaseYangSwaggerGenerator.java @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.rest.doc.impl; + +import java.io.IOException; +import java.net.URI; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import javax.ws.rs.core.UriInfo; + +import org.json.JSONException; +import org.json.JSONObject; +import org.opendaylight.controller.sal.rest.doc.model.builder.OperationBuilder; +import org.opendaylight.controller.sal.rest.doc.swagger.Api; +import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration; +import org.opendaylight.controller.sal.rest.doc.swagger.Operation; +import org.opendaylight.controller.sal.rest.doc.swagger.Parameter; +import org.opendaylight.controller.sal.rest.doc.swagger.Resource; +import org.opendaylight.controller.sal.rest.doc.swagger.ResourceList; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.RpcDefinition; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule; +import com.google.common.base.Preconditions; + +public class BaseYangSwaggerGenerator { + + private static Logger _logger = LoggerFactory.getLogger(BaseYangSwaggerGenerator.class); + + protected static final String API_VERSION = "1.0.0"; + protected static final String SWAGGER_VERSION = "1.2"; + protected static final String RESTCONF_CONTEXT_ROOT = "restconf"; + protected final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); + private final ModelGenerator jsonConverter = new ModelGenerator(); + + // private Map MODULE_DOC_CACHE = new HashMap<>() + private final ObjectMapper mapper = new ObjectMapper(); + + protected BaseYangSwaggerGenerator() { + mapper.registerModule(new JsonOrgModule()); + mapper.configure(SerializationFeature.INDENT_OUTPUT, true); + } + + /** + * + * @param uriInfo + * @param operType + * @return list of modules converted to swagger compliant resource list. + */ + public ResourceList getResourceListing(UriInfo uriInfo, SchemaContext schemaContext, + String context) { + + ResourceList resourceList = createResourceList(); + + Set modules = getSortedModules(schemaContext); + + List resources = new ArrayList<>(modules.size()); + + _logger.info("Modules found [{}]", modules.size()); + + for (Module module : modules) { + String revisionString = SIMPLE_DATE_FORMAT.format(module.getRevision()); + + Resource resource = new Resource(); + _logger.debug("Working on [{},{}]...", module.getName(), revisionString); + ApiDeclaration doc = getApiDeclaration(module.getName(), revisionString, uriInfo, + schemaContext, context); + + if (doc != null) { + resource.setPath(generatePath(uriInfo, module.getName(), revisionString)); + resources.add(resource); + } else { + _logger.debug("Could not generate doc for {},{}", module.getName(), revisionString); + } + } + + resourceList.setApis(resources); + + return resourceList; + } + + protected ResourceList createResourceList() { + ResourceList resourceList = new ResourceList(); + resourceList.setApiVersion(API_VERSION); + resourceList.setSwaggerVersion(SWAGGER_VERSION); + return resourceList; + } + + protected String generatePath(UriInfo uriInfo, String name, String revision) { + URI uri = uriInfo.getRequestUriBuilder().path(generateCacheKey(name, revision)).build(); + return uri.toASCIIString(); + } + + public ApiDeclaration getApiDeclaration(String module, String revision, UriInfo uriInfo, + SchemaContext schemaContext, String context) { + Date rev = null; + try { + rev = SIMPLE_DATE_FORMAT.parse(revision); + } catch (ParseException e) { + throw new IllegalArgumentException(e); + } + Module m = schemaContext.findModuleByName(module, rev); + Preconditions.checkArgument(m != null, "Could not find module by name,revision: " + module + + "," + revision); + + return getApiDeclaration(m, rev, uriInfo, schemaContext, context); + } + + public ApiDeclaration getApiDeclaration(Module module, Date revision, UriInfo uriInfo, + SchemaContext schemaContext, String context) { + String basePath = createBasePathFromUriInfo(uriInfo); + + ApiDeclaration doc = getSwaggerDocSpec(module, basePath, context); + if (doc != null) { + return doc; + } + return null; + } + + protected String createBasePathFromUriInfo(UriInfo uriInfo) { + String portPart = ""; + int port = uriInfo.getBaseUri().getPort(); + if (port != -1) { + portPart = ":" + port; + } + String basePath = new StringBuilder(uriInfo.getBaseUri().getScheme()).append("://") + .append(uriInfo.getBaseUri().getHost()).append(portPart).append("/") + .append(RESTCONF_CONTEXT_ROOT).toString(); + return basePath; + } + + public ApiDeclaration getSwaggerDocSpec(Module m, String basePath, String context) { + ApiDeclaration doc = createApiDeclaration(basePath); + + List apis = new ArrayList(); + + Set dataSchemaNodes = m.getChildNodes(); + _logger.debug("child nodes size [{}]", dataSchemaNodes.size()); + for (DataSchemaNode node : dataSchemaNodes) { + if ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode)) { + + _logger.debug("Is Configuration node [{}] [{}]", node.isConfiguration(), node + .getQName().getLocalName()); + + List pathParams = new ArrayList(); + String resourcePath = getDataStorePath("/config/", context) + m.getName() + ":"; + addApis(node, apis, resourcePath, pathParams, true); + + pathParams = new ArrayList(); + resourcePath = getDataStorePath("/operational/", context) + m.getName() + ":"; + addApis(node, apis, resourcePath, pathParams, false); + } + + Set rpcs = m.getRpcs(); + for (RpcDefinition rpcDefinition : rpcs) { + String resourcePath = getDataStorePath("/operations/", context) + m.getName() + ":"; + addRpcs(rpcDefinition, apis, resourcePath); + } + } + + _logger.debug("Number of APIs found [{}]", apis.size()); + + if (!apis.isEmpty()) { + doc.setApis(apis); + JSONObject models = null; + + try { + models = jsonConverter.convertToJsonSchema(m); + doc.setModels(models); + if (_logger.isDebugEnabled()) { + _logger.debug(mapper.writeValueAsString(doc)); + } + } catch (IOException | JSONException e) { + e.printStackTrace(); + } + + return doc; + } + return null; + } + + protected ApiDeclaration createApiDeclaration(String basePath) { + ApiDeclaration doc = new ApiDeclaration(); + doc.setApiVersion(API_VERSION); + doc.setSwaggerVersion(SWAGGER_VERSION); + doc.setBasePath(basePath); + doc.setProduces(Arrays.asList("application/json", "application/xml")); + return doc; + } + + protected String getDataStorePath(String dataStore, String context) { + return dataStore + context; + } + + private String generateCacheKey(Module m) { + return generateCacheKey(m.getName(), SIMPLE_DATE_FORMAT.format(m.getRevision())); + } + + private String generateCacheKey(String module, String revision) { + return module + "(" + revision + ")"; + } + + private void addApis(DataSchemaNode node, List apis, String parentPath, + List parentPathParams, boolean addConfigApi) { + + Api api = new Api(); + List pathParams = new ArrayList(parentPathParams); + + String resourcePath = parentPath + createPath(node, pathParams) + "/"; + _logger.debug("Adding path: [{}]", resourcePath); + api.setPath(resourcePath); + api.setOperations(operations(node, pathParams, addConfigApi)); + apis.add(api); + if ((node instanceof ListSchemaNode) || (node instanceof ContainerSchemaNode)) { + DataNodeContainer schemaNode = (DataNodeContainer) node; + Set dataSchemaNodes = schemaNode.getChildNodes(); + + for (DataSchemaNode childNode : dataSchemaNodes) { + // We don't support going to leaf nodes today. Only lists and + // containers. + if (childNode instanceof ListSchemaNode || childNode instanceof ContainerSchemaNode) { + // keep config and operation attributes separate. + if (childNode.isConfiguration() == addConfigApi) { + addApis(childNode, apis, resourcePath, pathParams, addConfigApi); + } + } + } + } + + } + + /** + * @param node + * @param pathParams + * @return + */ + private List operations(DataSchemaNode node, List pathParams, + boolean isConfig) { + List operations = new ArrayList<>(); + + OperationBuilder.Get getBuilder = new OperationBuilder.Get(node, isConfig); + operations.add(getBuilder.pathParams(pathParams).build()); + + if (isConfig) { + OperationBuilder.Post postBuilder = new OperationBuilder.Post(node); + operations.add(postBuilder.pathParams(pathParams).build()); + + OperationBuilder.Put putBuilder = new OperationBuilder.Put(node); + operations.add(putBuilder.pathParams(pathParams).build()); + + OperationBuilder.Delete deleteBuilder = new OperationBuilder.Delete(node); + operations.add(deleteBuilder.pathParams(pathParams).build()); + } + return operations; + } + + private String createPath(final DataSchemaNode schemaNode, List pathParams) { + ArrayList pathListParams = new ArrayList(); + StringBuilder path = new StringBuilder(); + QName _qName = schemaNode.getQName(); + String localName = _qName.getLocalName(); + path.append(localName); + + if ((schemaNode instanceof ListSchemaNode)) { + final List listKeys = ((ListSchemaNode) schemaNode).getKeyDefinition(); + for (final QName listKey : listKeys) { + { + DataSchemaNode _dataChildByName = ((DataNodeContainer) schemaNode) + .getDataChildByName(listKey); + pathListParams.add(((LeafSchemaNode) _dataChildByName)); + + String pathParamIdentifier = new StringBuilder("/{") + .append(listKey.getLocalName()).append("}").toString(); + path.append(pathParamIdentifier); + + Parameter pathParam = new Parameter(); + pathParam.setName(listKey.getLocalName()); + pathParam.setDescription(_dataChildByName.getDescription()); + pathParam.setType("string"); + pathParam.setParamType("path"); + + pathParams.add(pathParam); + } + } + } + return path.toString(); + } + + protected void addRpcs(RpcDefinition rpcDefn, List apis, String parentPath) { + Api rpc = new Api(); + String resourcePath = parentPath + rpcDefn.getQName().getLocalName(); + rpc.setPath(resourcePath); + + Operation operationSpec = new Operation(); + operationSpec.setMethod("POST"); + operationSpec.setNotes(rpcDefn.getDescription()); + operationSpec.setNickname(rpcDefn.getQName().getLocalName()); + if (rpcDefn.getOutput() != null) { + operationSpec.setType("(" + rpcDefn.getQName().getLocalName() + ")output"); + } + if (rpcDefn.getInput() != null) { + Parameter payload = new Parameter(); + payload.setParamType("body"); + payload.setType("(" + rpcDefn.getQName().getLocalName() + ")input"); + operationSpec.setParameters(Collections.singletonList(payload)); + } + + rpc.setOperations(Arrays.asList(operationSpec)); + + apis.add(rpc); + } + + protected SortedSet getSortedModules(SchemaContext schemaContext) { + if (schemaContext == null) { + return new TreeSet<>(); + } + + Set modules = schemaContext.getModules(); + + SortedSet sortedModules = new TreeSet<>(new Comparator() { + @Override + public int compare(Module o1, Module o2) { + int result = o1.getName().compareTo(o2.getName()); + if (result == 0) { + result = o1.getRevision().compareTo(o2.getRevision()); + } + if (result == 0) { + result = o1.getNamespace().compareTo(o2.getNamespace()); + } + return result; + } + }); + for (Module m : modules) { + if (m != null) { + sortedModules.add(m); + } + } + return sortedModules; + } +} diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ModelGenerator.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ModelGenerator.java index 719dd78064..0e929afc84 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ModelGenerator.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/impl/ModelGenerator.java @@ -18,6 +18,7 @@ import java.util.Set; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.opendaylight.controller.sal.rest.doc.model.builder.OperationBuilder; import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; import org.opendaylight.yangtools.yang.model.api.ChoiceNode; @@ -58,7 +59,7 @@ import org.slf4j.LoggerFactory; */ public class ModelGenerator { - private static final Logger _logger = LoggerFactory.getLogger(ModelGenerator.class); + private static Logger _logger = LoggerFactory.getLogger(ModelGenerator.class); private static final String BASE_64 = "base64"; private static final String BINARY_ENCODING_KEY = "binaryEncoding"; @@ -88,109 +89,123 @@ public class ModelGenerator { private static final Map>, String> YANG_TYPE_TO_JSON_TYPE_MAPPING; static { - Map>, String> tempMap1 = new HashMap>, String>(10); - tempMap1.put(StringType.class , STRING); - tempMap1.put(BooleanType.class , BOOLEAN); - tempMap1.put(Int8.class , INTEGER); - tempMap1.put(Int16.class , INTEGER); - tempMap1.put(Int32.class , INTEGER); - tempMap1.put(Int64.class , INTEGER); - tempMap1.put(Uint16.class , INTEGER); - tempMap1.put(Uint32.class , INTEGER); - tempMap1.put(Uint64.class , INTEGER); - tempMap1.put(Uint8.class , INTEGER); - tempMap1.put(Decimal64.class , NUMBER); - tempMap1.put(EnumerationType.class , ENUM); - //TODO: Binary type + Map>, String> tempMap1 = new HashMap>, String>( + 10); + tempMap1.put(StringType.class, STRING); + tempMap1.put(BooleanType.class, BOOLEAN); + tempMap1.put(Int8.class, INTEGER); + tempMap1.put(Int16.class, INTEGER); + tempMap1.put(Int32.class, INTEGER); + tempMap1.put(Int64.class, INTEGER); + tempMap1.put(Uint16.class, INTEGER); + tempMap1.put(Uint32.class, INTEGER); + tempMap1.put(Uint64.class, INTEGER); + tempMap1.put(Uint8.class, INTEGER); + tempMap1.put(Decimal64.class, NUMBER); + tempMap1.put(EnumerationType.class, ENUM); + // TODO: Binary type YANG_TYPE_TO_JSON_TYPE_MAPPING = Collections.unmodifiableMap(tempMap1); } - public ModelGenerator(){ + public ModelGenerator() { } - public JSONObject convertToJsonSchema(final Module module) throws IOException, JSONException { + public JSONObject convertToJsonSchema(Module module) throws IOException, JSONException { JSONObject models = new JSONObject(); processContainers(module, models); processRPCs(module, models); - return models; } - - - private void processContainers(final Module module, final JSONObject models) throws IOException, JSONException { + private void processContainers(Module module, JSONObject models) throws IOException, + JSONException { String moduleName = module.getName(); - Set childNodes = module.getChildNodes(); + Set childNodes = module.getChildNodes(); - for(DataSchemaNode childNode : childNodes){ - JSONObject moduleJSON=null; - String filename = childNode.getQName().getLocalName(); + for (DataSchemaNode childNode : childNodes) { + JSONObject configModuleJSON = null; + JSONObject operationalModuleJSON = null; + + String childNodeName = childNode.getQName().getLocalName(); /* * For every container in the module */ - if(childNode instanceof ContainerSchemaNode) { - moduleJSON = processContainer((ContainerSchemaNode)childNode, moduleName, true, models); + if (childNode instanceof ContainerSchemaNode) { + configModuleJSON = processContainer((ContainerSchemaNode) childNode, moduleName, + true, models, true); + operationalModuleJSON = processContainer((ContainerSchemaNode) childNode, + moduleName, true, models, false); } - if(moduleJSON!=null) { - _logger.debug("Adding model for [{}]", filename); - moduleJSON.put("id", filename); - models.put(filename, moduleJSON); + if (configModuleJSON != null) { + _logger.debug("Adding model for [{}]", OperationBuilder.CONFIG + childNodeName); + configModuleJSON.put("id", OperationBuilder.CONFIG + childNodeName); + models.put(OperationBuilder.CONFIG + childNodeName, configModuleJSON); + } + if (operationalModuleJSON != null) { + _logger.debug("Adding model for [{}]", OperationBuilder.OPERATIONAL + childNodeName); + operationalModuleJSON.put("id", OperationBuilder.OPERATIONAL + childNodeName); + models.put(OperationBuilder.OPERATIONAL + childNodeName, operationalModuleJSON); } } } - /** - * Process the RPCs for a Module - * Spits out a file each of the name -input.json - * and -output.json for each RPC that contains + * Process the RPCs for a Module Spits out a file each of the name + * -input.json and -output.json for each RPC that contains * input & output elements * * @param module * @throws JSONException * @throws IOException */ - private void processRPCs(final Module module, final JSONObject models) throws JSONException, IOException { + private void processRPCs(Module module, JSONObject models) throws JSONException, IOException { - Set rpcs = module.getRpcs(); + Set rpcs = module.getRpcs(); String moduleName = module.getName(); - for(RpcDefinition rpc: rpcs) { + for (RpcDefinition rpc : rpcs) { ContainerSchemaNode input = rpc.getInput(); - if(input!=null) { + if (input != null) { JSONObject inputJSON = processContainer(input, moduleName, true, models); - String filename = rpc.getQName().getLocalName() + "-input"; + String filename = "(" + rpc.getQName().getLocalName() + ")input"; inputJSON.put("id", filename); - //writeToFile(filename, inputJSON.toString(2), moduleName); + // writeToFile(filename, inputJSON.toString(2), moduleName); models.put(filename, inputJSON); } ContainerSchemaNode output = rpc.getOutput(); - if(output!=null) { + if (output != null) { JSONObject outputJSON = processContainer(output, moduleName, true, models); - String filename = rpc.getQName().getLocalName() + "-output"; + String filename = "(" + rpc.getQName().getLocalName() + ")output"; outputJSON.put("id", filename); models.put(filename, outputJSON); } } } - /** * Processes the container node and populates the moduleJSON * * @param container * @param moduleName + * @param isConfig * @throws JSONException * @throws IOException */ - private JSONObject processContainer(final ContainerSchemaNode container, final String moduleName, final boolean addSchemaStmt, final JSONObject models) throws JSONException, IOException{ + private JSONObject processContainer(ContainerSchemaNode container, String moduleName, + boolean addSchemaStmt, JSONObject models) throws JSONException, IOException { + return processContainer(container, moduleName, addSchemaStmt, models, (Boolean) null); + } + + private JSONObject processContainer(ContainerSchemaNode container, String moduleName, + boolean addSchemaStmt, JSONObject models, Boolean isConfig) throws JSONException, + IOException { JSONObject moduleJSON = getSchemaTemplate(); - if(addSchemaStmt) { + if (addSchemaStmt) { moduleJSON = getSchemaTemplate(); } else { moduleJSON = new JSONObject(); @@ -201,49 +216,62 @@ public class ModelGenerator { moduleJSON.put(DESCRIPTION_KEY, containerDescription); Set containerChildren = container.getChildNodes(); - JSONObject properties = processChildren(containerChildren, moduleName, models); + JSONObject properties = processChildren(containerChildren, moduleName, models, isConfig); moduleJSON.put(PROPERTIES_KEY, properties); return moduleJSON; } + private JSONObject processChildren(Set nodes, String moduleName, + JSONObject models) throws JSONException, IOException { + return processChildren(nodes, moduleName, models, null); + } + /** * Processes the nodes + * * @param nodes * @param moduleName + * @param isConfig * @return * @throws JSONException * @throws IOException */ - private JSONObject processChildren(final Set nodes, final String moduleName, final JSONObject models) throws JSONException, IOException { + private JSONObject processChildren(Set nodes, String moduleName, + JSONObject models, Boolean isConfig) throws JSONException, IOException { JSONObject properties = new JSONObject(); - for(DataSchemaNode node : nodes){ - String name = node.getQName().getLocalName(); - JSONObject property = null; - if(node instanceof LeafSchemaNode) { - property = processLeafNode((LeafSchemaNode)node); - } else if (node instanceof ListSchemaNode) { - property = processListSchemaNode((ListSchemaNode)node, moduleName, models); + for (DataSchemaNode node : nodes) { + if (isConfig == null || node.isConfiguration() == isConfig) { - } else if (node instanceof LeafListSchemaNode) { - property = processLeafListNode((LeafListSchemaNode)node); + String name = node.getQName().getLocalName(); + JSONObject property = null; + if (node instanceof LeafSchemaNode) { + property = processLeafNode((LeafSchemaNode) node); + } else if (node instanceof ListSchemaNode) { + property = processListSchemaNode((ListSchemaNode) node, moduleName, models); - } else if (node instanceof ChoiceNode) { - property = processChoiceNode((ChoiceNode)node, moduleName, models); + } else if (node instanceof LeafListSchemaNode) { + property = processLeafListNode((LeafListSchemaNode) node); - } else if (node instanceof AnyXmlSchemaNode) { - property = processAnyXMLNode((AnyXmlSchemaNode)node); + } else if (node instanceof ChoiceNode) { + property = processChoiceNode((ChoiceNode) node, moduleName, models); - } else if (node instanceof ContainerSchemaNode) { - property = processContainer((ContainerSchemaNode)node, moduleName, false, models); + } else if (node instanceof AnyXmlSchemaNode) { + property = processAnyXMLNode((AnyXmlSchemaNode) node); - } else { - throw new IllegalArgumentException("Unknown DataSchemaNode type: " + node.getClass()); - } + } else if (node instanceof ContainerSchemaNode) { + property = processContainer((ContainerSchemaNode) node, moduleName, false, + models, isConfig); + + } else { + throw new IllegalArgumentException("Unknown DataSchemaNode type: " + + node.getClass()); + } - property.putOpt(DESCRIPTION_KEY, node.getDescription()); - properties.put(name, property); + property.putOpt(DESCRIPTION_KEY, node.getDescription()); + properties.put(name, property); + } } return properties; } @@ -253,7 +281,7 @@ public class ModelGenerator { * @param listNode * @throws JSONException */ - private JSONObject processLeafListNode(final LeafListSchemaNode listNode) throws JSONException { + private JSONObject processLeafListNode(LeafListSchemaNode listNode) throws JSONException { JSONObject props = new JSONObject(); props.put(TYPE_KEY, ARRAY_TYPE); @@ -274,12 +302,13 @@ public class ModelGenerator { * @throws JSONException * @throws IOException */ - private JSONObject processChoiceNode(final ChoiceNode choiceNode, final String moduleName, final JSONObject models) throws JSONException, IOException { + private JSONObject processChoiceNode(ChoiceNode choiceNode, String moduleName, JSONObject models) + throws JSONException, IOException { Set cases = choiceNode.getCases(); JSONArray choiceProps = new JSONArray(); - for(ChoiceCaseNode choiceCase: cases) { + for (ChoiceCaseNode choiceCase : cases) { String choiceName = choiceCase.getQName().getLocalName(); JSONObject choiceProp = processChildren(choiceCase.getChildNodes(), moduleName, models); JSONObject choiceObj = new JSONObject(); @@ -295,23 +324,23 @@ public class ModelGenerator { return oneOfProps; } - /** * * @param constraints * @param props * @throws JSONException */ - private void processConstraints(final ConstraintDefinition constraints, final JSONObject props) throws JSONException { + private void processConstraints(ConstraintDefinition constraints, JSONObject props) + throws JSONException { boolean isMandatory = constraints.isMandatory(); props.put(REQUIRED_KEY, isMandatory); Integer minElements = constraints.getMinElements(); Integer maxElements = constraints.getMaxElements(); - if(minElements !=null) { + if (minElements != null) { props.put(MIN_ITEMS, minElements); } - if(maxElements !=null) { + if (maxElements != null) { props.put(MAX_ITEMS, maxElements); } } @@ -319,9 +348,9 @@ public class ModelGenerator { /** * Parses a ListSchema node. * - * Due to a limitation of the RAML--->JAX-RS tool, sub-properties - * must be in a separate JSON schema file. Hence, we have to write - * some properties to a new file, while continuing to process the rest. + * Due to a limitation of the RAML--->JAX-RS tool, sub-properties must be in + * a separate JSON schema file. Hence, we have to write some properties to a + * new file, while continuing to process the rest. * * @param listNode * @param moduleName @@ -329,7 +358,8 @@ public class ModelGenerator { * @throws JSONException * @throws IOException */ - private JSONObject processListSchemaNode(final ListSchemaNode listNode, final String moduleName, final JSONObject models) throws JSONException, IOException { + private JSONObject processListSchemaNode(ListSchemaNode listNode, String moduleName, + JSONObject models) throws JSONException, IOException { Set listChildren = listNode.getChildNodes(); String fileName = listNode.getQName().getLocalName(); @@ -340,20 +370,19 @@ public class ModelGenerator { childSchema.put(PROPERTIES_KEY, childSchemaProperties); /* - * Due to a limitation of the RAML--->JAX-RS tool, sub-properties - * must be in a separate JSON schema file. Hence, we have to write - * some properties to a new file, while continuing to process the rest. + * Due to a limitation of the RAML--->JAX-RS tool, sub-properties must + * be in a separate JSON schema file. Hence, we have to write some + * properties to a new file, while continuing to process the rest. */ - //writeToFile(fileName, childSchema.toString(2), moduleName); + // writeToFile(fileName, childSchema.toString(2), moduleName); childSchema.put("id", fileName); models.put(fileName, childSchema); - JSONObject listNodeProperties = new JSONObject(); listNodeProperties.put(TYPE_KEY, ARRAY_TYPE); JSONObject items = new JSONObject(); - items.put(REF_KEY,fileName ); + items.put(REF_KEY, fileName); listNodeProperties.put(ITEMS_KEY, items); return listNodeProperties; @@ -366,7 +395,7 @@ public class ModelGenerator { * @return * @throws JSONException */ - private JSONObject processLeafNode(final LeafSchemaNode leafNode) throws JSONException { + private JSONObject processLeafNode(LeafSchemaNode leafNode) throws JSONException { JSONObject property = new JSONObject(); String leafDescription = leafNode.getDescription(); @@ -384,7 +413,7 @@ public class ModelGenerator { * @return * @throws JSONException */ - private JSONObject processAnyXMLNode(final AnyXmlSchemaNode leafNode) throws JSONException { + private JSONObject processAnyXMLNode(AnyXmlSchemaNode leafNode) throws JSONException { JSONObject property = new JSONObject(); String leafDescription = leafNode.getDescription(); @@ -399,27 +428,28 @@ public class ModelGenerator { * @param property * @throws JSONException */ - private void processTypeDef(final TypeDefinition leafTypeDef, final JSONObject property) throws JSONException { + private void processTypeDef(TypeDefinition leafTypeDef, JSONObject property) + throws JSONException { - if(leafTypeDef instanceof ExtendedType){ + if (leafTypeDef instanceof ExtendedType) { processExtendedType(leafTypeDef, property); } else if (leafTypeDef instanceof EnumerationType) { - processEnumType((EnumerationType)leafTypeDef, property); + processEnumType((EnumerationType) leafTypeDef, property); } else if (leafTypeDef instanceof BitsTypeDefinition) { - processBitsType((BitsTypeDefinition)leafTypeDef, property); + processBitsType((BitsTypeDefinition) leafTypeDef, property); } else if (leafTypeDef instanceof UnionTypeDefinition) { - processUnionType((UnionTypeDefinition)leafTypeDef, property); + processUnionType((UnionTypeDefinition) leafTypeDef, property); } else if (leafTypeDef instanceof IdentityrefTypeDefinition) { property.putOpt(TYPE_KEY, "object"); } else if (leafTypeDef instanceof BinaryTypeDefinition) { - processBinaryType((BinaryTypeDefinition)leafTypeDef, property); + processBinaryType((BinaryTypeDefinition) leafTypeDef, property); } else { - //System.out.println("In else: " + leafTypeDef.getClass()); + // System.out.println("In else: " + leafTypeDef.getClass()); String jsonType = YANG_TYPE_TO_JSON_TYPE_MAPPING.get(leafTypeDef.getClass()); - if(jsonType==null) { + if (jsonType == null) { jsonType = "object"; } property.putOpt(TYPE_KEY, jsonType); @@ -432,29 +462,32 @@ public class ModelGenerator { * @param property * @throws JSONException */ - private void processExtendedType(final TypeDefinition leafTypeDef, final JSONObject property) throws JSONException { + private void processExtendedType(TypeDefinition leafTypeDef, JSONObject property) + throws JSONException { Object leafBaseType = leafTypeDef.getBaseType(); - if(leafBaseType instanceof ExtendedType){ - //recursively process an extended type until we hit a base type - processExtendedType((TypeDefinition)leafBaseType, property); + if (leafBaseType instanceof ExtendedType) { + // recursively process an extended type until we hit a base type + processExtendedType((TypeDefinition) leafBaseType, property); } else { - List lengthConstraints = ((ExtendedType) leafTypeDef).getLengthConstraints(); - for(LengthConstraint lengthConstraint: lengthConstraints) { + List lengthConstraints = ((ExtendedType) leafTypeDef) + .getLengthConstraints(); + for (LengthConstraint lengthConstraint : lengthConstraints) { Number min = lengthConstraint.getMin(); Number max = lengthConstraint.getMax(); property.putOpt(MIN_LENGTH_KEY, min); property.putOpt(MAX_LENGTH_KEY, max); } String jsonType = YANG_TYPE_TO_JSON_TYPE_MAPPING.get(leafBaseType.getClass()); - property.putOpt(TYPE_KEY,jsonType ); + property.putOpt(TYPE_KEY, jsonType); } } /* - * - */ - private void processBinaryType(final BinaryTypeDefinition binaryType, final JSONObject property) throws JSONException { + * + */ + private void processBinaryType(BinaryTypeDefinition binaryType, JSONObject property) + throws JSONException { property.put(TYPE_KEY, STRING); JSONObject media = new JSONObject(); media.put(BINARY_ENCODING_KEY, BASE_64); @@ -467,10 +500,11 @@ public class ModelGenerator { * @param property * @throws JSONException */ - private void processEnumType(final EnumerationType enumLeafType, final JSONObject property) throws JSONException { + private void processEnumType(EnumerationType enumLeafType, JSONObject property) + throws JSONException { List enumPairs = enumLeafType.getValues(); List enumNames = new ArrayList(); - for(EnumPair enumPair: enumPairs) { + for (EnumPair enumPair : enumPairs) { enumNames.add(enumPair.getName()); } property.putOpt(ENUM, new JSONArray(enumNames)); @@ -482,14 +516,15 @@ public class ModelGenerator { * @param property * @throws JSONException */ - private void processBitsType(final BitsTypeDefinition bitsType, final JSONObject property) throws JSONException{ + private void processBitsType(BitsTypeDefinition bitsType, JSONObject property) + throws JSONException { property.put(TYPE_KEY, ARRAY_TYPE); property.put(MIN_ITEMS, 0); property.put(UNIQUE_ITEMS_KEY, true); JSONArray enumValues = new JSONArray(); List bits = bitsType.getBits(); - for(Bit bit: bits) { + for (Bit bit : bits) { enumValues.put(bit.getName()); } JSONObject itemsValue = new JSONObject(); @@ -497,27 +532,26 @@ public class ModelGenerator { property.put(ITEMS_KEY, itemsValue); } - /** * * @param unionType * @param property * @throws JSONException */ - private void processUnionType(final UnionTypeDefinition unionType, final JSONObject property) throws JSONException{ + private void processUnionType(UnionTypeDefinition unionType, JSONObject property) + throws JSONException { List> unionTypes = unionType.getTypes(); JSONArray unionArray = new JSONArray(); - for(TypeDefinition typeDef: unionTypes) { + for (TypeDefinition typeDef : unionTypes) { unionArray.put(YANG_TYPE_TO_JSON_TYPE_MAPPING.get(typeDef.getClass())); } property.put(TYPE_KEY, unionArray); } - /** - * Helper method to generate a pre-filled - * JSON schema object. + * Helper method to generate a pre-filled JSON schema object. + * * @return * @throws JSONException */ diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/jaxrs/ApiDocApplication.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/jaxrs/ApiDocApplication.java index bec34b232a..e833c61399 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/jaxrs/ApiDocApplication.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/jaxrs/ApiDocApplication.java @@ -7,18 +7,19 @@ */ package org.opendaylight.controller.sal.rest.doc.jaxrs; -import org.opendaylight.controller.sal.rest.doc.impl.ApiDocServiceImpl; - -import javax.ws.rs.core.Application; import java.util.HashSet; import java.util.Set; +import javax.ws.rs.core.Application; + +import org.opendaylight.controller.sal.rest.doc.impl.ApiDocServiceImpl; + public class ApiDocApplication extends Application { - @Override - public Set getSingletons() { - Set singletons = new HashSet<>(); - singletons.add(ApiDocServiceImpl.getInstance()); - singletons.add(new JaxbContextResolver()); - return singletons; - } + @Override + public Set getSingletons() { + Set singletons = new HashSet<>(); + singletons.add(ApiDocServiceImpl.getInstance()); + singletons.add(new JaxbContextResolver()); + return singletons; + } } diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/jaxrs/JaxbContextResolver.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/jaxrs/JaxbContextResolver.java index 5508507d5f..66fc80fdc7 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/jaxrs/JaxbContextResolver.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/jaxrs/JaxbContextResolver.java @@ -7,37 +7,39 @@ */ package org.opendaylight.controller.sal.rest.doc.jaxrs; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule; -import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration; - import javax.ws.rs.Consumes; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.ext.ContextResolver; import javax.ws.rs.ext.Provider; +import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule; + @Provider @Consumes(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON) public class JaxbContextResolver implements ContextResolver { - private ObjectMapper ctx; + private final ObjectMapper ctx; - public JaxbContextResolver(){ - ctx = new ObjectMapper(); - ctx.registerModule(new JsonOrgModule()); - ctx.getSerializationConfig().withSerializationInclusion(JsonInclude.Include.ALWAYS); - } + public JaxbContextResolver() { + ctx = new ObjectMapper(); + ctx.registerModule(new JsonOrgModule()); + ctx.getSerializationConfig().withSerializationInclusion(JsonInclude.Include.ALWAYS); + } - @Override - public ObjectMapper getContext(Class aClass) { + @Override + public ObjectMapper getContext(Class aClass) { - if (ApiDeclaration.class.isAssignableFrom(aClass)){ - return ctx; - } + if (ApiDeclaration.class.isAssignableFrom(aClass)) { + return ctx; + } - return null;//must return null so that jax-rs can continue context search - } + return null;// must return null so that jax-rs can continue context + // search + } } diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/model/builder/OperationBuilder.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/model/builder/OperationBuilder.java index fc890d5a2d..9a33ee31b3 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/model/builder/OperationBuilder.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/model/builder/OperationBuilder.java @@ -7,112 +7,117 @@ */ package org.opendaylight.controller.sal.rest.doc.model.builder; +import java.util.ArrayList; +import java.util.List; + import org.opendaylight.controller.sal.rest.doc.swagger.Operation; import org.opendaylight.controller.sal.rest.doc.swagger.Parameter; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import java.util.ArrayList; -import java.util.List; - /** * */ public final class OperationBuilder { - /** + public static final String OPERATIONAL = "(operational)"; + public static final String CONFIG = "(config)"; + + /** * */ - public static class Get{ - - protected Operation spec; - protected DataSchemaNode schemaNode; - private final String METHOD_NAME = "GET"; - - public Get(DataSchemaNode node){ - this.schemaNode = node; - spec = new Operation(); - spec.setMethod(METHOD_NAME); - spec.setNickname(METHOD_NAME + "-" + node.getQName().getLocalName()); - spec.setType(node.getQName().getLocalName()); - spec.setNotes(node.getDescription()); + public static class Get { + + protected Operation spec; + protected DataSchemaNode schemaNode; + private final String METHOD_NAME = "GET"; + + public Get(DataSchemaNode node, boolean isConfig) { + this.schemaNode = node; + spec = new Operation(); + spec.setMethod(METHOD_NAME); + spec.setNickname(METHOD_NAME + "-" + node.getQName().getLocalName()); + spec.setType((isConfig ? CONFIG : OPERATIONAL) + node.getQName().getLocalName()); + spec.setNotes(node.getDescription()); + } + + public Get pathParams(List params) { + List pathParameters = new ArrayList<>(params); + spec.setParameters(pathParameters); + return this; + } + + public Operation build() { + return spec; + } } - public Get pathParams(List params){ - List pathParameters = new ArrayList<>(params); - spec.setParameters(pathParameters); - return this; - } - - public Operation build(){ - return spec; - } - } - - /** + /** * */ - public static class Put{ - protected Operation spec; - protected DataSchemaNode schemaNode; - private final String METHOD_NAME = "PUT"; - - public Put(DataSchemaNode node){ - this.schemaNode = node; - spec = new Operation(); - spec.setType(node.getQName().getLocalName()); - spec.setNotes(node.getDescription()); - } - - public Put pathParams(List params){ - List parameters = new ArrayList<>(params); - Parameter payload = new Parameter(); - payload.setParamType("body"); - payload.setType(schemaNode.getQName().getLocalName()); - parameters.add(payload); - spec.setParameters(parameters); - return this; - } - - public Operation build(){ - spec.setMethod(METHOD_NAME); - spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName()); - return spec; + public static class Put { + protected Operation spec; + protected DataSchemaNode schemaNode; + private final String METHOD_NAME = "PUT"; + + public Put(DataSchemaNode node) { + this.schemaNode = node; + spec = new Operation(); + spec.setType(CONFIG + node.getQName().getLocalName()); + spec.setNotes(node.getDescription()); + } + + public Put pathParams(List params) { + List parameters = new ArrayList<>(params); + Parameter payload = new Parameter(); + payload.setParamType("body"); + payload.setType(CONFIG + schemaNode.getQName().getLocalName()); + parameters.add(payload); + spec.setParameters(parameters); + return this; + } + + public Operation build() { + spec.setMethod(METHOD_NAME); + spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName()); + return spec; + } } - } - /** + /** * */ - public static final class Post extends Put{ + public static final class Post extends Put { - private final String METHOD_NAME = "POST"; + private final String METHOD_NAME = "POST"; - public Post(DataSchemaNode node){ - super(node); - } + public Post(DataSchemaNode node) { + super(node); + } - public Operation build(){ - spec.setMethod(METHOD_NAME); - spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName()); - return spec; + @Override + public Operation build() { + spec.setMethod(METHOD_NAME); + spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName()); + return spec; + } } - } - /** + /** * */ - public static final class Delete extends Get { - private final String METHOD_NAME = "DELETE"; - - public Delete(DataSchemaNode node){ - super(node); - } - - public Operation build(){ - spec.setMethod(METHOD_NAME); - spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName()); - spec.setType(null); - return spec; + public static final class Delete extends Get { + private final String METHOD_NAME = "DELETE"; + + public Delete(DataSchemaNode node) { + super(node, false); + } + + @Override + public Operation build() { + spec.setMethod(METHOD_NAME); + spec.setNickname(METHOD_NAME + "-" + schemaNode.getQName().getLocalName()); + spec.setType(null); + return spec; + } } - } } diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/mountpoints/MountPointSwagger.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/mountpoints/MountPointSwagger.java new file mode 100644 index 0000000000..b996bf1234 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/mountpoints/MountPointSwagger.java @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2014 Brocade Communications Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.sal.rest.doc.mountpoints; + +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.TreeMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + +import javax.ws.rs.core.UriInfo; + +import org.opendaylight.controller.sal.core.api.model.SchemaService; +import org.opendaylight.controller.sal.core.api.mount.MountProvisionInstance; +import org.opendaylight.controller.sal.core.api.mount.MountProvisionService; +import org.opendaylight.controller.sal.core.api.mount.MountProvisionService.MountProvisionListener; +import org.opendaylight.controller.sal.rest.doc.impl.BaseYangSwaggerGenerator; +import org.opendaylight.controller.sal.rest.doc.swagger.Api; +import org.opendaylight.controller.sal.rest.doc.swagger.ApiDeclaration; +import org.opendaylight.controller.sal.rest.doc.swagger.Operation; +import org.opendaylight.controller.sal.rest.doc.swagger.Resource; +import org.opendaylight.controller.sal.rest.doc.swagger.ResourceList; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +public class MountPointSwagger extends BaseYangSwaggerGenerator implements MountProvisionListener { + + private static final String DATASTORES_REVISION = "-"; + private static final String DATASTORES_LABEL = "Datastores"; + + private MountProvisionService mountService; + private final Map instanceIdToLongId = new TreeMap<>( + new Comparator() { + @Override + public int compare(InstanceIdentifier o1, InstanceIdentifier o2) { + return o1.toString().compareToIgnoreCase(o2.toString()); + } + }); + private final Map longIdToInstanceId = new HashMap<>(); + private final Object lock = new Object(); + + private final AtomicLong idKey = new AtomicLong(0); + + private static AtomicReference selfRef = new AtomicReference<>(); + private SchemaService globalSchema; + + public Map getInstanceIdentifiers() { + Map urlToId = new HashMap<>(); + synchronized (lock) { + SchemaContext context = globalSchema.getGlobalContext(); + for (Entry entry : instanceIdToLongId.entrySet()) { + String modName = findModuleName(entry.getKey(), context); + urlToId.put(generateUrlPrefixFromInstanceID(entry.getKey(), modName), + entry.getValue()); + } + } + return urlToId; + } + + public void setGlobalSchema(SchemaService globalSchema) { + this.globalSchema = globalSchema; + } + + private String findModuleName(InstanceIdentifier id, SchemaContext context) { + PathArgument rootQName = id.getPath().get(0); + for (Module mod : context.getModules()) { + if (mod.getDataChildByName(rootQName.getNodeType()) != null) { + return mod.getName(); + } + } + return null; + } + + private String generateUrlPrefixFromInstanceID(InstanceIdentifier key, String moduleName) { + List path = key.getPath(); + StringBuilder builder = new StringBuilder(); + if (moduleName != null) { + builder.append(moduleName); + builder.append(":"); + } + boolean first = true; + for (PathArgument arg : path) { + + String name = arg.getNodeType().getLocalName(); + if (first) { + first = false; + } else { + builder.append("/"); + } + builder.append(name); + if (arg instanceof InstanceIdentifier.NodeIdentifierWithPredicates) { + NodeIdentifierWithPredicates nodeId = (NodeIdentifierWithPredicates) arg; + for (Entry entry : nodeId.getKeyValues().entrySet()) { + builder.append("/").append(entry.getValue()); + } + } + } + + return builder.append("/").toString(); + } + + private String getYangMountUrl(InstanceIdentifier key) { + String modName = findModuleName(key, globalSchema.getGlobalContext()); + return generateUrlPrefixFromInstanceID(key, modName) + "yang-ext:mount/"; + } + + public ResourceList getResourceList(UriInfo uriInfo, Long id) { + InstanceIdentifier iid = getInstanceId(id); + if (iid == null) { + return null; // indicating not found. + } + SchemaContext context = getSchemaContext(iid); + String urlPrefix = getYangMountUrl(iid); + if (context == null) { + return createResourceList(); + } + List resources = new LinkedList<>(); + Resource dataStores = new Resource(); + dataStores.setDescription("Provides methods for accessing the data stores."); + dataStores.setPath(generatePath(uriInfo, DATASTORES_LABEL, DATASTORES_REVISION)); + resources.add(dataStores); + ResourceList list = super.getResourceListing(uriInfo, context, urlPrefix); + resources.addAll(list.getApis()); + list.setApis(resources); + return list; + } + + private InstanceIdentifier getInstanceId(Long id) { + InstanceIdentifier instanceId; + synchronized (lock) { + instanceId = longIdToInstanceId.get(id); + } + return instanceId; + } + + private SchemaContext getSchemaContext(InstanceIdentifier id) { + + if (id == null) { + return null; + } + + MountProvisionInstance mountPoint = mountService.getMountPoint(id); + if (mountPoint == null) { + return null; + } + + SchemaContext context = mountPoint.getSchemaContext(); + if (context == null) { + return null; + } + return context; + } + + public ApiDeclaration getMountPointApi(UriInfo uriInfo, Long id, String module, String revision) { + InstanceIdentifier iid = getInstanceId(id); + SchemaContext context = getSchemaContext(iid); + String urlPrefix = getYangMountUrl(iid); + if (context == null) { + return null; + } + + if (DATASTORES_LABEL.equals(module) && DATASTORES_REVISION.equals(revision)) { + return generateDataStoreApiDoc(uriInfo, urlPrefix); + } + return super.getApiDeclaration(module, revision, uriInfo, context, urlPrefix); + } + + private ApiDeclaration generateDataStoreApiDoc(UriInfo uriInfo, String context) { + + ApiDeclaration declaration = super.createApiDeclaration(createBasePathFromUriInfo(uriInfo)); + List apis = new LinkedList<>(); + apis.add(createGetApi("config", + "Queries the config (startup) datastore on the mounted hosted.", context)); + apis.add(createGetApi("operational", + "Queries the operational (running) datastore on the mounted hosted.", context)); + apis.add(createGetApi("operations", + "Queries the available operations (RPC calls) on the mounted hosted.", context)); + declaration.setApis(apis); + return declaration; + + } + + private Api createGetApi(String datastore, String note, String context) { + Operation getConfig = new Operation(); + getConfig.setMethod("GET"); + getConfig.setNickname("GET " + datastore); + getConfig.setNotes(note); + + Api api = new Api(); + api.setPath(getDataStorePath("/" + datastore + "/", context)); + api.setOperations(Collections.singletonList(getConfig)); + + return api; + } + + public void setMountService(MountProvisionService mountService) { + this.mountService = mountService; + } + + @Override + public void onMountPointCreated(InstanceIdentifier path) { + synchronized (lock) { + Long idLong = idKey.incrementAndGet(); + instanceIdToLongId.put(path, idLong); + longIdToInstanceId.put(idLong, path); + } + } + + @Override + public void onMountPointRemoved(InstanceIdentifier path) { + synchronized (lock) { + Long id = instanceIdToLongId.remove(path); + longIdToInstanceId.remove(id); + } + } + + public static MountPointSwagger getInstance() { + MountPointSwagger swagger = selfRef.get(); + if (swagger == null) { + selfRef.compareAndSet(null, new MountPointSwagger()); + swagger = selfRef.get(); + } + return swagger; + } + +} diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Api.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Api.java index a2be741c74..cc45875dfb 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Api.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Api.java @@ -10,28 +10,29 @@ package org.opendaylight.controller.sal.rest.doc.swagger; import java.util.List; /** - * Implementation of swagger spec - * (see - * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#522-api-object) + * Implementation of swagger spec (see https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#522-api + * -object) */ public class Api { - private String path; - private List operations; + private String path; + private List operations; - public String getPath() { - return path; - } + public String getPath() { + return path; + } - public void setPath(String path) { - this.path = path; - } + public void setPath(String path) { + this.path = path; + } - public List getOperations() { - return operations; - } + public List getOperations() { + return operations; + } - public void setOperations(List operations) { - this.operations = operations; - } + public void setOperations(List operations) { + this.operations = operations; + } } diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/ApiDeclaration.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/ApiDeclaration.java index e89bdaf603..4ba0ab2255 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/ApiDeclaration.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/ApiDeclaration.java @@ -7,85 +7,86 @@ */ package org.opendaylight.controller.sal.rest.doc.swagger; -import org.json.JSONObject; - import java.util.List; +import org.json.JSONObject; + /** - * Implementation of swagger spec - * (see - * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#52-api-declaration) + * Implementation of swagger spec (see https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#52-api- + * declaration) */ public class ApiDeclaration { - private String apiVersion; - private String swaggerVersion; - private String basePath; - private String resourcePath; - private List produces; - private List apis; - private JSONObject models; - - public JSONObject getModels() { - return models; - } - - public void setModels(JSONObject models) { - this.models = models; - } - - public String getApiVersion() { - return apiVersion; - } - - public void setApiVersion(String apiVersion) { - this.apiVersion = apiVersion; - } - - public String getSwaggerVersion() { - return swaggerVersion; - } - - public void setSwaggerVersion(String swaggerVersion) { - this.swaggerVersion = swaggerVersion; - } - - public String getBasePath() { - return basePath; - } - - public void setBasePath(String basePath) { - this.basePath = basePath; - } - - public String getResourcePath() { - return resourcePath; - } - - public void setResourcePath(String resourcePath) { - this.resourcePath = resourcePath; - } - - public List getProduces() { - return produces; - } - - public void setProduces(List produces) { - this.produces = produces; - } - - public List getApis() { - return apis; - } - - public void setApis(List apis) { - this.apis = apis; - } - - public boolean hasApi(){ - return (apis != null && !apis.isEmpty()); - } - - public boolean hasModel(){ - return (models != null && models.length() > 0); - } + private String apiVersion; + private String swaggerVersion; + private String basePath; + private String resourcePath; + private List produces; + private List apis; + private JSONObject models; + + public JSONObject getModels() { + return models; + } + + public void setModels(JSONObject models) { + this.models = models; + } + + public String getApiVersion() { + return apiVersion; + } + + public void setApiVersion(String apiVersion) { + this.apiVersion = apiVersion; + } + + public String getSwaggerVersion() { + return swaggerVersion; + } + + public void setSwaggerVersion(String swaggerVersion) { + this.swaggerVersion = swaggerVersion; + } + + public String getBasePath() { + return basePath; + } + + public void setBasePath(String basePath) { + this.basePath = basePath; + } + + public String getResourcePath() { + return resourcePath; + } + + public void setResourcePath(String resourcePath) { + this.resourcePath = resourcePath; + } + + public List getProduces() { + return produces; + } + + public void setProduces(List produces) { + this.produces = produces; + } + + public List getApis() { + return apis; + } + + public void setApis(List apis) { + this.apis = apis; + } + + public boolean hasApi() { + return (apis != null && !apis.isEmpty()); + } + + public boolean hasModel() { + return (models != null && models.length() > 0); + } } diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Operation.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Operation.java index d1150fd0bd..ba0c7966ec 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Operation.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Operation.java @@ -10,81 +10,82 @@ package org.opendaylight.controller.sal.rest.doc.swagger; import java.util.List; /** - * Implementation of swagger spec - * (see - * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#523-operation-object) + * Implementation of swagger spec (see https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#523- + * operation-object) */ public class Operation { - private String method; - private String summary; - private String notes; - private String type; - private String nickname; - private List consumes; - private List parameters; - private List responseMessages; - - public String getMethod() { - return method; - } - - public void setMethod(String method) { - this.method = method; - } - - public String getSummary() { - return summary; - } - - public void setSummary(String summary) { - this.summary = summary; - } - - public String getNotes() { - return notes; - } - - public void setNotes(String notes) { - this.notes = notes; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getNickname() { - return nickname; - } - - public void setNickname(String nickname) { - this.nickname = nickname; - } - - public List getConsumes() { - return consumes; - } - - public void setConsumes(List consumes) { - this.consumes = consumes; - } - - public List getParameters() { - return parameters; - } - - public void setParameters(List parameters) { - this.parameters = parameters; - } - - public List getResponseMessages() { - return responseMessages; - } - - public void setResponseMessages(List responseMessages) { - this.responseMessages = responseMessages; - } + private String method; + private String summary; + private String notes; + private String type; + private String nickname; + private List consumes; + private List parameters; + private List responseMessages; + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getNickname() { + return nickname; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } + + public List getConsumes() { + return consumes; + } + + public void setConsumes(List consumes) { + this.consumes = consumes; + } + + public List getParameters() { + return parameters; + } + + public void setParameters(List parameters) { + this.parameters = parameters; + } + + public List getResponseMessages() { + return responseMessages; + } + + public void setResponseMessages(List responseMessages) { + this.responseMessages = responseMessages; + } } diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Parameter.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Parameter.java index dd56b9e854..41cec4db54 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Parameter.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Parameter.java @@ -8,54 +8,55 @@ package org.opendaylight.controller.sal.rest.doc.swagger; /** - * Implementation of swagger spec - * (see - * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#524-parameter-object) + * Implementation of swagger spec (see https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#524- + * parameter-object) */ public class Parameter { - private String name; - private String description; - private boolean required; - private String type; - private String paramType; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public boolean isRequired() { - return required; - } - - public void setRequired(boolean required) { - this.required = required; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getParamType() { - return paramType; - } - - public void setParamType(String paramType) { - this.paramType = paramType; - } + private String name; + private String description; + private boolean required; + private String type; + private String paramType; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public boolean isRequired() { + return required; + } + + public void setRequired(boolean required) { + this.required = required; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getParamType() { + return paramType; + } + + public void setParamType(String paramType) { + this.paramType = paramType; + } } diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Resource.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Resource.java index c5c39799a8..cb5b200c60 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Resource.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/Resource.java @@ -8,27 +8,28 @@ package org.opendaylight.controller.sal.rest.doc.swagger; /** - * Implementation of swagger spec - * (see - * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#512-resource-object) + * Implementation of swagger spec (see https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#512- + * resource-object) */ public class Resource { - private String path; - private String description; + private String path; + private String description; - public String getPath() { - return path; - } + public String getPath() { + return path; + } - public void setPath(String path) { - this.path = path; - } + public void setPath(String path) { + this.path = path; + } - public String getDescription() { - return description; - } + public String getDescription() { + return description; + } - public void setDescription(String description) { - this.description = description; - } + public void setDescription(String description) { + this.description = description; + } } diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/ResourceList.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/ResourceList.java index 9b1a106d0e..48362dc4ed 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/ResourceList.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/ResourceList.java @@ -10,36 +10,37 @@ package org.opendaylight.controller.sal.rest.doc.swagger; import java.util.List; /** - * Implementation of swagger spec - * (see - * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#51-resource-listing) + * Implementation of swagger spec (see https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#51- + * resource-listing) */ public class ResourceList { - private String apiVersion; - private String swaggerVersion; - private List apis; + private String apiVersion; + private String swaggerVersion; + private List apis; - public String getApiVersion() { - return apiVersion; - } + public String getApiVersion() { + return apiVersion; + } - public void setApiVersion(String apiVersion) { - this.apiVersion = apiVersion; - } + public void setApiVersion(String apiVersion) { + this.apiVersion = apiVersion; + } - public String getSwaggerVersion() { - return swaggerVersion; - } + public String getSwaggerVersion() { + return swaggerVersion; + } - public void setSwaggerVersion(String swaggerVersion) { - this.swaggerVersion = swaggerVersion; - } + public void setSwaggerVersion(String swaggerVersion) { + this.swaggerVersion = swaggerVersion; + } - public List getApis() { - return apis; - } + public List getApis() { + return apis; + } - public void setApis(List apis) { - this.apis = apis; - } + public void setApis(List apis) { + this.apis = apis; + } } diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/ResponseMessage.java b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/ResponseMessage.java index 6c23099286..d82e1f4a81 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/ResponseMessage.java +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/java/org/opendaylight/controller/sal/rest/doc/swagger/ResponseMessage.java @@ -8,27 +8,28 @@ package org.opendaylight.controller.sal.rest.doc.swagger; /** - * Implementation of swagger spec - * (see - * https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#525-response-message-object) + * Implementation of swagger spec (see https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#525- + * response-message-object) */ public class ResponseMessage { - private int code; - private String message; + private int code; + private String message; - public int getCode() { - return code; - } + public int getCode() { + return code; + } - public void setCode(int code) { - this.code = code; - } + public void setCode(int code) { + this.code = code; + } - public String getMessage() { - return message; - } + public String getMessage() { + return message; + } - public void setMessage(String message) { - this.message = message; - } + public void setMessage(String message) { + this.message = message; + } } diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/animated-overlay.gif b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/animated-overlay.gif new file mode 100644 index 0000000000..d441f75ebf Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/animated-overlay.gif differ diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png new file mode 100644 index 0000000000..a28d6780ad Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png differ diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png new file mode 100644 index 0000000000..ab561d2bda Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png differ diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png new file mode 100644 index 0000000000..be5c4c6ea3 Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png differ diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png new file mode 100644 index 0000000000..c955a2cc06 Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png differ diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png new file mode 100644 index 0000000000..de7c46da1c Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png differ diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png new file mode 100644 index 0000000000..6cc858e6a9 Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png differ diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png new file mode 100644 index 0000000000..653f494cd4 Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png differ diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png new file mode 100644 index 0000000000..663b1129ec Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png differ diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png new file mode 100644 index 0000000000..ee995d9e79 Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png differ diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_222222_256x240.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_222222_256x240.png new file mode 100644 index 0000000000..c1cb1170c8 Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_222222_256x240.png differ diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_228ef1_256x240.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_228ef1_256x240.png new file mode 100644 index 0000000000..3a0140cff6 Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_228ef1_256x240.png differ diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ef8c08_256x240.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ef8c08_256x240.png new file mode 100644 index 0000000000..036ee072d4 Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ef8c08_256x240.png differ diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ffd27a_256x240.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ffd27a_256x240.png new file mode 100644 index 0000000000..8b6c05868b Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ffd27a_256x240.png differ diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ffffff_256x240.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ffffff_256x240.png new file mode 100644 index 0000000000..4f624bb2b1 Binary files /dev/null and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/images/ui-icons_ffffff_256x240.png differ diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/jquery-ui-1.10.4.custom.css b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/jquery-ui-1.10.4.custom.css new file mode 100644 index 0000000000..a9aa9c4d6f --- /dev/null +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/jquery-ui-1.10.4.custom.css @@ -0,0 +1,1178 @@ +/*! jQuery UI - v1.10.4 - 2014-06-03 +* http://jqueryui.com +* Includes: jquery.ui.core.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css, jquery.ui.theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS%2CTahoma%2CVerdana%2CArial%2Csans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=gloss_wave&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=highlight_soft&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=glass&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=glass&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=highlight_soft&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=diagonals_thick&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=diagonals_thick&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=flat&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px +* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { + display: none; +} +.ui-helper-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} +.ui-helper-reset { + margin: 0; + padding: 0; + border: 0; + outline: 0; + line-height: 1.3; + text-decoration: none; + font-size: 100%; + list-style: none; +} +.ui-helper-clearfix:before, +.ui-helper-clearfix:after { + content: ""; + display: table; + border-collapse: collapse; +} +.ui-helper-clearfix:after { + clear: both; +} +.ui-helper-clearfix { + min-height: 0; /* support: IE7 */ +} +.ui-helper-zfix { + width: 100%; + height: 100%; + top: 0; + left: 0; + position: absolute; + opacity: 0; + filter:Alpha(Opacity=0); +} + +.ui-front { + z-index: 100; +} + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { + cursor: default !important; +} + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + display: block; + text-indent: -99999px; + overflow: hidden; + background-repeat: no-repeat; +} + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.ui-resizable { + position: relative; +} +.ui-resizable-handle { + position: absolute; + font-size: 0.1px; + display: block; +} +.ui-resizable-disabled .ui-resizable-handle, +.ui-resizable-autohide .ui-resizable-handle { + display: none; +} +.ui-resizable-n { + cursor: n-resize; + height: 7px; + width: 100%; + top: -5px; + left: 0; +} +.ui-resizable-s { + cursor: s-resize; + height: 7px; + width: 100%; + bottom: -5px; + left: 0; +} +.ui-resizable-e { + cursor: e-resize; + width: 7px; + right: -5px; + top: 0; + height: 100%; +} +.ui-resizable-w { + cursor: w-resize; + width: 7px; + left: -5px; + top: 0; + height: 100%; +} +.ui-resizable-se { + cursor: se-resize; + width: 12px; + height: 12px; + right: 1px; + bottom: 1px; +} +.ui-resizable-sw { + cursor: sw-resize; + width: 9px; + height: 9px; + left: -5px; + bottom: -5px; +} +.ui-resizable-nw { + cursor: nw-resize; + width: 9px; + height: 9px; + left: -5px; + top: -5px; +} +.ui-resizable-ne { + cursor: ne-resize; + width: 9px; + height: 9px; + right: -5px; + top: -5px; +} +.ui-selectable-helper { + position: absolute; + z-index: 100; + border: 1px dotted black; +} +.ui-accordion .ui-accordion-header { + display: block; + cursor: pointer; + position: relative; + margin-top: 2px; + padding: .5em .5em .5em .7em; + min-height: 0; /* support: IE7 */ +} +.ui-accordion .ui-accordion-icons { + padding-left: 2.2em; +} +.ui-accordion .ui-accordion-noicons { + padding-left: .7em; +} +.ui-accordion .ui-accordion-icons .ui-accordion-icons { + padding-left: 2.2em; +} +.ui-accordion .ui-accordion-header .ui-accordion-header-icon { + position: absolute; + left: .5em; + top: 50%; + margin-top: -8px; +} +.ui-accordion .ui-accordion-content { + padding: 1em 2.2em; + border-top: 0; + overflow: auto; +} +.ui-autocomplete { + position: absolute; + top: 0; + left: 0; + cursor: default; +} +.ui-button { + display: inline-block; + position: relative; + padding: 0; + line-height: normal; + margin-right: .1em; + cursor: pointer; + vertical-align: middle; + text-align: center; + overflow: visible; /* removes extra width in IE */ +} +.ui-button, +.ui-button:link, +.ui-button:visited, +.ui-button:hover, +.ui-button:active { + text-decoration: none; +} +/* to make room for the icon, a width needs to be set here */ +.ui-button-icon-only { + width: 2.2em; +} +/* button elements seem to need a little more width */ +button.ui-button-icon-only { + width: 2.4em; +} +.ui-button-icons-only { + width: 3.4em; +} +button.ui-button-icons-only { + width: 3.7em; +} + +/* button text element */ +.ui-button .ui-button-text { + display: block; + line-height: normal; +} +.ui-button-text-only .ui-button-text { + padding: .4em 1em; +} +.ui-button-icon-only .ui-button-text, +.ui-button-icons-only .ui-button-text { + padding: .4em; + text-indent: -9999999px; +} +.ui-button-text-icon-primary .ui-button-text, +.ui-button-text-icons .ui-button-text { + padding: .4em 1em .4em 2.1em; +} +.ui-button-text-icon-secondary .ui-button-text, +.ui-button-text-icons .ui-button-text { + padding: .4em 2.1em .4em 1em; +} +.ui-button-text-icons .ui-button-text { + padding-left: 2.1em; + padding-right: 2.1em; +} +/* no icon support for input elements, provide padding by default */ +input.ui-button { + padding: .4em 1em; +} + +/* button icon element(s) */ +.ui-button-icon-only .ui-icon, +.ui-button-text-icon-primary .ui-icon, +.ui-button-text-icon-secondary .ui-icon, +.ui-button-text-icons .ui-icon, +.ui-button-icons-only .ui-icon { + position: absolute; + top: 50%; + margin-top: -8px; +} +.ui-button-icon-only .ui-icon { + left: 50%; + margin-left: -8px; +} +.ui-button-text-icon-primary .ui-button-icon-primary, +.ui-button-text-icons .ui-button-icon-primary, +.ui-button-icons-only .ui-button-icon-primary { + left: .5em; +} +.ui-button-text-icon-secondary .ui-button-icon-secondary, +.ui-button-text-icons .ui-button-icon-secondary, +.ui-button-icons-only .ui-button-icon-secondary { + right: .5em; +} + +/* button sets */ +.ui-buttonset { + margin-right: 7px; +} +.ui-buttonset .ui-button { + margin-left: 0; + margin-right: -.3em; +} + +/* workarounds */ +/* reset extra padding in Firefox, see h5bp.com/l */ +input.ui-button::-moz-focus-inner, +button.ui-button::-moz-focus-inner { + border: 0; + padding: 0; +} +.ui-datepicker { + width: 17em; + padding: .2em .2em 0; + display: none; +} +.ui-datepicker .ui-datepicker-header { + position: relative; + padding: .2em 0; +} +.ui-datepicker .ui-datepicker-prev, +.ui-datepicker .ui-datepicker-next { + position: absolute; + top: 2px; + width: 1.8em; + height: 1.8em; +} +.ui-datepicker .ui-datepicker-prev-hover, +.ui-datepicker .ui-datepicker-next-hover { + top: 1px; +} +.ui-datepicker .ui-datepicker-prev { + left: 2px; +} +.ui-datepicker .ui-datepicker-next { + right: 2px; +} +.ui-datepicker .ui-datepicker-prev-hover { + left: 1px; +} +.ui-datepicker .ui-datepicker-next-hover { + right: 1px; +} +.ui-datepicker .ui-datepicker-prev span, +.ui-datepicker .ui-datepicker-next span { + display: block; + position: absolute; + left: 50%; + margin-left: -8px; + top: 50%; + margin-top: -8px; +} +.ui-datepicker .ui-datepicker-title { + margin: 0 2.3em; + line-height: 1.8em; + text-align: center; +} +.ui-datepicker .ui-datepicker-title select { + font-size: 1em; + margin: 1px 0; +} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { + width: 49%; +} +.ui-datepicker table { + width: 100%; + font-size: .9em; + border-collapse: collapse; + margin: 0 0 .4em; +} +.ui-datepicker th { + padding: .7em .3em; + text-align: center; + font-weight: bold; + border: 0; +} +.ui-datepicker td { + border: 0; + padding: 1px; +} +.ui-datepicker td span, +.ui-datepicker td a { + display: block; + padding: .2em; + text-align: right; + text-decoration: none; +} +.ui-datepicker .ui-datepicker-buttonpane { + background-image: none; + margin: .7em 0 0 0; + padding: 0 .2em; + border-left: 0; + border-right: 0; + border-bottom: 0; +} +.ui-datepicker .ui-datepicker-buttonpane button { + float: right; + margin: .5em .2em .4em; + cursor: pointer; + padding: .2em .6em .3em .6em; + width: auto; + overflow: visible; +} +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { + float: left; +} + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { + width: auto; +} +.ui-datepicker-multi .ui-datepicker-group { + float: left; +} +.ui-datepicker-multi .ui-datepicker-group table { + width: 95%; + margin: 0 auto .4em; +} +.ui-datepicker-multi-2 .ui-datepicker-group { + width: 50%; +} +.ui-datepicker-multi-3 .ui-datepicker-group { + width: 33.3%; +} +.ui-datepicker-multi-4 .ui-datepicker-group { + width: 25%; +} +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { + border-left-width: 0; +} +.ui-datepicker-multi .ui-datepicker-buttonpane { + clear: left; +} +.ui-datepicker-row-break { + clear: both; + width: 100%; + font-size: 0; +} + +/* RTL support */ +.ui-datepicker-rtl { + direction: rtl; +} +.ui-datepicker-rtl .ui-datepicker-prev { + right: 2px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next { + left: 2px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-prev:hover { + right: 1px; + left: auto; +} +.ui-datepicker-rtl .ui-datepicker-next:hover { + left: 1px; + right: auto; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane { + clear: right; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button { + float: left; +} +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current, +.ui-datepicker-rtl .ui-datepicker-group { + float: right; +} +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header, +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { + border-right-width: 0; + border-left-width: 1px; +} +.ui-dialog { + overflow: hidden; + position: absolute; + top: 0; + left: 0; + padding: .2em; + outline: 0; +} +.ui-dialog .ui-dialog-titlebar { + padding: .4em 1em; + position: relative; +} +.ui-dialog .ui-dialog-title { + float: left; + margin: .1em 0; + white-space: nowrap; + width: 90%; + overflow: hidden; + text-overflow: ellipsis; +} +.ui-dialog .ui-dialog-titlebar-close { + position: absolute; + right: .3em; + top: 50%; + width: 20px; + margin: -10px 0 0 0; + padding: 1px; + height: 20px; +} +.ui-dialog .ui-dialog-content { + position: relative; + border: 0; + padding: .5em 1em; + background: none; + overflow: auto; +} +.ui-dialog .ui-dialog-buttonpane { + text-align: left; + border-width: 1px 0 0 0; + background-image: none; + margin-top: .5em; + padding: .3em 1em .5em .4em; +} +.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset { + float: right; +} +.ui-dialog .ui-dialog-buttonpane button { + margin: .5em .4em .5em 0; + cursor: pointer; +} +.ui-dialog .ui-resizable-se { + width: 12px; + height: 12px; + right: -5px; + bottom: -5px; + background-position: 16px 16px; +} +.ui-draggable .ui-dialog-titlebar { + cursor: move; +} +.ui-menu { + list-style: none; + padding: 2px; + margin: 0; + display: block; + outline: none; +} +.ui-menu .ui-menu { + margin-top: -3px; + position: absolute; +} +.ui-menu .ui-menu-item { + margin: 0; + padding: 0; + width: 100%; + /* support: IE10, see #8844 */ + list-style-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7); +} +.ui-menu .ui-menu-divider { + margin: 5px -2px 5px -2px; + height: 0; + font-size: 0; + line-height: 0; + border-width: 1px 0 0 0; +} +.ui-menu .ui-menu-item a { + text-decoration: none; + display: block; + padding: 2px .4em; + line-height: 1.5; + min-height: 0; /* support: IE7 */ + font-weight: normal; +} +.ui-menu .ui-menu-item a.ui-state-focus, +.ui-menu .ui-menu-item a.ui-state-active { + font-weight: normal; + margin: -1px; +} + +.ui-menu .ui-state-disabled { + font-weight: normal; + margin: .4em 0 .2em; + line-height: 1.5; +} +.ui-menu .ui-state-disabled a { + cursor: default; +} + +/* icon support */ +.ui-menu-icons { + position: relative; +} +.ui-menu-icons .ui-menu-item a { + position: relative; + padding-left: 2em; +} + +/* left-aligned */ +.ui-menu .ui-icon { + position: absolute; + top: .2em; + left: .2em; +} + +/* right-aligned */ +.ui-menu .ui-menu-icon { + position: static; + float: right; +} +.ui-progressbar { + height: 2em; + text-align: left; + overflow: hidden; +} +.ui-progressbar .ui-progressbar-value { + margin: -1px; + height: 100%; +} +.ui-progressbar .ui-progressbar-overlay { + background: url("images/animated-overlay.gif"); + height: 100%; + filter: alpha(opacity=25); + opacity: 0.25; +} +.ui-progressbar-indeterminate .ui-progressbar-value { + background-image: none; +} +.ui-slider { + position: relative; + text-align: left; +} +.ui-slider .ui-slider-handle { + position: absolute; + z-index: 2; + width: 1.2em; + height: 1.2em; + cursor: default; +} +.ui-slider .ui-slider-range { + position: absolute; + z-index: 1; + font-size: .7em; + display: block; + border: 0; + background-position: 0 0; +} + +/* For IE8 - See #6727 */ +.ui-slider.ui-state-disabled .ui-slider-handle, +.ui-slider.ui-state-disabled .ui-slider-range { + filter: inherit; +} + +.ui-slider-horizontal { + height: .8em; +} +.ui-slider-horizontal .ui-slider-handle { + top: -.3em; + margin-left: -.6em; +} +.ui-slider-horizontal .ui-slider-range { + top: 0; + height: 100%; +} +.ui-slider-horizontal .ui-slider-range-min { + left: 0; +} +.ui-slider-horizontal .ui-slider-range-max { + right: 0; +} + +.ui-slider-vertical { + width: .8em; + height: 100px; +} +.ui-slider-vertical .ui-slider-handle { + left: -.3em; + margin-left: 0; + margin-bottom: -.6em; +} +.ui-slider-vertical .ui-slider-range { + left: 0; + width: 100%; +} +.ui-slider-vertical .ui-slider-range-min { + bottom: 0; +} +.ui-slider-vertical .ui-slider-range-max { + top: 0; +} +.ui-spinner { + position: relative; + display: inline-block; + overflow: hidden; + padding: 0; + vertical-align: middle; +} +.ui-spinner-input { + border: none; + background: none; + color: inherit; + padding: 0; + margin: .2em 0; + vertical-align: middle; + margin-left: .4em; + margin-right: 22px; +} +.ui-spinner-button { + width: 16px; + height: 50%; + font-size: .5em; + padding: 0; + margin: 0; + text-align: center; + position: absolute; + cursor: default; + display: block; + overflow: hidden; + right: 0; +} +/* more specificity required here to override default borders */ +.ui-spinner a.ui-spinner-button { + border-top: none; + border-bottom: none; + border-right: none; +} +/* vertically center icon */ +.ui-spinner .ui-icon { + position: absolute; + margin-top: -8px; + top: 50%; + left: 0; +} +.ui-spinner-up { + top: 0; +} +.ui-spinner-down { + bottom: 0; +} + +/* TR overrides */ +.ui-spinner .ui-icon-triangle-1-s { + /* need to fix icons sprite */ + background-position: -65px -16px; +} +.ui-tabs { + position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */ + padding: .2em; +} +.ui-tabs .ui-tabs-nav { + margin: 0; + padding: .2em .2em 0; +} +.ui-tabs .ui-tabs-nav li { + list-style: none; + float: left; + position: relative; + top: 0; + margin: 1px .2em 0 0; + border-bottom-width: 0; + padding: 0; + white-space: nowrap; +} +.ui-tabs .ui-tabs-nav .ui-tabs-anchor { + float: left; + padding: .5em 1em; + text-decoration: none; +} +.ui-tabs .ui-tabs-nav li.ui-tabs-active { + margin-bottom: -1px; + padding-bottom: 1px; +} +.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor, +.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor, +.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor { + cursor: text; +} +.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor { + cursor: pointer; +} +.ui-tabs .ui-tabs-panel { + display: block; + border-width: 0; + padding: 1em 1.4em; + background: none; +} +.ui-tooltip { + padding: 8px; + position: absolute; + z-index: 9999; + max-width: 300px; + -webkit-box-shadow: 0 0 5px #aaa; + box-shadow: 0 0 5px #aaa; +} +body .ui-tooltip { + border-width: 2px; +} + +/* Component containers +----------------------------------*/ +.ui-widget { + font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif; + font-size: 1.1em; +} +.ui-widget .ui-widget { + font-size: 1em; +} +.ui-widget input, +.ui-widget select, +.ui-widget textarea, +.ui-widget button { + font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif; + font-size: 1em; +} +.ui-widget-content { + border: 1px solid #dddddd; + background: #eeeeee url("images/ui-bg_highlight-soft_100_eeeeee_1x100.png") 50% top repeat-x; + color: #333333; +} +.ui-widget-content a { + color: #333333; +} +.ui-widget-header { + border: 1px solid #e78f08; + background: #f6a828 url("images/ui-bg_gloss-wave_35_f6a828_500x100.png") 50% 50% repeat-x; + color: #ffffff; + font-weight: bold; +} +.ui-widget-header a { + color: #ffffff; +} + +/* Interaction states +----------------------------------*/ +.ui-state-default, +.ui-widget-content .ui-state-default, +.ui-widget-header .ui-state-default { + border: 1px solid #cccccc; + background: #f6f6f6 url("images/ui-bg_glass_100_f6f6f6_1x400.png") 50% 50% repeat-x; + font-weight: bold; + color: #1c94c4; +} +.ui-state-default a, +.ui-state-default a:link, +.ui-state-default a:visited { + color: #1c94c4; + text-decoration: none; +} +.ui-state-hover, +.ui-widget-content .ui-state-hover, +.ui-widget-header .ui-state-hover, +.ui-state-focus, +.ui-widget-content .ui-state-focus, +.ui-widget-header .ui-state-focus { + border: 1px solid #fbcb09; + background: #fdf5ce url("images/ui-bg_glass_100_fdf5ce_1x400.png") 50% 50% repeat-x; + font-weight: bold; + color: #c77405; +} +.ui-state-hover a, +.ui-state-hover a:hover, +.ui-state-hover a:link, +.ui-state-hover a:visited, +.ui-state-focus a, +.ui-state-focus a:hover, +.ui-state-focus a:link, +.ui-state-focus a:visited { + color: #c77405; + text-decoration: none; +} +.ui-state-active, +.ui-widget-content .ui-state-active, +.ui-widget-header .ui-state-active { + border: 1px solid #fbd850; + background: #ffffff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x; + font-weight: bold; + color: #eb8f00; +} +.ui-state-active a, +.ui-state-active a:link, +.ui-state-active a:visited { + color: #eb8f00; + text-decoration: none; +} + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, +.ui-widget-content .ui-state-highlight, +.ui-widget-header .ui-state-highlight { + border: 1px solid #fed22f; + background: #ffe45c url("images/ui-bg_highlight-soft_75_ffe45c_1x100.png") 50% top repeat-x; + color: #363636; +} +.ui-state-highlight a, +.ui-widget-content .ui-state-highlight a, +.ui-widget-header .ui-state-highlight a { + color: #363636; +} +.ui-state-error, +.ui-widget-content .ui-state-error, +.ui-widget-header .ui-state-error { + border: 1px solid #cd0a0a; + background: #b81900 url("images/ui-bg_diagonals-thick_18_b81900_40x40.png") 50% 50% repeat; + color: #ffffff; +} +.ui-state-error a, +.ui-widget-content .ui-state-error a, +.ui-widget-header .ui-state-error a { + color: #ffffff; +} +.ui-state-error-text, +.ui-widget-content .ui-state-error-text, +.ui-widget-header .ui-state-error-text { + color: #ffffff; +} +.ui-priority-primary, +.ui-widget-content .ui-priority-primary, +.ui-widget-header .ui-priority-primary { + font-weight: bold; +} +.ui-priority-secondary, +.ui-widget-content .ui-priority-secondary, +.ui-widget-header .ui-priority-secondary { + opacity: .7; + filter:Alpha(Opacity=70); + font-weight: normal; +} +.ui-state-disabled, +.ui-widget-content .ui-state-disabled, +.ui-widget-header .ui-state-disabled { + opacity: .35; + filter:Alpha(Opacity=35); + background-image: none; +} +.ui-state-disabled .ui-icon { + filter:Alpha(Opacity=35); /* For IE8 - See #6059 */ +} + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + width: 16px; + height: 16px; +} +.ui-icon, +.ui-widget-content .ui-icon { + background-image: url("images/ui-icons_222222_256x240.png"); +} +.ui-widget-header .ui-icon { + background-image: url("images/ui-icons_ffffff_256x240.png"); +} +.ui-state-default .ui-icon { + background-image: url("images/ui-icons_ef8c08_256x240.png"); +} +.ui-state-hover .ui-icon, +.ui-state-focus .ui-icon { + background-image: url("images/ui-icons_ef8c08_256x240.png"); +} +.ui-state-active .ui-icon { + background-image: url("images/ui-icons_ef8c08_256x240.png"); +} +.ui-state-highlight .ui-icon { + background-image: url("images/ui-icons_228ef1_256x240.png"); +} +.ui-state-error .ui-icon, +.ui-state-error-text .ui-icon { + background-image: url("images/ui-icons_ffd27a_256x240.png"); +} + +/* positioning */ +.ui-icon-blank { background-position: 16px 16px; } +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-on { background-position: -96px -144px; } +.ui-icon-radio-off { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-all, +.ui-corner-top, +.ui-corner-left, +.ui-corner-tl { + border-top-left-radius: 4px; +} +.ui-corner-all, +.ui-corner-top, +.ui-corner-right, +.ui-corner-tr { + border-top-right-radius: 4px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-left, +.ui-corner-bl { + border-bottom-left-radius: 4px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-right, +.ui-corner-br { + border-bottom-right-radius: 4px; +} + +/* Overlays */ +.ui-widget-overlay { + background: #666666 url("images/ui-bg_diagonals-thick_20_666666_40x40.png") 50% 50% repeat; + opacity: .5; + filter: Alpha(Opacity=50); +} +.ui-widget-shadow { + margin: -5px 0 0 -5px; + padding: 5px; + background: #000000 url("images/ui-bg_flat_10_000000_40x100.png") 50% 50% repeat-x; + opacity: .2; + filter: Alpha(Opacity=20); + border-radius: 5px; +} diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/jquery-ui-1.10.4.custom.min.css b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/jquery-ui-1.10.4.custom.min.css new file mode 100644 index 0000000000..b1f2f3c65f --- /dev/null +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/css/ui-lightness/jquery-ui-1.10.4.custom.min.css @@ -0,0 +1,7 @@ +/*! jQuery UI - v1.10.4 - 2014-06-03 +* http://jqueryui.com +* Includes: jquery.ui.core.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css, jquery.ui.theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS%2CTahoma%2CVerdana%2CArial%2Csans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=gloss_wave&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=highlight_soft&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=glass&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=glass&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=highlight_soft&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=diagonals_thick&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=diagonals_thick&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=flat&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px +* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ + +.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin-top:2px;padding:.5em .5em .5em .7em;min-height:0}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-noicons{padding-left:.7em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:49%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-dialog{overflow:hidden;position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-menu{list-style:none;padding:2px;margin:0;display:block;outline:none}.ui-menu .ui-menu{margin-top:-3px;position:absolute}.ui-menu .ui-menu-item{margin:0;padding:0;width:100%;list-style-image:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}.ui-menu .ui-menu-divider{margin:5px -2px 5px -2px;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-menu-item a{text-decoration:none;display:block;padding:2px .4em;line-height:1.5;min-height:0;font-weight:normal}.ui-menu .ui-menu-item a.ui-state-focus,.ui-menu .ui-menu-item a.ui-state-active{font-weight:normal;margin:-1px}.ui-menu .ui-state-disabled{font-weight:normal;margin:.4em 0 .2em;line-height:1.5}.ui-menu .ui-state-disabled a{cursor:default}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item a{position:relative;padding-left:2em}.ui-menu .ui-icon{position:absolute;top:.2em;left:.2em}.ui-menu .ui-menu-icon{position:static;float:right}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("images/animated-overlay.gif");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:none;border-bottom:none;border-right:none}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #ddd;background:#eee url("images/ui-bg_highlight-soft_100_eeeeee_1x100.png") 50% top repeat-x;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #e78f08;background:#f6a828 url("images/ui-bg_gloss-wave_35_f6a828_500x100.png") 50% 50% repeat-x;color:#fff;font-weight:bold}.ui-widget-header a{color:#fff}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #ccc;background:#f6f6f6 url("images/ui-bg_glass_100_f6f6f6_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#1c94c4}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#1c94c4;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #fbcb09;background:#fdf5ce url("images/ui-bg_glass_100_fdf5ce_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#c77405}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited{color:#c77405;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #fbd850;background:#fff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;font-weight:bold;color:#eb8f00}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#eb8f00;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fed22f;background:#ffe45c url("images/ui-bg_highlight-soft_75_ffe45c_1x100.png") 50% top repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#b81900 url("images/ui-bg_diagonals-thick_18_b81900_40x40.png") 50% 50% repeat;color:#fff}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#fff}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#fff}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-default .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-active .ui-icon{background-image:url("images/ui-icons_ef8c08_256x240.png")}.ui-state-highlight .ui-icon{background-image:url("images/ui-icons_228ef1_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_ffd27a_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px}.ui-widget-overlay{background:#666 url("images/ui-bg_diagonals-thick_20_666666_40x40.png") 50% 50% repeat;opacity:.5;filter:Alpha(Opacity=50)}.ui-widget-shadow{margin:-5px 0 0 -5px;padding:5px;background:#000 url("images/ui-bg_flat_10_000000_40x100.png") 50% 50% repeat-x;opacity:.2;filter:Alpha(Opacity=20);border-radius:5px} \ No newline at end of file diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/images/logo_small.png b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/images/logo_small.png index 5496a65579..46c02e2aeb 100644 Binary files a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/images/logo_small.png and b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/images/logo_small.png differ diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/index.html b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/index.html index b8f67750c3..2816f79e24 100644 --- a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/index.html +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/index.html @@ -1,83 +1,118 @@ - Swagger UI - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - $('#input_apiKey').change(function() { - var key = $('#input_apiKey')[0].value; - console.log("key: " + key); - if(key && key.trim() != "") { - console.log("added key " + key); - window.authorizations.add("key", new ApiKeyAuthorization("api_key", key, "query")); - } - }) - window.swaggerUi.load(); - }); + + //clears the swagger UI and adds text prompting use to select a mount point. + var selectAMount = function(string) { + $("#swagger-ui-container").empty(); + $("#message").empty(); + $("#message").append("

Select a mount point.

"); + } + + //loads the root swagger documenation (which comes from RestConf) + var loadRootSwagger = function() { + $("#message").empty(); + loadSwagger("http://localhost:8080/apidoc/apis", "swagger-ui-container"); + } + + //main method to initialize the mount list / swagger docs / tabs on page load + $(function() { + $("#tabs").tabs(); + + loadMountList($("#mountlist")); + + loadRootSwagger(); + }); + - +
+ +
+ + + + + +

OpenDaylight RestConf API + Documentation

+
+
-
-   -
+ -
+ + +
+
+

Below are the list of APIs supported by the Controller.

+
+
+
+
+

Mount Points - Select an API below for details on available + queries.

+
+
+
+
+ +

+ + +
+ + +
+
 
diff --git a/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/lib/jquery-ui-1.11.0.min.js b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/lib/jquery-ui-1.11.0.min.js new file mode 100644 index 0000000000..0e0a79ec62 --- /dev/null +++ b/opendaylight/md-sal/sal-rest-docgen/src/main/resources/explorer/lib/jquery-ui-1.11.0.min.js @@ -0,0 +1,13 @@ +/*! jQuery UI - v1.11.0-beta.2 - 2014-06-08 +* http://jqueryui.com +* Includes: core.js, widget.js, mouse.js, position.js, draggable.js, droppable.js, resizable.js, selectable.js, sortable.js, accordion.js, autocomplete.js, button.js, datepicker.js, dialog.js, menu.js, progressbar.js, selectmenu.js, slider.js, spinner.js, tabs.js, tooltip.js, effect.js, effect-blind.js, effect-bounce.js, effect-clip.js, effect-drop.js, effect-explode.js, effect-fade.js, effect-fold.js, effect-highlight.js, effect-puff.js, effect-pulsate.js, effect-scale.js, effect-shake.js, effect-size.js, effect-slide.js, effect-transfer.js +* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ + +(function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e(jQuery)})(function(e){function t(t,i){var r,s,n,o=t.nodeName.toLowerCase();return"area"===o?(r=t.parentNode,s=r.name,t.href&&s&&"map"===r.nodeName.toLowerCase()?(n=e("img[usemap=#"+s+"]")[0],!!n&&a(n)):!1):(/input|select|textarea|button|object/.test(o)?!t.disabled:"a"===o?t.href||i:i)&&a(t)}function a(t){return e.expr.filters.visible(t)&&!e(t).parents().addBack().filter(function(){return"hidden"===e.css(this,"visibility")}).length}function i(e){for(var t,a;e.length&&e[0]!==document;){if(t=e.css("position"),("absolute"===t||"relative"===t||"fixed"===t)&&(a=parseInt(e.css("zIndex"),10),!isNaN(a)&&0!==a))return a;e=e.parent()}return 0}function r(){this._curInst=null,this._keyEvent=!1,this._disabledInputs=[],this._datepickerShowing=!1,this._inDialog=!1,this._mainDivId="ui-datepicker-div",this._inlineClass="ui-datepicker-inline",this._appendClass="ui-datepicker-append",this._triggerClass="ui-datepicker-trigger",this._dialogClass="ui-datepicker-dialog",this._disableClass="ui-datepicker-disabled",this._unselectableClass="ui-datepicker-unselectable",this._currentClass="ui-datepicker-current-day",this._dayOverClass="ui-datepicker-days-cell-over",this.regional=[],this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],weekHeader:"Wk",dateFormat:"mm/dd/yy",firstDay:0,isRTL:!1,showMonthAfterYear:!1,yearSuffix:""},this._defaults={showOn:"focus",showAnim:"fadeIn",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:!1,hideIfNoPrevNext:!1,navigationAsDateFormat:!1,gotoCurrent:!1,changeMonth:!1,changeYear:!1,yearRange:"c-10:c+10",showOtherMonths:!1,selectOtherMonths:!1,showWeek:!1,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"fast",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:!0,showButtonPanel:!1,autoSize:!1,disabled:!1},e.extend(this._defaults,this.regional[""]),this.dpDiv=s(e("
"))}function s(t){var a="button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";return t.delegate(a,"mouseout",function(){e(this).removeClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).removeClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).removeClass("ui-datepicker-next-hover")}).delegate(a,"mouseover",function(){e.datepicker._isDisabledDatepicker(g.inline?t.parent()[0]:g.input[0])||(e(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover"),e(this).addClass("ui-state-hover"),-1!==this.className.indexOf("ui-datepicker-prev")&&e(this).addClass("ui-datepicker-prev-hover"),-1!==this.className.indexOf("ui-datepicker-next")&&e(this).addClass("ui-datepicker-next-hover"))})}function n(t,a){e.extend(t,a);for(var i in a)null==a[i]&&(t[i]=a[i]);return t}function o(e){return function(){var t=this.element.val();e.apply(this,arguments),this._refresh(),t!==this.element.val()&&this._trigger("change")}}e.ui=e.ui||{},e.extend(e.ui,{version:"1.11.0-beta.2",keyCode:{BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38}}),e.fn.extend({scrollParent:function(){var t=this.css("position"),a="absolute"===t,i=this.parents().filter(function(){var t=e(this);return a&&"static"===t.css("position")?!1:/(auto|scroll)/.test(t.css("overflow")+t.css("overflow-y")+t.css("overflow-x"))}).eq(0);return"fixed"!==t&&i.length?i:e(this[0].ownerDocument||document)},uniqueId:function(){var e=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++e)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&e(this).removeAttr("id")})}}),e.extend(e.expr[":"],{data:e.expr.createPseudo?e.expr.createPseudo(function(t){return function(a){return!!e.data(a,t)}}):function(t,a,i){return!!e.data(t,i[3])},focusable:function(a){return t(a,!isNaN(e.attr(a,"tabindex")))},tabbable:function(a){var i=e.attr(a,"tabindex"),r=isNaN(i);return(r||i>=0)&&t(a,!r)}}),e("").outerWidth(1).jquery||e.each(["Width","Height"],function(t,a){function i(t,a,i,s){return e.each(r,function(){a-=parseFloat(e.css(t,"padding"+this))||0,i&&(a-=parseFloat(e.css(t,"border"+this+"Width"))||0),s&&(a-=parseFloat(e.css(t,"margin"+this))||0)}),a}var r="Width"===a?["Left","Right"]:["Top","Bottom"],s=a.toLowerCase(),n={innerWidth:e.fn.innerWidth,innerHeight:e.fn.innerHeight,outerWidth:e.fn.outerWidth,outerHeight:e.fn.outerHeight};e.fn["inner"+a]=function(t){return void 0===t?n["inner"+a].call(this):this.each(function(){e(this).css(s,i(this,t)+"px")})},e.fn["outer"+a]=function(t,r){return"number"!=typeof t?n["outer"+a].call(this,t):this.each(function(){e(this).css(s,i(this,t,!0,r)+"px")})}}),e.fn.addBack||(e.fn.addBack=function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}),e("").data("a-b","a").removeData("a-b").data("a-b")&&(e.fn.removeData=function(t){return function(a){return arguments.length?t.call(this,e.camelCase(a)):t.call(this)}}(e.fn.removeData)),e.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase()),e.fn.extend({focus:function(t){return function(a,i){return"number"==typeof a?this.each(function(){var t=this;setTimeout(function(){e(t).focus(),i&&i.call(t)},a)}):t.apply(this,arguments)}}(e.fn.focus),disableSelection:function(){var e="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.bind(e+".ui-disableSelection",function(e){e.preventDefault()})}}(),enableSelection:function(){return this.unbind(".ui-disableSelection")},zIndex:function(t){if(void 0!==t)return this.css("zIndex",t);if(this.length)for(var a,i,r=e(this[0]);r.length&&r[0]!==document;){if(a=r.css("position"),("absolute"===a||"relative"===a||"fixed"===a)&&(i=parseInt(r.css("zIndex"),10),!isNaN(i)&&0!==i))return i;r=r.parent()}return 0}}),e.ui.plugin={add:function(t,a,i){var r,s=e.ui[t].prototype;for(r in i)s.plugins[r]=s.plugins[r]||[],s.plugins[r].push([a,i[r]])},call:function(e,t,a,i){var r,s=e.plugins[t];if(s&&(i||e.element[0].parentNode&&11!==e.element[0].parentNode.nodeType))for(r=0;s.length>r;r++)e.options[s[r][0]]&&s[r][1].apply(e.element,a)}};var d=0,u=Array.prototype.slice;e.cleanData=function(t){return function(a){for(var i,r=0;null!=(i=a[r]);r++)try{e(i).triggerHandler("remove")}catch(s){}t(a)}}(e.cleanData),e.widget=function(t,a,i){var r,s,n,o,d={},u=t.split(".")[0];return t=t.split(".")[1],r=u+"-"+t,i||(i=a,a=e.Widget),e.expr[":"][r.toLowerCase()]=function(t){return!!e.data(t,r)},e[u]=e[u]||{},s=e[u][t],n=e[u][t]=function(e,t){return this._createWidget?(arguments.length&&this._createWidget(e,t),void 0):new n(e,t)},e.extend(n,s,{version:i.version,_proto:e.extend({},i),_childConstructors:[]}),o=new a,o.options=e.widget.extend({},o.options),e.each(i,function(t,i){return e.isFunction(i)?(d[t]=function(){var e=function(){return a.prototype[t].apply(this,arguments)},r=function(e){return a.prototype[t].apply(this,e)};return function(){var t,a=this._super,s=this._superApply;return this._super=e,this._superApply=r,t=i.apply(this,arguments),this._super=a,this._superApply=s,t}}(),void 0):(d[t]=i,void 0)}),n.prototype=e.widget.extend(o,{widgetEventPrefix:s?o.widgetEventPrefix||t:t},d,{constructor:n,namespace:u,widgetName:t,widgetFullName:r}),s?(e.each(s._childConstructors,function(t,a){var i=a.prototype;e.widget(i.namespace+"."+i.widgetName,n,a._proto)}),delete s._childConstructors):a._childConstructors.push(n),e.widget.bridge(t,n),n},e.widget.extend=function(t){for(var a,i,r=u.call(arguments,1),s=0,n=r.length;n>s;s++)for(a in r[s])i=r[s][a],r[s].hasOwnProperty(a)&&void 0!==i&&(t[a]=e.isPlainObject(i)?e.isPlainObject(t[a])?e.widget.extend({},t[a],i):e.widget.extend({},i):i);return t},e.widget.bridge=function(t,a){var i=a.prototype.widgetFullName||t;e.fn[t]=function(r){var s="string"==typeof r,n=u.call(arguments,1),o=this;return r=!s&&n.length?e.widget.extend.apply(null,[r].concat(n)):r,s?this.each(function(){var a,s=e.data(this,i);return"instance"===r?(o=s,!1):s?e.isFunction(s[r])&&"_"!==r.charAt(0)?(a=s[r].apply(s,n),a!==s&&void 0!==a?(o=a&&a.jquery?o.pushStack(a.get()):a,!1):void 0):e.error("no such method '"+r+"' for "+t+" widget instance"):e.error("cannot call methods on "+t+" prior to initialization; "+"attempted to call method '"+r+"'")}):this.each(function(){var t=e.data(this,i);t?(t.option(r||{}),t._init&&t._init()):e.data(this,i,new a(r,this))}),o}},e.Widget=function(){},e.Widget._childConstructors=[],e.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{disabled:!1,create:null},_createWidget:function(t,a){a=e(a||this.defaultElement||this)[0],this.element=e(a),this.uuid=d++,this.eventNamespace="."+this.widgetName+this.uuid,this.options=e.widget.extend({},this.options,this._getCreateOptions(),t),this.bindings=e(),this.hoverable=e(),this.focusable=e(),a!==this&&(e.data(a,this.widgetFullName,this),this._on(!0,this.element,{remove:function(e){e.target===a&&this.destroy()}}),this.document=e(a.style?a.ownerDocument:a.document||a),this.window=e(this.document[0].defaultView||this.document[0].parentWindow)),this._create(),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:e.noop,_getCreateEventData:e.noop,_create:e.noop,_init:e.noop,destroy:function(){this._destroy(),this.element.unbind(this.eventNamespace).removeData(this.widgetFullName).removeData(e.camelCase(this.widgetFullName)),this.widget().unbind(this.eventNamespace).removeAttr("aria-disabled").removeClass(this.widgetFullName+"-disabled "+"ui-state-disabled"),this.bindings.unbind(this.eventNamespace),this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus")},_destroy:e.noop,widget:function(){return this.element},option:function(t,a){var i,r,s,n=t;if(0===arguments.length)return e.widget.extend({},this.options);if("string"==typeof t)if(n={},i=t.split("."),t=i.shift(),i.length){for(r=n[t]=e.widget.extend({},this.options[t]),s=0;i.length-1>s;s++)r[i[s]]=r[i[s]]||{},r=r[i[s]];if(t=i.pop(),1===arguments.length)return void 0===r[t]?null:r[t];r[t]=a}else{if(1===arguments.length)return void 0===this.options[t]?null:this.options[t];n[t]=a}return this._setOptions(n),this},_setOptions:function(e){var t;for(t in e)this._setOption(t,e[t]);return this},_setOption:function(e,t){return this.options[e]=t,"disabled"===e&&(this.widget().toggleClass(this.widgetFullName+"-disabled",!!t),t&&(this.hoverable.removeClass("ui-state-hover"),this.focusable.removeClass("ui-state-focus"))),this},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_on:function(t,a,i){var r,s=this;"boolean"!=typeof t&&(i=a,a=t,t=!1),i?(a=r=e(a),this.bindings=this.bindings.add(a)):(i=a,a=this.element,r=this.widget()),e.each(i,function(i,n){function o(){return t||s.options.disabled!==!0&&!e(this).hasClass("ui-state-disabled")?("string"==typeof n?s[n]:n).apply(s,arguments):void 0}"string"!=typeof n&&(o.guid=n.guid=n.guid||o.guid||e.guid++);var d=i.match(/^([\w:-]*)\s*(.*)$/),u=d[1]+s.eventNamespace,h=d[2];h?r.delegate(h,u,o):a.bind(u,o)})},_off:function(e,t){t=(t||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.unbind(t).undelegate(t)},_delay:function(e,t){function a(){return("string"==typeof e?i[e]:e).apply(i,arguments)}var i=this;return setTimeout(a,t||0)},_hoverable:function(t){this.hoverable=this.hoverable.add(t),this._on(t,{mouseenter:function(t){e(t.currentTarget).addClass("ui-state-hover")},mouseleave:function(t){e(t.currentTarget).removeClass("ui-state-hover")}})},_focusable:function(t){this.focusable=this.focusable.add(t),this._on(t,{focusin:function(t){e(t.currentTarget).addClass("ui-state-focus")},focusout:function(t){e(t.currentTarget).removeClass("ui-state-focus")}})},_trigger:function(t,a,i){var r,s,n=this.options[t];if(i=i||{},a=e.Event(a),a.type=(t===this.widgetEventPrefix?t:this.widgetEventPrefix+t).toLowerCase(),a.target=this.element[0],s=a.originalEvent)for(r in s)r in a||(a[r]=s[r]);return this.element.trigger(a,i),!(e.isFunction(n)&&n.apply(this.element[0],[a].concat(i))===!1||a.isDefaultPrevented())}},e.each({show:"fadeIn",hide:"fadeOut"},function(t,a){e.Widget.prototype["_"+t]=function(i,r,s){"string"==typeof r&&(r={effect:r});var n,o=r?r===!0||"number"==typeof r?a:r.effect||a:t;r=r||{},"number"==typeof r&&(r={duration:r}),n=!e.isEmptyObject(r),r.complete=s,r.delay&&i.delay(r.delay),n&&e.effects&&e.effects.effect[o]?i[t](r):o!==t&&i[o]?i[o](r.duration,r.easing,s):i.queue(function(a){e(this)[t](),s&&s.call(i[0]),a()})}}),e.widget;var h=!1;e(document).mouseup(function(){h=!1}),e.widget("ui.mouse",{version:"1.11.0-beta.2",options:{cancel:"input,textarea,button,select,option",distance:1,delay:0},_mouseInit:function(){var t=this;this.element.bind("mousedown."+this.widgetName,function(e){return t._mouseDown(e)}).bind("click."+this.widgetName,function(a){return!0===e.data(a.target,t.widgetName+".preventClickEvent")?(e.removeData(a.target,t.widgetName+".preventClickEvent"),a.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.unbind("."+this.widgetName),this._mouseMoveDelegate&&this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(t){if(!h){this._mouseStarted&&this._mouseUp(t),this._mouseDownEvent=t;var a=this,i=1===t.which,r="string"==typeof this.options.cancel&&t.target.nodeName?e(t.target).closest(this.options.cancel).length:!1;return i&&!r&&this._mouseCapture(t)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){a.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(t)!==!1,!this._mouseStarted)?(t.preventDefault(),!0):(!0===e.data(t.target,this.widgetName+".preventClickEvent")&&e.removeData(t.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(e){return a._mouseMove(e)},this._mouseUpDelegate=function(e){return a._mouseUp(e)},this.document.bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate),t.preventDefault(),h=!0,!0)):!0}},_mouseMove:function(t){return e.ui.ie&&(!document.documentMode||9>document.documentMode)&&!t.button?this._mouseUp(t):t.which?this._mouseStarted?(this._mouseDrag(t),t.preventDefault()):(this._mouseDistanceMet(t)&&this._mouseDelayMet(t)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,t)!==!1,this._mouseStarted?this._mouseDrag(t):this._mouseUp(t)),!this._mouseStarted):this._mouseUp(t)},_mouseUp:function(t){return this.document.unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,t.target===this._mouseDownEvent.target&&e.data(t.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(t)),h=!1,!1},_mouseDistanceMet:function(e){return Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),function(){function t(e,t,a){return[parseFloat(e[0])*(c.test(e[0])?t/100:1),parseFloat(e[1])*(c.test(e[1])?a/100:1)]}function a(t,a){return parseInt(e.css(t,a),10)||0}function i(t){var a=t[0];return 9===a.nodeType?{width:t.width(),height:t.height(),offset:{top:0,left:0}}:e.isWindow(a)?{width:t.width(),height:t.height(),offset:{top:t.scrollTop(),left:t.scrollLeft()}}:a.preventDefault?{width:0,height:0,offset:{top:a.pageY,left:a.pageX}}:{width:t.outerWidth(),height:t.outerHeight(),offset:t.offset()}}e.ui=e.ui||{};var r,s,n=Math.max,o=Math.abs,d=Math.round,u=/left|center|right/,h=/top|center|bottom/,l=/[\+\-]\d+(\.[\d]+)?%?/,m=/^\w+/,c=/%$/,p=e.fn.position;e.position={scrollbarWidth:function(){if(void 0!==r)return r;var t,a,i=e("
"),s=i.children()[0];return e("body").append(i),t=s.offsetWidth,i.css("overflow","scroll"),a=s.offsetWidth,t===a&&(a=i[0].clientWidth),i.remove(),r=t-a},getScrollInfo:function(t){var a=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),i=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),r="scroll"===a||"auto"===a&&t.widtha?"left":t>0?"right":"center",vertical:0>s?"top":i>0?"bottom":"middle"};l>f&&f>o(t+a)&&(d.horizontal="center"),m>g&&g>o(i+s)&&(d.vertical="middle"),d.important=n(o(t),o(a))>n(o(i),o(s))?"horizontal":"vertical",r.using.call(this,e,d)}),h.offset(e.extend(w,{using:u}))})},e.ui.position={fit:{left:function(e,t){var a,i=t.within,r=i.isWindow?i.scrollLeft:i.offset.left,s=i.width,o=e.left-t.collisionPosition.marginLeft,d=r-o,u=o+t.collisionWidth-s-r;t.collisionWidth>s?d>0&&0>=u?(a=e.left+d+t.collisionWidth-s-r,e.left+=d-a):e.left=u>0&&0>=d?r:d>u?r+s-t.collisionWidth:r:d>0?e.left+=d:u>0?e.left-=u:e.left=n(e.left-o,e.left)},top:function(e,t){var a,i=t.within,r=i.isWindow?i.scrollTop:i.offset.top,s=t.within.height,o=e.top-t.collisionPosition.marginTop,d=r-o,u=o+t.collisionHeight-s-r;t.collisionHeight>s?d>0&&0>=u?(a=e.top+d+t.collisionHeight-s-r,e.top+=d-a):e.top=u>0&&0>=d?r:d>u?r+s-t.collisionHeight:r:d>0?e.top+=d:u>0?e.top-=u:e.top=n(e.top-o,e.top)}},flip:{left:function(e,t){var a,i,r=t.within,s=r.offset.left+r.scrollLeft,n=r.width,d=r.isWindow?r.scrollLeft:r.offset.left,u=e.left-t.collisionPosition.marginLeft,h=u-d,l=u+t.collisionWidth-n-d,m="left"===t.my[0]?-t.elemWidth:"right"===t.my[0]?t.elemWidth:0,c="left"===t.at[0]?t.targetWidth:"right"===t.at[0]?-t.targetWidth:0,p=-2*t.offset[0];0>h?(a=e.left+m+c+p+t.collisionWidth-n-s,(0>a||o(h)>a)&&(e.left+=m+c+p)):l>0&&(i=e.left-t.collisionPosition.marginLeft+m+c+p-d,(i>0||l>o(i))&&(e.left+=m+c+p))},top:function(e,t){var a,i,r=t.within,s=r.offset.top+r.scrollTop,n=r.height,d=r.isWindow?r.scrollTop:r.offset.top,u=e.top-t.collisionPosition.marginTop,h=u-d,l=u+t.collisionHeight-n-d,m="top"===t.my[1],c=m?-t.elemHeight:"bottom"===t.my[1]?t.elemHeight:0,p="top"===t.at[1]?t.targetHeight:"bottom"===t.at[1]?-t.targetHeight:0,f=-2*t.offset[1];0>h?(i=e.top+c+p+f+t.collisionHeight-n-s,e.top+c+p+f>h&&(0>i||o(h)>i)&&(e.top+=c+p+f)):l>0&&(a=e.top-t.collisionPosition.marginTop+c+p+f-d,e.top+c+p+f>l&&(a>0||l>o(a))&&(e.top+=c+p+f))}},flipfit:{left:function(){e.ui.position.flip.left.apply(this,arguments),e.ui.position.fit.left.apply(this,arguments)},top:function(){e.ui.position.flip.top.apply(this,arguments),e.ui.position.fit.top.apply(this,arguments)}}},function(){var t,a,i,r,n,o=document.getElementsByTagName("body")[0],d=document.createElement("div");t=document.createElement(o?"div":"body"),i={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"},o&&e.extend(i,{position:"absolute",left:"-1000px",top:"-1000px"});for(n in i)t.style[n]=i[n];t.appendChild(d),a=o||document.documentElement,a.insertBefore(t,a.firstChild),d.style.cssText="position: absolute; left: 10.7432222px;",r=e(d).offset().left,s=r>10&&11>r,t.innerHTML="",a.removeChild(t)}()}(),e.ui.position,e.widget("ui.draggable",e.ui.mouse,{version:"1.11.0-beta.2",widgetEventPrefix:"drag",options:{addClasses:!0,appendTo:"parent",axis:!1,connectToSortable:!1,containment:!1,cursor:"auto",cursorAt:!1,grid:!1,handle:!1,helper:"original",iframeFix:!1,opacity:!1,refreshPositions:!1,revert:!1,revertDuration:500,scope:"default",scroll:!0,scrollSensitivity:20,scrollSpeed:20,snap:!1,snapMode:"both",snapTolerance:20,stack:!1,zIndex:!1,drag:null,start:null,stop:null},_create:function(){"original"!==this.options.helper||/^(?:r|a|f)/.test(this.element.css("position"))||(this.element[0].style.position="relative"),this.options.addClasses&&this.element.addClass("ui-draggable"),this.options.disabled&&this.element.addClass("ui-draggable-disabled"),this._setHandleClassName(),this._mouseInit()},_setOption:function(e,t){this._super(e,t),"handle"===e&&this._setHandleClassName()},_destroy:function(){return(this.helper||this.element).is(".ui-draggable-dragging")?(this.destroyOnClear=!0,void 0):(this.element.removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled"),this._removeHandleClassName(),this._mouseDestroy(),void 0)},_mouseCapture:function(t){var a=this.document[0],i=this.options;try{a.activeElement&&"body"!==a.activeElement.nodeName.toLowerCase()&&e(a.activeElement).blur()}catch(r){}return this.helper||i.disabled||e(t.target).closest(".ui-resizable-handle").length>0?!1:(this.handle=this._getHandle(t),this.handle?(e(i.iframeFix===!0?"iframe":i.iframeFix).each(function(){e("
").css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1e3}).css(e(this).offset()).appendTo("body")}),!0):!1)},_mouseStart:function(t){var a=this.options;return this.helper=this._createHelper(t),this.helper.addClass("ui-draggable-dragging"),this._cacheHelperProportions(),e.ui.ddmanager&&(e.ui.ddmanager.current=this),this._cacheMargins(),this.cssPosition=this.helper.css("position"),this.scrollParent=this.helper.scrollParent(),this.offsetParent=this.helper.offsetParent(),this.offsetParentCssPosition=this.offsetParent.css("position"),this.offset=this.positionAbs=this.element.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},this.offset.scroll=!1,e.extend(this.offset,{click:{left:t.pageX-this.offset.left,top:t.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.originalPosition=this.position=this._generatePosition(t,!1),this.originalPageX=t.pageX,this.originalPageY=t.pageY,a.cursorAt&&this._adjustOffsetFromHelper(a.cursorAt),this._setContainment(),this._trigger("start",t)===!1?(this._clear(),!1):(this._cacheHelperProportions(),e.ui.ddmanager&&!a.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this._mouseDrag(t,!0),e.ui.ddmanager&&e.ui.ddmanager.dragStart(this,t),!0)},_mouseDrag:function(t,a){if("fixed"===this.offsetParentCssPosition&&(this.offset.parent=this._getParentOffset()),this.position=this._generatePosition(t,!0),this.positionAbs=this._convertPositionTo("absolute"),!a){var i=this._uiHash();if(this._trigger("drag",t,i)===!1)return this._mouseUp({}),!1;this.position=i.position}return this.helper[0].style.left=this.position.left+"px",this.helper[0].style.top=this.position.top+"px",e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),!1},_mouseStop:function(t){var a=this,i=!1;return e.ui.ddmanager&&!this.options.dropBehaviour&&(i=e.ui.ddmanager.drop(this,t)),this.dropped&&(i=this.dropped,this.dropped=!1),"invalid"===this.options.revert&&!i||"valid"===this.options.revert&&i||this.options.revert===!0||e.isFunction(this.options.revert)&&this.options.revert.call(this.element,i)?e(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration,10),function(){a._trigger("stop",t)!==!1&&a._clear()}):this._trigger("stop",t)!==!1&&this._clear(),!1},_mouseUp:function(t){return e("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)}),e.ui.ddmanager&&e.ui.ddmanager.dragStop(this,t),this.element.focus(),e.ui.mouse.prototype._mouseUp.call(this,t)},cancel:function(){return this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear(),this},_getHandle:function(t){return this.options.handle?!!e(t.target).closest(this.element.find(this.options.handle)).length:!0},_setHandleClassName:function(){this._removeHandleClassName(),e(this.options.handle||this.element).addClass("ui-draggable-handle")},_removeHandleClassName:function(){this.element.find(".ui-draggable-handle").addBack().removeClass("ui-draggable-handle")},_createHelper:function(t){var a=this.options,i=e.isFunction(a.helper)?e(a.helper.apply(this.element[0],[t])):"clone"===a.helper?this.element.clone().removeAttr("id"):this.element;return i.parents("body").length||i.appendTo("parent"===a.appendTo?this.element[0].parentNode:a.appendTo),i[0]===this.element[0]||/(fixed|absolute)/.test(i.css("position"))||i.css("position","absolute"),i},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_isRootNode:function(e){return/(html|body)/i.test(e.tagName)||e===this.document[0]},_getParentOffset:function(){var t=this.offsetParent.offset(),a=this.document[0];return"absolute"===this.cssPosition&&this.scrollParent[0]!==a&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),this._isRootNode(this.offsetParent[0])&&(t={top:0,left:0}),{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"!==this.cssPosition)return{top:0,left:0};var e=this.element.position(),t=this._isRootNode(this.scrollParent[0]);return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+(t?0:this.scrollParent.scrollTop()),left:e.left-(parseInt(this.helper.css("left"),10)||0)+(t?0:this.scrollParent.scrollLeft())}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t,a,i,r=this.options,s=this.document[0];return this.relative_container=null,r.containment?"window"===r.containment?(this.containment=[e(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,e(window).scrollTop()-this.offset.relative.top-this.offset.parent.top,e(window).scrollLeft()+e(window).width()-this.helperProportions.width-this.margins.left,e(window).scrollTop()+(e(window).height()||s.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):"document"===r.containment?(this.containment=[0,0,e(s).width()-this.helperProportions.width-this.margins.left,(e(s).height()||s.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top],void 0):r.containment.constructor===Array?(this.containment=r.containment,void 0):("parent"===r.containment&&(r.containment=this.helper[0].parentNode),a=e(r.containment),i=a[0],i&&(t="hidden"!==a.css("overflow"),this.containment=[(parseInt(a.css("borderLeftWidth"),10)||0)+(parseInt(a.css("paddingLeft"),10)||0),(parseInt(a.css("borderTopWidth"),10)||0)+(parseInt(a.css("paddingTop"),10)||0),(t?Math.max(i.scrollWidth,i.offsetWidth):i.offsetWidth)-(parseInt(a.css("borderRightWidth"),10)||0)-(parseInt(a.css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(t?Math.max(i.scrollHeight,i.offsetHeight):i.offsetHeight)-(parseInt(a.css("borderBottomWidth"),10)||0)-(parseInt(a.css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom],this.relative_container=a),void 0):(this.containment=null,void 0)},_convertPositionTo:function(e,t){t||(t=this.position);var a="absolute"===e?1:-1,i=this._isRootNode(this.scrollParent[0]);return{top:t.top+this.offset.relative.top*a+this.offset.parent.top*a-("fixed"===this.cssPosition?-this.offset.scroll.top:i?0:this.offset.scroll.top)*a,left:t.left+this.offset.relative.left*a+this.offset.parent.left*a-("fixed"===this.cssPosition?-this.offset.scroll.left:i?0:this.offset.scroll.left)*a}},_generatePosition:function(e,t){var a,i,r,s,n=this.options,o=this._isRootNode(this.scrollParent[0]),d=e.pageX,u=e.pageY;return o&&this.offset.scroll||(this.offset.scroll={top:this.scrollParent.scrollTop(),left:this.scrollParent.scrollLeft()}),t&&(this.containment&&(this.relative_container?(i=this.relative_container.offset(),a=[this.containment[0]+i.left,this.containment[1]+i.top,this.containment[2]+i.left,this.containment[3]+i.top]):a=this.containment,e.pageX-this.offset.click.lefta[2]&&(d=a[2]+this.offset.click.left),e.pageY-this.offset.click.top>a[3]&&(u=a[3]+this.offset.click.top)),n.grid&&(r=n.grid[1]?this.originalPageY+Math.round((u-this.originalPageY)/n.grid[1])*n.grid[1]:this.originalPageY,u=a?r-this.offset.click.top>=a[1]||r-this.offset.click.top>a[3]?r:r-this.offset.click.top>=a[1]?r-n.grid[1]:r+n.grid[1]:r,s=n.grid[0]?this.originalPageX+Math.round((d-this.originalPageX)/n.grid[0])*n.grid[0]:this.originalPageX,d=a?s-this.offset.click.left>=a[0]||s-this.offset.click.left>a[2]?s:s-this.offset.click.left>=a[0]?s-n.grid[0]:s+n.grid[0]:s),"y"===n.axis&&(d=this.originalPageX),"x"===n.axis&&(u=this.originalPageY)),{top:u-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.offset.scroll.top:o?0:this.offset.scroll.top),left:d-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.offset.scroll.left:o?0:this.offset.scroll.left)} +},_clear:function(){this.helper.removeClass("ui-draggable-dragging"),this.helper[0]===this.element[0]||this.cancelHelperRemoval||this.helper.remove(),this.helper=null,this.cancelHelperRemoval=!1,this.destroyOnClear&&this.destroy()},_trigger:function(t,a,i){return i=i||this._uiHash(),e.ui.plugin.call(this,t,[a,i,this],!0),"drag"===t&&(this.positionAbs=this._convertPositionTo("absolute")),e.Widget.prototype._trigger.call(this,t,a,i)},plugins:{},_uiHash:function(){return{helper:this.helper,position:this.position,originalPosition:this.originalPosition,offset:this.positionAbs}}}),e.ui.plugin.add("draggable","connectToSortable",{start:function(t,a,i){var r=i.options,s=e.extend({},a,{item:i.element});i.sortables=[],e(r.connectToSortable).each(function(){var a=e(this).sortable("instance");a&&!a.options.disabled&&(i.sortables.push({instance:a,shouldRevert:a.options.revert}),a.refreshPositions(),a._trigger("activate",t,s))})},stop:function(t,a,i){var r=e.extend({},a,{item:i.element});e.each(i.sortables,function(){this.instance.isOver?(this.instance.isOver=0,i.cancelHelperRemoval=!0,this.instance.cancelHelperRemoval=!1,this.shouldRevert&&(this.instance.options.revert=this.shouldRevert),this.instance._mouseStop(t),this.instance.options.helper=this.instance.options._helper,"original"===i.options.helper&&this.instance.currentItem.css({top:"auto",left:"auto"})):(this.instance.cancelHelperRemoval=!1,this.instance._trigger("deactivate",t,r))})},drag:function(t,a,i){var r=this;e.each(i.sortables,function(){var s=!1,n=this;this.instance.positionAbs=i.positionAbs,this.instance.helperProportions=i.helperProportions,this.instance.offset.click=i.offset.click,this.instance._intersectsWith(this.instance.containerCache)&&(s=!0,e.each(i.sortables,function(){return this.instance.positionAbs=i.positionAbs,this.instance.helperProportions=i.helperProportions,this.instance.offset.click=i.offset.click,this!==n&&this.instance._intersectsWith(this.instance.containerCache)&&e.contains(n.instance.element[0],this.instance.element[0])&&(s=!1),s})),s?(this.instance.isOver||(this.instance.isOver=1,this.instance.currentItem=e(r).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item",!0),this.instance.options._helper=this.instance.options.helper,this.instance.options.helper=function(){return a.helper[0]},t.target=this.instance.currentItem[0],this.instance._mouseCapture(t,!0),this.instance._mouseStart(t,!0,!0),this.instance.offset.click.top=i.offset.click.top,this.instance.offset.click.left=i.offset.click.left,this.instance.offset.parent.left-=i.offset.parent.left-this.instance.offset.parent.left,this.instance.offset.parent.top-=i.offset.parent.top-this.instance.offset.parent.top,i._trigger("toSortable",t),i.dropped=this.instance.element,i.currentItem=i.element,this.instance.fromOutside=i),this.instance.currentItem&&this.instance._mouseDrag(t)):this.instance.isOver&&(this.instance.isOver=0,this.instance.cancelHelperRemoval=!0,this.instance.options.revert=!1,this.instance._trigger("out",t,this.instance._uiHash(this.instance)),this.instance._mouseStop(t,!0),this.instance.options.helper=this.instance.options._helper,this.instance.currentItem.remove(),this.instance.placeholder&&this.instance.placeholder.remove(),i._trigger("fromSortable",t),i.dropped=!1)})}}),e.ui.plugin.add("draggable","cursor",{start:function(t,a,i){var r=e("body"),s=i.options;r.css("cursor")&&(s._cursor=r.css("cursor")),r.css("cursor",s.cursor)},stop:function(t,a,i){var r=i.options;r._cursor&&e("body").css("cursor",r._cursor)}}),e.ui.plugin.add("draggable","opacity",{start:function(t,a,i){var r=e(a.helper),s=i.options;r.css("opacity")&&(s._opacity=r.css("opacity")),r.css("opacity",s.opacity)},stop:function(t,a,i){var r=i.options;r._opacity&&e(a.helper).css("opacity",r._opacity)}}),e.ui.plugin.add("draggable","scroll",{start:function(e,t,a){a.scrollParent[0]!==a.document[0]&&"HTML"!==a.scrollParent[0].tagName&&(a.overflowOffset=a.scrollParent.offset())},drag:function(t,a,i){var r=i.options,s=!1,n=i.document[0];i.scrollParent[0]!==n&&"HTML"!==i.scrollParent[0].tagName?(r.axis&&"x"===r.axis||(i.overflowOffset.top+i.scrollParent[0].offsetHeight-t.pageY=0;m--)d=i.snapElements[m].left,u=d+i.snapElements[m].width,h=i.snapElements[m].top,l=h+i.snapElements[m].height,d-f>y||g>u+f||h-f>v||x>l+f||!e.contains(i.snapElements[m].item.ownerDocument,i.snapElements[m].item)?(i.snapElements[m].snapping&&i.options.snap.release&&i.options.snap.release.call(i.element,t,e.extend(i._uiHash(),{snapItem:i.snapElements[m].item})),i.snapElements[m].snapping=!1):("inner"!==p.snapMode&&(r=f>=Math.abs(h-v),s=f>=Math.abs(l-x),n=f>=Math.abs(d-y),o=f>=Math.abs(u-g),r&&(a.position.top=i._convertPositionTo("relative",{top:h-i.helperProportions.height,left:0}).top-i.margins.top),s&&(a.position.top=i._convertPositionTo("relative",{top:l,left:0}).top-i.margins.top),n&&(a.position.left=i._convertPositionTo("relative",{top:0,left:d-i.helperProportions.width}).left-i.margins.left),o&&(a.position.left=i._convertPositionTo("relative",{top:0,left:u}).left-i.margins.left)),c=r||s||n||o,"outer"!==p.snapMode&&(r=f>=Math.abs(h-x),s=f>=Math.abs(l-v),n=f>=Math.abs(d-g),o=f>=Math.abs(u-y),r&&(a.position.top=i._convertPositionTo("relative",{top:h,left:0}).top-i.margins.top),s&&(a.position.top=i._convertPositionTo("relative",{top:l-i.helperProportions.height,left:0}).top-i.margins.top),n&&(a.position.left=i._convertPositionTo("relative",{top:0,left:d}).left-i.margins.left),o&&(a.position.left=i._convertPositionTo("relative",{top:0,left:u-i.helperProportions.width}).left-i.margins.left)),!i.snapElements[m].snapping&&(r||s||n||o||c)&&i.options.snap.snap&&i.options.snap.snap.call(i.element,t,e.extend(i._uiHash(),{snapItem:i.snapElements[m].item})),i.snapElements[m].snapping=r||s||n||o||c)}}),e.ui.plugin.add("draggable","stack",{start:function(t,a,i){var r,s=i.options,n=e.makeArray(e(s.stack)).sort(function(t,a){return(parseInt(e(t).css("zIndex"),10)||0)-(parseInt(e(a).css("zIndex"),10)||0)});n.length&&(r=parseInt(e(n[0]).css("zIndex"),10)||0,e(n).each(function(t){e(this).css("zIndex",r+t)}),this.css("zIndex",r+n.length))}}),e.ui.plugin.add("draggable","zIndex",{start:function(t,a,i){var r=e(a.helper),s=i.options;r.css("zIndex")&&(s._zIndex=r.css("zIndex")),r.css("zIndex",s.zIndex)},stop:function(t,a,i){var r=i.options;r._zIndex&&e(a.helper).css("zIndex",r._zIndex)}}),e.ui.draggable,e.widget("ui.droppable",{version:"1.11.0-beta.2",widgetEventPrefix:"drop",options:{accept:"*",activeClass:!1,addClasses:!0,greedy:!1,hoverClass:!1,scope:"default",tolerance:"intersect",activate:null,deactivate:null,drop:null,out:null,over:null},_create:function(){var t,a=this.options,i=a.accept;this.isover=!1,this.isout=!0,this.accept=e.isFunction(i)?i:function(e){return e.is(i)},this.proportions=function(){return arguments.length?(t=arguments[0],void 0):t?t:t={width:this.element[0].offsetWidth,height:this.element[0].offsetHeight}},this._addToManager(a.scope),a.addClasses&&this.element.addClass("ui-droppable")},_addToManager:function(t){e.ui.ddmanager.droppables[t]=e.ui.ddmanager.droppables[t]||[],e.ui.ddmanager.droppables[t].push(this)},_splice:function(e){for(var t=0;e.length>t;t++)e[t]===this&&e.splice(t,1)},_destroy:function(){var t=e.ui.ddmanager.droppables[this.options.scope];this._splice(t),this.element.removeClass("ui-droppable ui-droppable-disabled")},_setOption:function(t,a){if("accept"===t)this.accept=e.isFunction(a)?a:function(e){return e.is(a)};else if("scope"===t){var i=e.ui.ddmanager.droppables[this.options.scope];this._splice(i),this._addToManager(a)}this._super(t,a)},_activate:function(t){var a=e.ui.ddmanager.current;this.options.activeClass&&this.element.addClass(this.options.activeClass),a&&this._trigger("activate",t,this.ui(a))},_deactivate:function(t){var a=e.ui.ddmanager.current;this.options.activeClass&&this.element.removeClass(this.options.activeClass),a&&this._trigger("deactivate",t,this.ui(a))},_over:function(t){var a=e.ui.ddmanager.current;a&&(a.currentItem||a.element)[0]!==this.element[0]&&this.accept.call(this.element[0],a.currentItem||a.element)&&(this.options.hoverClass&&this.element.addClass(this.options.hoverClass),this._trigger("over",t,this.ui(a)))},_out:function(t){var a=e.ui.ddmanager.current;a&&(a.currentItem||a.element)[0]!==this.element[0]&&this.accept.call(this.element[0],a.currentItem||a.element)&&(this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("out",t,this.ui(a)))},_drop:function(t,a){var i=a||e.ui.ddmanager.current,r=!1;return i&&(i.currentItem||i.element)[0]!==this.element[0]?(this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function(){var t=e(this).droppable("instance");return t.options.greedy&&!t.options.disabled&&t.options.scope===i.options.scope&&t.accept.call(t.element[0],i.currentItem||i.element)&&e.ui.intersect(i,e.extend(t,{offset:t.element.offset()}),t.options.tolerance)?(r=!0,!1):void 0}),r?!1:this.accept.call(this.element[0],i.currentItem||i.element)?(this.options.activeClass&&this.element.removeClass(this.options.activeClass),this.options.hoverClass&&this.element.removeClass(this.options.hoverClass),this._trigger("drop",t,this.ui(i)),this.element):!1):!1},ui:function(e){return{draggable:e.currentItem||e.element,helper:e.helper,position:e.position,offset:e.positionAbs}}}),e.ui.intersect=function(){function e(e,t,a){return e>=t&&t+a>e}return function(t,a,i){if(!a.offset)return!1;var r,s,n=(t.positionAbs||t.position.absolute).left,o=(t.positionAbs||t.position.absolute).top,d=n+t.helperProportions.width,u=o+t.helperProportions.height,h=a.offset.left,l=a.offset.top,m=h+a.proportions().width,c=l+a.proportions().height;switch(i){case"fit":return n>=h&&m>=d&&o>=l&&c>=u;case"intersect":return n+t.helperProportions.width/2>h&&m>d-t.helperProportions.width/2&&o+t.helperProportions.height/2>l&&c>u-t.helperProportions.height/2;case"pointer":return r=(t.positionAbs||t.position.absolute).left+(t.clickOffset||t.offset.click).left,s=(t.positionAbs||t.position.absolute).top+(t.clickOffset||t.offset.click).top,e(s,l,a.proportions().height)&&e(r,h,a.proportions().width);case"touch":return(o>=l&&c>=o||u>=l&&c>=u||l>o&&u>c)&&(n>=h&&m>=n||d>=h&&m>=d||h>n&&d>m);default:return!1}}}(),e.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(t,a){var i,r,s=e.ui.ddmanager.droppables[t.options.scope]||[],n=a?a.type:null,o=(t.currentItem||t.element).find(":data(ui-droppable)").addBack();e:for(i=0;s.length>i;i++)if(!(s[i].options.disabled||t&&!s[i].accept.call(s[i].element[0],t.currentItem||t.element))){for(r=0;o.length>r;r++)if(o[r]===s[i].element[0]){s[i].proportions().height=0;continue e}s[i].visible="none"!==s[i].element.css("display"),s[i].visible&&("mousedown"===n&&s[i]._activate.call(s[i],a),s[i].offset=s[i].element.offset(),s[i].proportions({width:s[i].element[0].offsetWidth,height:s[i].element[0].offsetHeight}))}},drop:function(t,a){var i=!1;return e.each((e.ui.ddmanager.droppables[t.options.scope]||[]).slice(),function(){this.options&&(!this.options.disabled&&this.visible&&e.ui.intersect(t,this,this.options.tolerance)&&(i=this._drop.call(this,a)||i),!this.options.disabled&&this.visible&&this.accept.call(this.element[0],t.currentItem||t.element)&&(this.isout=!0,this.isover=!1,this._deactivate.call(this,a)))}),i},dragStart:function(t,a){t.element.parentsUntil("body").bind("scroll.droppable",function(){t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,a)})},drag:function(t,a){t.options.refreshPositions&&e.ui.ddmanager.prepareOffsets(t,a),e.each(e.ui.ddmanager.droppables[t.options.scope]||[],function(){if(!this.options.disabled&&!this.greedyChild&&this.visible){var i,r,s,n=e.ui.intersect(t,this,this.options.tolerance),o=!n&&this.isover?"isout":n&&!this.isover?"isover":null;o&&(this.options.greedy&&(r=this.options.scope,s=this.element.parents(":data(ui-droppable)").filter(function(){return e(this).droppable("instance").options.scope===r}),s.length&&(i=e(s[0]).droppable("instance"),i.greedyChild="isover"===o)),i&&"isover"===o&&(i.isover=!1,i.isout=!0,i._out.call(i,a)),this[o]=!0,this["isout"===o?"isover":"isout"]=!1,this["isover"===o?"_over":"_out"].call(this,a),i&&"isout"===o&&(i.isout=!1,i.isover=!0,i._over.call(i,a)))}})},dragStop:function(t,a){t.element.parentsUntil("body").unbind("scroll.droppable"),t.options.refreshPositions||e.ui.ddmanager.prepareOffsets(t,a)}},e.ui.droppable,e.widget("ui.resizable",e.ui.mouse,{version:"1.11.0-beta.2",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_num:function(e){return parseInt(e,10)||0},_isNumber:function(e){return!isNaN(parseInt(e,10))},_hasScroll:function(t,a){if("hidden"===e(t).css("overflow"))return!1;var i=a&&"left"===a?"scrollLeft":"scrollTop",r=!1;return t[i]>0?!0:(t[i]=1,r=t[i]>0,t[i]=0,r)},_create:function(){var t,a,i,r,s,n=this,o=this.options;if(this.element.addClass("ui-resizable"),e.extend(this,{_aspectRatio:!!o.aspectRatio,aspectRatio:o.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:o.helper||o.ghost||o.animate?o.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)&&(this.element.wrap(e("
").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")}),this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0}),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css({margin:this.originalElement.css("margin")}),this._proportionallyResize()),this.handles=o.handles||(e(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),t=this.handles.split(","),this.handles={},a=0;t.length>a;a++)i=e.trim(t[a]),s="ui-resizable-"+i,r=e("
"),r.css({zIndex:o.zIndex}),"se"===i&&r.addClass("ui-icon ui-icon-gripsmall-diagonal-se"),this.handles[i]=".ui-resizable-"+i,this.element.append(r);this._renderAxis=function(t){var a,i,r,s;t=t||this.element;for(a in this.handles)this.handles[a].constructor===String&&(this.handles[a]=this.element.children(this.handles[a]).first().show()),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/textarea|input|select|button/i)&&(i=e(this.handles[a],this.element),s=/sw|ne|nw|se|n|s/.test(a)?i.outerHeight():i.outerWidth(),r=["padding",/ne|nw|n/.test(a)?"Top":/se|sw|s/.test(a)?"Bottom":/^e$/.test(a)?"Right":"Left"].join(""),t.css(r,s),this._proportionallyResize()),e(this.handles[a]).length},this._renderAxis(this.element),this._handles=e(".ui-resizable-handle",this.element).disableSelection(),this._handles.mouseover(function(){n.resizing||(this.className&&(r=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),n.axis=r&&r[1]?r[1]:"se")}),o.autoHide&&(this._handles.hide(),e(this.element).addClass("ui-resizable-autohide").mouseenter(function(){o.disabled||(e(this).removeClass("ui-resizable-autohide"),n._handles.show())}).mouseleave(function(){o.disabled||n.resizing||(e(this).addClass("ui-resizable-autohide"),n._handles.hide())})),this._mouseInit()},_destroy:function(){this._mouseDestroy();var t,a=function(t){e(t).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing").removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(a(this.element),t=this.element,this.originalElement.css({position:t.css("position"),width:t.outerWidth(),height:t.outerHeight(),top:t.css("top"),left:t.css("left")}).insertAfter(t),t.remove()),this.originalElement.css("resize",this.originalResizeStyle),a(this.originalElement),this},_mouseCapture:function(t){var a,i,r=!1;for(a in this.handles)i=e(this.handles[a])[0],(i===t.target||e.contains(i,t.target))&&(r=!0);return!this.options.disabled&&r},_mouseStart:function(t){var a,i,r,s=this.options,n=this.element;return this.resizing=!0,this._renderProxy(),a=this._num(this.helper.css("left")),i=this._num(this.helper.css("top")),s.containment&&(a+=e(s.containment).scrollLeft()||0,i+=e(s.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:a,top:i},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:n.width(),height:n.height()},this.originalSize=this._helper?{width:n.outerWidth(),height:n.outerHeight()}:{width:n.width(),height:n.height()},this.originalPosition={left:a,top:i},this.sizeDiff={width:n.outerWidth()-n.width(),height:n.outerHeight()-n.height()},this.originalMousePosition={left:t.pageX,top:t.pageY},this.aspectRatio="number"==typeof s.aspectRatio?s.aspectRatio:this.originalSize.width/this.originalSize.height||1,r=e(".ui-resizable-"+this.axis).css("cursor"),e("body").css("cursor","auto"===r?this.axis+"-resize":r),n.addClass("ui-resizable-resizing"),this._propagate("start",t),!0},_mouseDrag:function(t){var a,i=this.helper,r={},s=this.originalMousePosition,n=this.axis,o=t.pageX-s.left||0,d=t.pageY-s.top||0,u=this._change[n];return this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height},u?(a=u.apply(this,[t,o,d]),this._updateVirtualBoundaries(t.shiftKey),(this._aspectRatio||t.shiftKey)&&(a=this._updateRatio(a,t)),a=this._respectSize(a,t),this._updateCache(a),this._propagate("resize",t),this.position.top!==this.prevPosition.top&&(r.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(r.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(r.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(r.height=this.size.height+"px"),i.css(r),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),e.isEmptyObject(r)||this._trigger("resize",t,this.ui()),!1):!1},_mouseStop:function(t){this.resizing=!1;var a,i,r,s,n,o,d,u=this.options,h=this;return this._helper&&(a=this._proportionallyResizeElements,i=a.length&&/textarea/i.test(a[0].nodeName),r=i&&this._hasScroll(a[0],"left")?0:h.sizeDiff.height,s=i?0:h.sizeDiff.width,n={width:h.helper.width()-s,height:h.helper.height()-r},o=parseInt(h.element.css("left"),10)+(h.position.left-h.originalPosition.left)||null,d=parseInt(h.element.css("top"),10)+(h.position.top-h.originalPosition.top)||null,u.animate||this.element.css(e.extend(n,{top:d,left:o})),h.helper.height(h.size.height),h.helper.width(h.size.width),this._helper&&!u.animate&&this._proportionallyResize()),e("body").css("cursor","auto"),this.element.removeClass("ui-resizable-resizing"),this._propagate("stop",t),this._helper&&this.helper.remove(),!1},_updateVirtualBoundaries:function(e){var t,a,i,r,s,n=this.options;s={minWidth:this._isNumber(n.minWidth)?n.minWidth:0,maxWidth:this._isNumber(n.maxWidth)?n.maxWidth:1/0,minHeight:this._isNumber(n.minHeight)?n.minHeight:0,maxHeight:this._isNumber(n.maxHeight)?n.maxHeight:1/0},(this._aspectRatio||e)&&(t=s.minHeight*this.aspectRatio,i=s.minWidth/this.aspectRatio,a=s.maxHeight*this.aspectRatio,r=s.maxWidth/this.aspectRatio,t>s.minWidth&&(s.minWidth=t),i>s.minHeight&&(s.minHeight=i),s.maxWidth>a&&(s.maxWidth=a),s.maxHeight>r&&(s.maxHeight=r)),this._vBoundaries=s},_updateCache:function(e){this.offset=this.helper.offset(),this._isNumber(e.left)&&(this.position.left=e.left),this._isNumber(e.top)&&(this.position.top=e.top),this._isNumber(e.height)&&(this.size.height=e.height),this._isNumber(e.width)&&(this.size.width=e.width)},_updateRatio:function(e){var t=this.position,a=this.size,i=this.axis;return this._isNumber(e.height)?e.width=e.height*this.aspectRatio:this._isNumber(e.width)&&(e.height=e.width/this.aspectRatio),"sw"===i&&(e.left=t.left+(a.width-e.width),e.top=null),"nw"===i&&(e.top=t.top+(a.height-e.height),e.left=t.left+(a.width-e.width)),e},_respectSize:function(e){var t=this._vBoundaries,a=this.axis,i=this._isNumber(e.width)&&t.maxWidth&&t.maxWidthe.width,n=this._isNumber(e.height)&&t.minHeight&&t.minHeight>e.height,o=this.originalPosition.left+this.originalSize.width,d=this.position.top+this.size.height,u=/sw|nw|w/.test(a),h=/nw|ne|n/.test(a);return s&&(e.width=t.minWidth),n&&(e.height=t.minHeight),i&&(e.width=t.maxWidth),r&&(e.height=t.maxHeight),s&&u&&(e.left=o-t.minWidth),i&&u&&(e.left=o-t.maxWidth),n&&h&&(e.top=d-t.minHeight),r&&h&&(e.top=d-t.maxHeight),e.width||e.height||e.left||!e.top?e.width||e.height||e.top||!e.left||(e.left=null):e.top=null,e},_proportionallyResize:function(){if(this._proportionallyResizeElements.length){var e,t,a,i,r,s=this.helper||this.element;for(e=0;this._proportionallyResizeElements.length>e;e++){if(r=this._proportionallyResizeElements[e],!this.borderDif)for(this.borderDif=[],a=[r.css("borderTopWidth"),r.css("borderRightWidth"),r.css("borderBottomWidth"),r.css("borderLeftWidth")],i=[r.css("paddingTop"),r.css("paddingRight"),r.css("paddingBottom"),r.css("paddingLeft")],t=0;a.length>t;t++)this.borderDif[t]=(parseInt(a[t],10)||0)+(parseInt(i[t],10)||0);r.css({height:s.height()-this.borderDif[0]-this.borderDif[2]||0,width:s.width()-this.borderDif[1]-this.borderDif[3]||0})}}},_renderProxy:function(){var t=this.element,a=this.options;this.elementOffset=t.offset(),this._helper?(this.helper=this.helper||e("
"),this.helper.addClass(this._helper).css({width:this.element.outerWidth()-1,height:this.element.outerHeight()-1,position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++a.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element},_change:{e:function(e,t){return{width:this.originalSize.width+t}},w:function(e,t){var a=this.originalSize,i=this.originalPosition;return{left:i.left+t,width:a.width-t}},n:function(e,t,a){var i=this.originalSize,r=this.originalPosition;return{top:r.top+a,height:i.height-a}},s:function(e,t,a){return{height:this.originalSize.height+a}},se:function(t,a,i){return e.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[t,a,i]))},sw:function(t,a,i){return e.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[t,a,i]))},ne:function(t,a,i){return e.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[t,a,i]))},nw:function(t,a,i){return e.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[t,a,i]))}},_propagate:function(t,a){e.ui.plugin.call(this,t,[a,this.ui()]),"resize"!==t&&this._trigger(t,a,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition,prevSize:this.prevSize,prevPosition:this.prevPosition}}}),e.ui.plugin.add("resizable","animate",{stop:function(t){var a=e(this).resizable("instance"),i=a.options,r=a._proportionallyResizeElements,s=r.length&&/textarea/i.test(r[0].nodeName),n=s&&a._hasScroll(r[0],"left")?0:a.sizeDiff.height,o=s?0:a.sizeDiff.width,d={width:a.size.width-o,height:a.size.height-n},u=parseInt(a.element.css("left"),10)+(a.position.left-a.originalPosition.left)||null,h=parseInt(a.element.css("top"),10)+(a.position.top-a.originalPosition.top)||null;a.element.animate(e.extend(d,h&&u?{top:h,left:u}:{}),{duration:i.animateDuration,easing:i.animateEasing,step:function(){var i={width:parseInt(a.element.css("width"),10),height:parseInt(a.element.css("height"),10),top:parseInt(a.element.css("top"),10),left:parseInt(a.element.css("left"),10)};r&&r.length&&e(r[0]).css({width:i.width,height:i.height}),a._updateCache(i),a._propagate("resize",t)}})}}),e.ui.plugin.add("resizable","containment",{start:function(){var t,a,i,r,s,n,o,d=e(this).resizable("instance"),u=d.options,h=d.element,l=u.containment,m=l instanceof e?l.get(0):/parent/.test(l)?h.parent().get(0):l;m&&(d.containerElement=e(m),/document/.test(l)||l===document?(d.containerOffset={left:0,top:0},d.containerPosition={left:0,top:0},d.parentData={element:e(document),left:0,top:0,width:e(document).width(),height:e(document).height()||document.body.parentNode.scrollHeight}):(t=e(m),a=[],e(["Top","Right","Left","Bottom"]).each(function(e,i){a[e]=d._num(t.css("padding"+i))}),d.containerOffset=t.offset(),d.containerPosition=t.position(),d.containerSize={height:t.innerHeight()-a[3],width:t.innerWidth()-a[1]},i=d.containerOffset,r=d.containerSize.height,s=d.containerSize.width,n=d._hasScroll(m,"left")?m.scrollWidth:s,o=d._hasScroll(m)?m.scrollHeight:r,d.parentData={element:m,left:i.left,top:i.top,width:n,height:o}))},resize:function(t,a){var i,r,s,n,o=e(this).resizable("instance"),d=o.options,u=o.containerOffset,h=o.position,l=o._aspectRatio||t.shiftKey,m={top:0,left:0},c=o.containerElement,p=!0;c[0]!==document&&/static/.test(c.css("position"))&&(m=u),h.left<(o._helper?u.left:0)&&(o.size.width=o.size.width+(o._helper?o.position.left-u.left:o.position.left-m.left),l&&(o.size.height=o.size.width/o.aspectRatio,p=!1),o.position.left=d.helper?u.left:0),h.top<(o._helper?u.top:0)&&(o.size.height=o.size.height+(o._helper?o.position.top-u.top:o.position.top),l&&(o.size.width=o.size.height*o.aspectRatio,p=!1),o.position.top=o._helper?u.top:0),o.offset.left=o.parentData.left+o.position.left,o.offset.top=o.parentData.top+o.position.top,i=Math.abs((o._helper?o.offset.left-m.left:o.offset.left-u.left)+o.sizeDiff.width),r=Math.abs((o._helper?o.offset.top-m.top:o.offset.top-u.top)+o.sizeDiff.height),s=o.containerElement.get(0)===o.element.parent().get(0),n=/relative|absolute/.test(o.containerElement.css("position")),s&&n&&(i-=Math.abs(o.parentData.left)),i+o.size.width>=o.parentData.width&&(o.size.width=o.parentData.width-i,l&&(o.size.height=o.size.width/o.aspectRatio,p=!1)),r+o.size.height>=o.parentData.height&&(o.size.height=o.parentData.height-r,l&&(o.size.width=o.size.height*o.aspectRatio,p=!1)),p||(o.position.left=a.prevPosition.left,o.position.top=a.prevPosition.top,o.size.width=a.prevSize.width,o.size.height=a.prevSize.height)},stop:function(){var t=e(this).resizable("instance"),a=t.options,i=t.containerOffset,r=t.containerPosition,s=t.containerElement,n=e(t.helper),o=n.offset(),d=n.outerWidth()-t.sizeDiff.width,u=n.outerHeight()-t.sizeDiff.height;t._helper&&!a.animate&&/relative/.test(s.css("position"))&&e(this).css({left:o.left-r.left-i.left,width:d,height:u}),t._helper&&!a.animate&&/static/.test(s.css("position"))&&e(this).css({left:o.left-r.left-i.left,width:d,height:u})}}),e.ui.plugin.add("resizable","alsoResize",{start:function(){var t=e(this).resizable("instance"),a=t.options,i=function(t){e(t).each(function(){var t=e(this);t.data("ui-resizable-alsoresize",{width:parseInt(t.width(),10),height:parseInt(t.height(),10),left:parseInt(t.css("left"),10),top:parseInt(t.css("top"),10)})})};"object"!=typeof a.alsoResize||a.alsoResize.parentNode?i(a.alsoResize):a.alsoResize.length?(a.alsoResize=a.alsoResize[0],i(a.alsoResize)):e.each(a.alsoResize,function(e){i(e)})},resize:function(t,a){var i=e(this).resizable("instance"),r=i.options,s=i.originalSize,n=i.originalPosition,o={height:i.size.height-s.height||0,width:i.size.width-s.width||0,top:i.position.top-n.top||0,left:i.position.left-n.left||0},d=function(t,i){e(t).each(function(){var t=e(this),r=e(this).data("ui-resizable-alsoresize"),s={},n=i&&i.length?i:t.parents(a.originalElement[0]).length?["width","height"]:["width","height","top","left"];e.each(n,function(e,t){var a=(r[t]||0)+(o[t]||0);a&&a>=0&&(s[t]=a||null)}),t.css(s)})};"object"!=typeof r.alsoResize||r.alsoResize.nodeType?d(r.alsoResize):e.each(r.alsoResize,function(e,t){d(e,t)})},stop:function(){e(this).removeData("resizable-alsoresize")}}),e.ui.plugin.add("resizable","ghost",{start:function(){var t=e(this).resizable("instance"),a=t.options,i=t.size;t.ghost=t.originalElement.clone(),t.ghost.css({opacity:.25,display:"block",position:"relative",height:i.height,width:i.width,margin:0,left:0,top:0}).addClass("ui-resizable-ghost").addClass("string"==typeof a.ghost?a.ghost:""),t.ghost.appendTo(t.helper)},resize:function(){var t=e(this).resizable("instance");t.ghost&&t.ghost.css({position:"relative",height:t.size.height,width:t.size.width})},stop:function(){var t=e(this).resizable("instance");t.ghost&&t.helper&&t.helper.get(0).removeChild(t.ghost.get(0))}}),e.ui.plugin.add("resizable","grid",{resize:function(){var t=e(this).resizable("instance"),a=t.options,i=t.size,r=t.originalSize,s=t.originalPosition,n=t.axis,o="number"==typeof a.grid?[a.grid,a.grid]:a.grid,d=o[0]||1,u=o[1]||1,h=Math.round((i.width-r.width)/d)*d,l=Math.round((i.height-r.height)/u)*u,m=r.width+h,c=r.height+l,p=a.maxWidth&&m>a.maxWidth,f=a.maxHeight&&c>a.maxHeight,g=a.minWidth&&a.minWidth>m,y=a.minHeight&&a.minHeight>c;a.grid=o,g&&(m+=d),y&&(c+=u),p&&(m-=d),f&&(c-=u),/^(se|s|e)$/.test(n)?(t.size.width=m,t.size.height=c):/^(ne)$/.test(n)?(t.size.width=m,t.size.height=c,t.position.top=s.top-l):/^(sw)$/.test(n)?(t.size.width=m,t.size.height=c,t.position.left=s.left-h):(c-u>0?(t.size.height=c,t.position.top=s.top-l):(t.size.height=u,t.position.top=s.top+r.height-u),m-d>0?(t.size.width=m,t.position.left=s.left-h):(t.size.width=d,t.position.left=s.left+r.width-d))}}),e.ui.resizable,e.widget("ui.selectable",e.ui.mouse,{version:"1.11.0-beta.2",options:{appendTo:"body",autoRefresh:!0,distance:0,filter:"*",tolerance:"touch",selected:null,selecting:null,start:null,stop:null,unselected:null,unselecting:null},_create:function(){var t,a=this; +this.element.addClass("ui-selectable"),this.dragged=!1,this.refresh=function(){t=e(a.options.filter,a.element[0]),t.addClass("ui-selectee"),t.each(function(){var t=e(this),a=t.offset();e.data(this,"selectable-item",{element:this,$element:t,left:a.left,top:a.top,right:a.left+t.outerWidth(),bottom:a.top+t.outerHeight(),startselected:!1,selected:t.hasClass("ui-selected"),selecting:t.hasClass("ui-selecting"),unselecting:t.hasClass("ui-unselecting")})})},this.refresh(),this.selectees=t.addClass("ui-selectee"),this._mouseInit(),this.helper=e("
")},_destroy:function(){this.selectees.removeClass("ui-selectee").removeData("selectable-item"),this.element.removeClass("ui-selectable ui-selectable-disabled"),this._mouseDestroy()},_mouseStart:function(t){var a=this,i=this.options;this.opos=[t.pageX,t.pageY],this.options.disabled||(this.selectees=e(i.filter,this.element[0]),this._trigger("start",t),e(i.appendTo).append(this.helper),this.helper.css({left:t.pageX,top:t.pageY,width:0,height:0}),i.autoRefresh&&this.refresh(),this.selectees.filter(".ui-selected").each(function(){var i=e.data(this,"selectable-item");i.startselected=!0,t.metaKey||t.ctrlKey||(i.$element.removeClass("ui-selected"),i.selected=!1,i.$element.addClass("ui-unselecting"),i.unselecting=!0,a._trigger("unselecting",t,{unselecting:i.element}))}),e(t.target).parents().addBack().each(function(){var i,r=e.data(this,"selectable-item");return r?(i=!t.metaKey&&!t.ctrlKey||!r.$element.hasClass("ui-selected"),r.$element.removeClass(i?"ui-unselecting":"ui-selected").addClass(i?"ui-selecting":"ui-unselecting"),r.unselecting=!i,r.selecting=i,r.selected=i,i?a._trigger("selecting",t,{selecting:r.element}):a._trigger("unselecting",t,{unselecting:r.element}),!1):void 0}))},_mouseDrag:function(t){if(this.dragged=!0,!this.options.disabled){var a,i=this,r=this.options,s=this.opos[0],n=this.opos[1],o=t.pageX,d=t.pageY;return s>o&&(a=o,o=s,s=a),n>d&&(a=d,d=n,n=a),this.helper.css({left:s,top:n,width:o-s,height:d-n}),this.selectees.each(function(){var a=e.data(this,"selectable-item"),u=!1;a&&a.element!==i.element[0]&&("touch"===r.tolerance?u=!(a.left>o||s>a.right||a.top>d||n>a.bottom):"fit"===r.tolerance&&(u=a.left>s&&o>a.right&&a.top>n&&d>a.bottom),u?(a.selected&&(a.$element.removeClass("ui-selected"),a.selected=!1),a.unselecting&&(a.$element.removeClass("ui-unselecting"),a.unselecting=!1),a.selecting||(a.$element.addClass("ui-selecting"),a.selecting=!0,i._trigger("selecting",t,{selecting:a.element}))):(a.selecting&&((t.metaKey||t.ctrlKey)&&a.startselected?(a.$element.removeClass("ui-selecting"),a.selecting=!1,a.$element.addClass("ui-selected"),a.selected=!0):(a.$element.removeClass("ui-selecting"),a.selecting=!1,a.startselected&&(a.$element.addClass("ui-unselecting"),a.unselecting=!0),i._trigger("unselecting",t,{unselecting:a.element}))),a.selected&&(t.metaKey||t.ctrlKey||a.startselected||(a.$element.removeClass("ui-selected"),a.selected=!1,a.$element.addClass("ui-unselecting"),a.unselecting=!0,i._trigger("unselecting",t,{unselecting:a.element})))))}),!1}},_mouseStop:function(t){var a=this;return this.dragged=!1,e(".ui-unselecting",this.element[0]).each(function(){var i=e.data(this,"selectable-item");i.$element.removeClass("ui-unselecting"),i.unselecting=!1,i.startselected=!1,a._trigger("unselected",t,{unselected:i.element})}),e(".ui-selecting",this.element[0]).each(function(){var i=e.data(this,"selectable-item");i.$element.removeClass("ui-selecting").addClass("ui-selected"),i.selecting=!1,i.selected=!0,i.startselected=!0,a._trigger("selected",t,{selected:i.element})}),this._trigger("stop",t),this.helper.remove(),!1}}),e.widget("ui.sortable",e.ui.mouse,{version:"1.11.0-beta.2",widgetEventPrefix:"sort",ready:!1,options:{appendTo:"parent",axis:!1,connectWith:!1,containment:!1,cursor:"auto",cursorAt:!1,dropOnEmpty:!0,forcePlaceholderSize:!1,forceHelperSize:!1,grid:!1,handle:!1,helper:"original",items:"> *",opacity:!1,placeholder:!1,revert:!1,scroll:!0,scrollSensitivity:20,scrollSpeed:20,scope:"default",tolerance:"intersect",zIndex:1e3,activate:null,beforeStop:null,change:null,deactivate:null,out:null,over:null,receive:null,remove:null,sort:null,start:null,stop:null,update:null},_isOverAxis:function(e,t,a){return e>=t&&t+a>e},_isFloating:function(e){return/left|right/.test(e.css("float"))||/inline|table-cell/.test(e.css("display"))},_create:function(){var e=this.options;this.containerCache={},this.element.addClass("ui-sortable"),this.refresh(),this.floating=this.items.length?"x"===e.axis||this._isFloating(this.items[0].item):!1,this.offset=this.element.offset(),this._mouseInit(),this._setHandleClassName(),this.ready=!0},_setOption:function(e,t){this._super(e,t),"handle"===e&&this._setHandleClassName()},_setHandleClassName:function(){this.element.find(".ui-sortable-handle").removeClass("ui-sortable-handle"),e.each(this.items,function(){(this.instance.options.handle?this.item.find(this.instance.options.handle):this.item).addClass("ui-sortable-handle")})},_destroy:function(){this.element.removeClass("ui-sortable ui-sortable-disabled").find(".ui-sortable-handle").removeClass("ui-sortable-handle"),this._mouseDestroy();for(var e=this.items.length-1;e>=0;e--)this.items[e].item.removeData(this.widgetName+"-item");return this},_mouseCapture:function(t,a){var i=null,r=!1,s=this;return this.reverting?!1:this.options.disabled||"static"===this.options.type?!1:(this._refreshItems(t),e(t.target).parents().each(function(){return e.data(this,s.widgetName+"-item")===s?(i=e(this),!1):void 0}),e.data(t.target,s.widgetName+"-item")===s&&(i=e(t.target)),i?!this.options.handle||a||(e(this.options.handle,i).find("*").addBack().each(function(){this===t.target&&(r=!0)}),r)?(this.currentItem=i,this._removeCurrentsFromItems(),!0):!1:!1)},_mouseStart:function(t,a,i){var r,s,n=this.options;if(this.currentContainer=this,this.refreshPositions(),this.helper=this._createHelper(t),this._cacheHelperProportions(),this._cacheMargins(),this.scrollParent=this.helper.scrollParent(),this.offset=this.currentItem.offset(),this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left},e.extend(this.offset,{click:{left:t.pageX-this.offset.left,top:t.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}),this.helper.css("position","absolute"),this.cssPosition=this.helper.css("position"),this.originalPosition=this._generatePosition(t),this.originalPageX=t.pageX,this.originalPageY=t.pageY,n.cursorAt&&this._adjustOffsetFromHelper(n.cursorAt),this.domPosition={prev:this.currentItem.prev()[0],parent:this.currentItem.parent()[0]},this.helper[0]!==this.currentItem[0]&&this.currentItem.hide(),this._createPlaceholder(),n.containment&&this._setContainment(),n.cursor&&"auto"!==n.cursor&&(s=this.document.find("body"),this.storedCursor=s.css("cursor"),s.css("cursor",n.cursor),this.storedStylesheet=e("").appendTo(s)),n.opacity&&(this.helper.css("opacity")&&(this._storedOpacity=this.helper.css("opacity")),this.helper.css("opacity",n.opacity)),n.zIndex&&(this.helper.css("zIndex")&&(this._storedZIndex=this.helper.css("zIndex")),this.helper.css("zIndex",n.zIndex)),this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName&&(this.overflowOffset=this.scrollParent.offset()),this._trigger("start",t,this._uiHash()),this._preserveHelperProportions||this._cacheHelperProportions(),!i)for(r=this.containers.length-1;r>=0;r--)this.containers[r]._trigger("activate",t,this._uiHash(this));return e.ui.ddmanager&&(e.ui.ddmanager.current=this),e.ui.ddmanager&&!n.dropBehaviour&&e.ui.ddmanager.prepareOffsets(this,t),this.dragging=!0,this.helper.addClass("ui-sortable-helper"),this._mouseDrag(t),!0},_mouseDrag:function(t){var a,i,r,s,n=this.options,o=!1;for(this.position=this._generatePosition(t),this.positionAbs=this._convertPositionTo("absolute"),this.lastPositionAbs||(this.lastPositionAbs=this.positionAbs),this.options.scroll&&(this.scrollParent[0]!==document&&"HTML"!==this.scrollParent[0].tagName?(this.overflowOffset.top+this.scrollParent[0].offsetHeight-t.pageY=0;a--)if(i=this.items[a],r=i.item[0],s=this._intersectsWithPointer(i),s&&i.instance===this.currentContainer&&r!==this.currentItem[0]&&this.placeholder[1===s?"next":"prev"]()[0]!==r&&!e.contains(this.placeholder[0],r)&&("semi-dynamic"===this.options.type?!e.contains(this.element[0],r):!0)){if(this.direction=1===s?"down":"up","pointer"!==this.options.tolerance&&!this._intersectsWithSides(i))break;this._rearrange(t,i),this._trigger("change",t,this._uiHash());break}return this._contactContainers(t),e.ui.ddmanager&&e.ui.ddmanager.drag(this,t),this._trigger("sort",t,this._uiHash()),this.lastPositionAbs=this.positionAbs,!1},_mouseStop:function(t,a){if(t){if(e.ui.ddmanager&&!this.options.dropBehaviour&&e.ui.ddmanager.drop(this,t),this.options.revert){var i=this,r=this.placeholder.offset(),s=this.options.axis,n={};s&&"x"!==s||(n.left=r.left-this.offset.parent.left-this.margins.left+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollLeft)),s&&"y"!==s||(n.top=r.top-this.offset.parent.top-this.margins.top+(this.offsetParent[0]===document.body?0:this.offsetParent[0].scrollTop)),this.reverting=!0,e(this.helper).animate(n,parseInt(this.options.revert,10)||500,function(){i._clear(t)})}else this._clear(t,a);return!1}},cancel:function(){if(this.dragging){this._mouseUp({target:null}),"original"===this.options.helper?this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper"):this.currentItem.show();for(var t=this.containers.length-1;t>=0;t--)this.containers[t]._trigger("deactivate",null,this._uiHash(this)),this.containers[t].containerCache.over&&(this.containers[t]._trigger("out",null,this._uiHash(this)),this.containers[t].containerCache.over=0)}return this.placeholder&&(this.placeholder[0].parentNode&&this.placeholder[0].parentNode.removeChild(this.placeholder[0]),"original"!==this.options.helper&&this.helper&&this.helper[0].parentNode&&this.helper.remove(),e.extend(this,{helper:null,dragging:!1,reverting:!1,_noFinalSort:null}),this.domPosition.prev?e(this.domPosition.prev).after(this.currentItem):e(this.domPosition.parent).prepend(this.currentItem)),this},serialize:function(t){var a=this._getItemsAsjQuery(t&&t.connected),i=[];return t=t||{},e(a).each(function(){var a=(e(t.item||this).attr(t.attribute||"id")||"").match(t.expression||/(.+)[\-=_](.+)/);a&&i.push((t.key||a[1]+"[]")+"="+(t.key&&t.expression?a[1]:a[2]))}),!i.length&&t.key&&i.push(t.key+"="),i.join("&")},toArray:function(t){var a=this._getItemsAsjQuery(t&&t.connected),i=[];return t=t||{},a.each(function(){i.push(e(t.item||this).attr(t.attribute||"id")||"")}),i},_intersectsWith:function(e){var t=this.positionAbs.left,a=t+this.helperProportions.width,i=this.positionAbs.top,r=i+this.helperProportions.height,s=e.left,n=s+e.width,o=e.top,d=o+e.height,u=this.offset.click.top,h=this.offset.click.left,l="x"===this.options.axis||i+u>o&&d>i+u,m="y"===this.options.axis||t+h>s&&n>t+h,c=l&&m;return"pointer"===this.options.tolerance||this.options.forcePointerForContainers||"pointer"!==this.options.tolerance&&this.helperProportions[this.floating?"width":"height"]>e[this.floating?"width":"height"]?c:t+this.helperProportions.width/2>s&&n>a-this.helperProportions.width/2&&i+this.helperProportions.height/2>o&&d>r-this.helperProportions.height/2},_intersectsWithPointer:function(e){var t="x"===this.options.axis||this._isOverAxis(this.positionAbs.top+this.offset.click.top,e.top,e.height),a="y"===this.options.axis||this._isOverAxis(this.positionAbs.left+this.offset.click.left,e.left,e.width),i=t&&a,r=this._getDragVerticalDirection(),s=this._getDragHorizontalDirection();return i?this.floating?s&&"right"===s||"down"===r?2:1:r&&("down"===r?2:1):!1},_intersectsWithSides:function(e){var t=this._isOverAxis(this.positionAbs.top+this.offset.click.top,e.top+e.height/2,e.height),a=this._isOverAxis(this.positionAbs.left+this.offset.click.left,e.left+e.width/2,e.width),i=this._getDragVerticalDirection(),r=this._getDragHorizontalDirection();return this.floating&&r?"right"===r&&a||"left"===r&&!a:i&&("down"===i&&t||"up"===i&&!t)},_getDragVerticalDirection:function(){var e=this.positionAbs.top-this.lastPositionAbs.top;return 0!==e&&(e>0?"down":"up")},_getDragHorizontalDirection:function(){var e=this.positionAbs.left-this.lastPositionAbs.left;return 0!==e&&(e>0?"right":"left")},refresh:function(e){return this._refreshItems(e),this._setHandleClassName(),this.refreshPositions(),this},_connectWith:function(){var e=this.options;return e.connectWith.constructor===String?[e.connectWith]:e.connectWith},_getItemsAsjQuery:function(t){function a(){o.push(this)}var i,r,s,n,o=[],d=[],u=this._connectWith();if(u&&t)for(i=u.length-1;i>=0;i--)for(s=e(u[i]),r=s.length-1;r>=0;r--)n=e.data(s[r],this.widgetFullName),n&&n!==this&&!n.options.disabled&&d.push([e.isFunction(n.options.items)?n.options.items.call(n.element):e(n.options.items,n.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),n]);for(d.push([e.isFunction(this.options.items)?this.options.items.call(this.element,null,{options:this.options,item:this.currentItem}):e(this.options.items,this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"),this]),i=d.length-1;i>=0;i--)d[i][0].each(a);return e(o)},_removeCurrentsFromItems:function(){var t=this.currentItem.find(":data("+this.widgetName+"-item)");this.items=e.grep(this.items,function(e){for(var a=0;t.length>a;a++)if(t[a]===e.item[0])return!1;return!0})},_refreshItems:function(t){this.items=[],this.containers=[this];var a,i,r,s,n,o,d,u,h=this.items,l=[[e.isFunction(this.options.items)?this.options.items.call(this.element[0],t,{item:this.currentItem}):e(this.options.items,this.element),this]],m=this._connectWith();if(m&&this.ready)for(a=m.length-1;a>=0;a--)for(r=e(m[a]),i=r.length-1;i>=0;i--)s=e.data(r[i],this.widgetFullName),s&&s!==this&&!s.options.disabled&&(l.push([e.isFunction(s.options.items)?s.options.items.call(s.element[0],t,{item:this.currentItem}):e(s.options.items,s.element),s]),this.containers.push(s));for(a=l.length-1;a>=0;a--)for(n=l[a][1],o=l[a][0],i=0,u=o.length;u>i;i++)d=e(o[i]),d.data(this.widgetName+"-item",n),h.push({item:d,instance:n,width:0,height:0,left:0,top:0})},refreshPositions:function(t){this.offsetParent&&this.helper&&(this.offset.parent=this._getParentOffset());var a,i,r,s;for(a=this.items.length-1;a>=0;a--)i=this.items[a],i.instance!==this.currentContainer&&this.currentContainer&&i.item[0]!==this.currentItem[0]||(r=this.options.toleranceElement?e(this.options.toleranceElement,i.item):i.item,t||(i.width=r.outerWidth(),i.height=r.outerHeight()),s=r.offset(),i.left=s.left,i.top=s.top);if(this.options.custom&&this.options.custom.refreshContainers)this.options.custom.refreshContainers.call(this);else for(a=this.containers.length-1;a>=0;a--)s=this.containers[a].element.offset(),this.containers[a].containerCache.left=s.left,this.containers[a].containerCache.top=s.top,this.containers[a].containerCache.width=this.containers[a].element.outerWidth(),this.containers[a].containerCache.height=this.containers[a].element.outerHeight();return this},_createPlaceholder:function(t){t=t||this;var a,i=t.options;i.placeholder&&i.placeholder.constructor!==String||(a=i.placeholder,i.placeholder={element:function(){var i=t.currentItem[0].nodeName.toLowerCase(),r=e("<"+i+">",t.document[0]).addClass(a||t.currentItem[0].className+" ui-sortable-placeholder").removeClass("ui-sortable-helper");return"tr"===i?t.currentItem.children().each(function(){e(" ",t.document[0]).attr("colspan",e(this).attr("colspan")||1).appendTo(r)}):"img"===i&&r.attr("src",t.currentItem.attr("src")),a||r.css("visibility","hidden"),r},update:function(e,r){(!a||i.forcePlaceholderSize)&&(r.height()||r.height(t.currentItem.innerHeight()-parseInt(t.currentItem.css("paddingTop")||0,10)-parseInt(t.currentItem.css("paddingBottom")||0,10)),r.width()||r.width(t.currentItem.innerWidth()-parseInt(t.currentItem.css("paddingLeft")||0,10)-parseInt(t.currentItem.css("paddingRight")||0,10)))}}),t.placeholder=e(i.placeholder.element.call(t.element,t.currentItem)),t.currentItem.after(t.placeholder),i.placeholder.update(t,t.placeholder)},_contactContainers:function(t){var a,i,r,s,n,o,d,u,h,l,m=null,c=null;for(a=this.containers.length-1;a>=0;a--)if(!e.contains(this.currentItem[0],this.containers[a].element[0]))if(this._intersectsWith(this.containers[a].containerCache)){if(m&&e.contains(this.containers[a].element[0],m.element[0]))continue;m=this.containers[a],c=a}else this.containers[a].containerCache.over&&(this.containers[a]._trigger("out",t,this._uiHash(this)),this.containers[a].containerCache.over=0);if(m)if(1===this.containers.length)this.containers[c].containerCache.over||(this.containers[c]._trigger("over",t,this._uiHash(this)),this.containers[c].containerCache.over=1);else{for(r=1e4,s=null,h=m.floating||this._isFloating(this.currentItem),n=h?"left":"top",o=h?"width":"height",l=h?"clientX":"clientY",i=this.items.length-1;i>=0;i--)e.contains(this.containers[c].element[0],this.items[i].item[0])&&this.items[i].item[0]!==this.currentItem[0]&&(d=this.items[i].item.offset()[n],u=!1,t[l]-d>this.items[i][o]/2&&(u=!0),r>Math.abs(t[l]-d)&&(r=Math.abs(t[l]-d),s=this.items[i],this.direction=u?"up":"down"));if(!s&&!this.options.dropOnEmpty)return;if(this.currentContainer===this.containers[c])return;s?this._rearrange(t,s,null,!0):this._rearrange(t,null,this.containers[c].element,!0),this._trigger("change",t,this._uiHash()),this.containers[c]._trigger("change",t,this._uiHash(this)),this.currentContainer=this.containers[c],this.options.placeholder.update(this.currentContainer,this.placeholder),this.containers[c]._trigger("over",t,this._uiHash(this)),this.containers[c].containerCache.over=1}},_createHelper:function(t){var a=this.options,i=e.isFunction(a.helper)?e(a.helper.apply(this.element[0],[t,this.currentItem])):"clone"===a.helper?this.currentItem.clone():this.currentItem;return i.parents("body").length||e("parent"!==a.appendTo?a.appendTo:this.currentItem[0].parentNode)[0].appendChild(i[0]),i[0]===this.currentItem[0]&&(this._storedCSS={width:this.currentItem[0].style.width,height:this.currentItem[0].style.height,position:this.currentItem.css("position"),top:this.currentItem.css("top"),left:this.currentItem.css("left")}),(!i[0].style.width||a.forceHelperSize)&&i.width(this.currentItem.width()),(!i[0].style.height||a.forceHelperSize)&&i.height(this.currentItem.height()),i},_adjustOffsetFromHelper:function(t){"string"==typeof t&&(t=t.split(" ")),e.isArray(t)&&(t={left:+t[0],top:+t[1]||0}),"left"in t&&(this.offset.click.left=t.left+this.margins.left),"right"in t&&(this.offset.click.left=this.helperProportions.width-t.right+this.margins.left),"top"in t&&(this.offset.click.top=t.top+this.margins.top),"bottom"in t&&(this.offset.click.top=this.helperProportions.height-t.bottom+this.margins.top)},_getParentOffset:function(){this.offsetParent=this.helper.offsetParent();var t=this.offsetParent.offset();return"absolute"===this.cssPosition&&this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])&&(t.left+=this.scrollParent.scrollLeft(),t.top+=this.scrollParent.scrollTop()),(this.offsetParent[0]===document.body||this.offsetParent[0].tagName&&"html"===this.offsetParent[0].tagName.toLowerCase()&&e.ui.ie)&&(t={top:0,left:0}),{top:t.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:t.left+(parseInt(this.offsetParent.css("borderLeftWidth"),10)||0)}},_getRelativeOffset:function(){if("relative"===this.cssPosition){var e=this.currentItem.position();return{top:e.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:e.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.currentItem.css("marginLeft"),10)||0,top:parseInt(this.currentItem.css("marginTop"),10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var t,a,i,r=this.options;"parent"===r.containment&&(r.containment=this.helper[0].parentNode),("document"===r.containment||"window"===r.containment)&&(this.containment=[0-this.offset.relative.left-this.offset.parent.left,0-this.offset.relative.top-this.offset.parent.top,e("document"===r.containment?document:window).width()-this.helperProportions.width-this.margins.left,(e("document"===r.containment?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top]),/^(document|window|parent)$/.test(r.containment)||(t=e(r.containment)[0],a=e(r.containment).offset(),i="hidden"!==e(t).css("overflow"),this.containment=[a.left+(parseInt(e(t).css("borderLeftWidth"),10)||0)+(parseInt(e(t).css("paddingLeft"),10)||0)-this.margins.left,a.top+(parseInt(e(t).css("borderTopWidth"),10)||0)+(parseInt(e(t).css("paddingTop"),10)||0)-this.margins.top,a.left+(i?Math.max(t.scrollWidth,t.offsetWidth):t.offsetWidth)-(parseInt(e(t).css("borderLeftWidth"),10)||0)-(parseInt(e(t).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left,a.top+(i?Math.max(t.scrollHeight,t.offsetHeight):t.offsetHeight)-(parseInt(e(t).css("borderTopWidth"),10)||0)-(parseInt(e(t).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top])},_convertPositionTo:function(t,a){a||(a=this.position);var i="absolute"===t?1:-1,r="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,s=/(html|body)/i.test(r[0].tagName);return{top:a.top+this.offset.relative.top*i+this.offset.parent.top*i-("fixed"===this.cssPosition?-this.scrollParent.scrollTop():s?0:r.scrollTop())*i,left:a.left+this.offset.relative.left*i+this.offset.parent.left*i-("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():s?0:r.scrollLeft())*i}},_generatePosition:function(t){var a,i,r=this.options,s=t.pageX,n=t.pageY,o="absolute"!==this.cssPosition||this.scrollParent[0]!==document&&e.contains(this.scrollParent[0],this.offsetParent[0])?this.scrollParent:this.offsetParent,d=/(html|body)/i.test(o[0].tagName);return"relative"!==this.cssPosition||this.scrollParent[0]!==document&&this.scrollParent[0]!==this.offsetParent[0]||(this.offset.relative=this._getRelativeOffset()),this.originalPosition&&(this.containment&&(t.pageX-this.offset.click.leftthis.containment[2]&&(s=this.containment[2]+this.offset.click.left),t.pageY-this.offset.click.top>this.containment[3]&&(n=this.containment[3]+this.offset.click.top)),r.grid&&(a=this.originalPageY+Math.round((n-this.originalPageY)/r.grid[1])*r.grid[1],n=this.containment?a-this.offset.click.top>=this.containment[1]&&a-this.offset.click.top<=this.containment[3]?a:a-this.offset.click.top>=this.containment[1]?a-r.grid[1]:a+r.grid[1]:a,i=this.originalPageX+Math.round((s-this.originalPageX)/r.grid[0])*r.grid[0],s=this.containment?i-this.offset.click.left>=this.containment[0]&&i-this.offset.click.left<=this.containment[2]?i:i-this.offset.click.left>=this.containment[0]?i-r.grid[0]:i+r.grid[0]:i)),{top:n-this.offset.click.top-this.offset.relative.top-this.offset.parent.top+("fixed"===this.cssPosition?-this.scrollParent.scrollTop():d?0:o.scrollTop()),left:s-this.offset.click.left-this.offset.relative.left-this.offset.parent.left+("fixed"===this.cssPosition?-this.scrollParent.scrollLeft():d?0:o.scrollLeft())}},_rearrange:function(e,t,a,i){a?a[0].appendChild(this.placeholder[0]):t.item[0].parentNode.insertBefore(this.placeholder[0],"down"===this.direction?t.item[0]:t.item[0].nextSibling),this.counter=this.counter?++this.counter:1;var r=this.counter;this._delay(function(){r===this.counter&&this.refreshPositions(!i)})},_clear:function(e,t){function a(e,t,a){return function(i){a._trigger(e,i,t._uiHash(t))}}this.reverting=!1;var i,r=[];if(!this._noFinalSort&&this.currentItem.parent().length&&this.placeholder.before(this.currentItem),this._noFinalSort=null,this.helper[0]===this.currentItem[0]){for(i in this._storedCSS)("auto"===this._storedCSS[i]||"static"===this._storedCSS[i])&&(this._storedCSS[i]="");this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper")}else this.currentItem.show();for(this.fromOutside&&!t&&r.push(function(e){this._trigger("receive",e,this._uiHash(this.fromOutside))}),!this.fromOutside&&this.domPosition.prev===this.currentItem.prev().not(".ui-sortable-helper")[0]&&this.domPosition.parent===this.currentItem.parent()[0]||t||r.push(function(e){this._trigger("update",e,this._uiHash())}),this!==this.currentContainer&&(t||(r.push(function(e){this._trigger("remove",e,this._uiHash())}),r.push(function(e){return function(t){e._trigger("receive",t,this._uiHash(this))}}.call(this,this.currentContainer)),r.push(function(e){return function(t){e._trigger("update",t,this._uiHash(this))}}.call(this,this.currentContainer)))),i=this.containers.length-1;i>=0;i--)t||r.push(a("deactivate",this,this.containers[i])),this.containers[i].containerCache.over&&(r.push(a("out",this,this.containers[i])),this.containers[i].containerCache.over=0);if(this.storedCursor&&(this.document.find("body").css("cursor",this.storedCursor),this.storedStylesheet.remove()),this._storedOpacity&&this.helper.css("opacity",this._storedOpacity),this._storedZIndex&&this.helper.css("zIndex","auto"===this._storedZIndex?"":this._storedZIndex),this.dragging=!1,this.cancelHelperRemoval){if(!t){for(this._trigger("beforeStop",e,this._uiHash()),i=0;r.length>i;i++)r[i].call(this,e);this._trigger("stop",e,this._uiHash())}return this.fromOutside=!1,!1}if(t||this._trigger("beforeStop",e,this._uiHash()),this.placeholder[0].parentNode.removeChild(this.placeholder[0]),this.helper[0]!==this.currentItem[0]&&this.helper.remove(),this.helper=null,!t){for(i=0;r.length>i;i++)r[i].call(this,e);this._trigger("stop",e,this._uiHash())}return this.fromOutside=!1,!0},_trigger:function(){e.Widget.prototype._trigger.apply(this,arguments)===!1&&this.cancel()},_uiHash:function(t){var a=t||this;return{helper:a.helper,placeholder:a.placeholder||e([]),position:a.position,originalPosition:a.originalPosition,offset:a.positionAbs,item:a.currentItem,sender:t?t.element:null}}}),e.widget("ui.accordion",{version:"1.11.0-beta.2",options:{active:0,animate:{},collapsible:!1,event:"click",header:"> li > :first-child,> :not(li):even",heightStyle:"auto",icons:{activeHeader:"ui-icon-triangle-1-s",header:"ui-icon-triangle-1-e"},activate:null,beforeActivate:null},hideProps:{borderTopWidth:"hide",borderBottomWidth:"hide",paddingTop:"hide",paddingBottom:"hide",height:"hide"},showProps:{borderTopWidth:"show",borderBottomWidth:"show",paddingTop:"show",paddingBottom:"show",height:"show"},_create:function(){var t=this.options;this.prevShow=this.prevHide=e(),this.element.addClass("ui-accordion ui-widget ui-helper-reset").attr("role","tablist"),t.collapsible||t.active!==!1&&null!=t.active||(t.active=0),this._processPanels(),0>t.active&&(t.active+=this.headers.length),this._refresh()},_getCreateEventData:function(){return{header:this.active,panel:this.active.length?this.active.next():e()}},_createIcons:function(){var t=this.options.icons;t&&(e("").addClass("ui-accordion-header-icon ui-icon "+t.header).prependTo(this.headers),this.active.children(".ui-accordion-header-icon").removeClass(t.header).addClass(t.activeHeader),this.headers.addClass("ui-accordion-icons"))},_destroyIcons:function(){this.headers.removeClass("ui-accordion-icons").children(".ui-accordion-header-icon").remove()},_destroy:function(){var e;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role"),this.headers.removeClass("ui-accordion-header ui-accordion-header-active ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("aria-selected").removeAttr("aria-controls").removeAttr("tabIndex").removeUniqueId(),this._destroyIcons(),e=this.headers.next().removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled").css("display","").removeAttr("role").removeAttr("aria-hidden").removeAttr("aria-labelledby").removeUniqueId(),"content"!==this.options.heightStyle&&e.css("height","")},_setOption:function(e,t){return"active"===e?(this._activate(t),void 0):("event"===e&&(this.options.event&&this._off(this.headers,this.options.event),this._setupEvents(t)),this._super(e,t),"collapsible"!==e||t||this.options.active!==!1||this._activate(0),"icons"===e&&(this._destroyIcons(),t&&this._createIcons()),"disabled"===e&&(this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this.headers.add(this.headers.next()).toggleClass("ui-state-disabled",!!t)),void 0)},_keydown:function(t){if(!t.altKey&&!t.ctrlKey){var a=e.ui.keyCode,i=this.headers.length,r=this.headers.index(t.target),s=!1;switch(t.keyCode){case a.RIGHT:case a.DOWN:s=this.headers[(r+1)%i];break;case a.LEFT:case a.UP:s=this.headers[(r-1+i)%i];break;case a.SPACE:case a.ENTER:this._eventHandler(t);break;case a.HOME:s=this.headers[0];break;case a.END:s=this.headers[i-1]}s&&(e(t.target).attr("tabIndex",-1),e(s).attr("tabIndex",0),s.focus(),t.preventDefault())}},_panelKeyDown:function(t){t.keyCode===e.ui.keyCode.UP&&t.ctrlKey&&e(t.currentTarget).prev().focus()},refresh:function(){var t=this.options;this._processPanels(),t.active===!1&&t.collapsible===!0||!this.headers.length?(t.active=!1,this.active=e()):t.active===!1?this._activate(0):this.active.length&&!e.contains(this.element[0],this.active[0])?this.headers.length===this.headers.find(".ui-state-disabled").length?(t.active=!1,this.active=e()):this._activate(Math.max(0,t.active-1)):t.active=this.headers.index(this.active),this._destroyIcons(),this._refresh()},_processPanels:function(){this.headers=this.element.find(this.options.header).addClass("ui-accordion-header ui-state-default ui-corner-all"),this.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom").filter(":not(.ui-accordion-content-active)").hide()},_refresh:function(){var t,a=this.options,i=a.heightStyle,r=this.element.parent();this.active=this._findActive(a.active).addClass("ui-accordion-header-active ui-state-active ui-corner-top").removeClass("ui-corner-all"),this.active.next().addClass("ui-accordion-content-active").show(),this.headers.attr("role","tab").each(function(){var t=e(this),a=t.uniqueId().attr("id"),i=t.next(),r=i.uniqueId().attr("id"); +t.attr("aria-controls",r),i.attr("aria-labelledby",a)}).next().attr("role","tabpanel"),this.headers.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}).next().attr({"aria-hidden":"true"}).hide(),this.active.length?this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}).next().attr({"aria-hidden":"false"}):this.headers.eq(0).attr("tabIndex",0),this._createIcons(),this._setupEvents(a.event),"fill"===i?(t=r.height(),this.element.siblings(":visible").each(function(){var a=e(this),i=a.css("position");"absolute"!==i&&"fixed"!==i&&(t-=a.outerHeight(!0))}),this.headers.each(function(){t-=e(this).outerHeight(!0)}),this.headers.next().each(function(){e(this).height(Math.max(0,t-e(this).innerHeight()+e(this).height()))}).css("overflow","auto")):"auto"===i&&(t=0,this.headers.next().each(function(){t=Math.max(t,e(this).css("height","").height())}).height(t))},_activate:function(t){var a=this._findActive(t)[0];a!==this.active[0]&&(a=a||this.active[0],this._eventHandler({target:a,currentTarget:a,preventDefault:e.noop}))},_findActive:function(t){return"number"==typeof t?this.headers.eq(t):e()},_setupEvents:function(t){var a={keydown:"_keydown"};t&&e.each(t.split(" "),function(e,t){a[t]="_eventHandler"}),this._off(this.headers.add(this.headers.next())),this._on(this.headers,a),this._on(this.headers.next(),{keydown:"_panelKeyDown"}),this._hoverable(this.headers),this._focusable(this.headers)},_eventHandler:function(t){var a=this.options,i=this.active,r=e(t.currentTarget),s=r[0]===i[0],n=s&&a.collapsible,o=n?e():r.next(),d=i.next(),u={oldHeader:i,oldPanel:d,newHeader:n?e():r,newPanel:o};t.preventDefault(),s&&!a.collapsible||this._trigger("beforeActivate",t,u)===!1||(a.active=n?!1:this.headers.index(r),this.active=s?e():r,this._toggle(u),i.removeClass("ui-accordion-header-active ui-state-active"),a.icons&&i.children(".ui-accordion-header-icon").removeClass(a.icons.activeHeader).addClass(a.icons.header),s||(r.removeClass("ui-corner-all").addClass("ui-accordion-header-active ui-state-active ui-corner-top"),a.icons&&r.children(".ui-accordion-header-icon").removeClass(a.icons.header).addClass(a.icons.activeHeader),r.next().addClass("ui-accordion-content-active")))},_toggle:function(t){var a=t.newPanel,i=this.prevShow.length?this.prevShow:t.oldPanel;this.prevShow.add(this.prevHide).stop(!0,!0),this.prevShow=a,this.prevHide=i,this.options.animate?this._animate(a,i,t):(i.hide(),a.show(),this._toggleComplete(t)),i.attr({"aria-hidden":"true"}),i.prev().attr("aria-selected","false"),a.length&&i.length?i.prev().attr({tabIndex:-1,"aria-expanded":"false"}):a.length&&this.headers.filter(function(){return 0===e(this).attr("tabIndex")}).attr("tabIndex",-1),a.attr("aria-hidden","false").prev().attr({"aria-selected":"true",tabIndex:0,"aria-expanded":"true"})},_animate:function(e,t,a){var i,r,s,n=this,o=0,d=e.length&&(!t.length||e.index()",delay:300,options:{icons:{submenu:"ui-icon-carat-1-e"},items:"> *",menus:"ul",position:{my:"left-1 top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().addClass("ui-menu ui-widget ui-widget-content").toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length).attr({role:this.options.role,tabIndex:0}),this.options.disabled&&this.element.addClass("ui-state-disabled").attr("aria-disabled","true"),this._on({"mousedown .ui-menu-item":function(e){e.preventDefault()},"click .ui-menu-item":function(t){var a=e(t.target);!this.mouseHandled&&a.not(".ui-state-disabled").length&&(this.select(t),t.isPropagationStopped()||(this.mouseHandled=!0),a.has(".ui-menu").length?this.expand(t):!this.element.is(":focus")&&e(this.document[0].activeElement).closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(t){var a=e(t.currentTarget);a.siblings(".ui-state-active").removeClass("ui-state-active"),this.focus(t,a)},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(e,t){var a=this.active||this.element.find(this.options.items).eq(0);t||this.focus(e,a)},blur:function(t){this._delay(function(){e.contains(this.element[0],this.document[0].activeElement)||this.collapseAll(t)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(e){this._closeOnDocumentClick(e)&&this.collapseAll(e),this.mouseHandled=!1}})},_destroy:function(){this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeClass("ui-menu ui-widget ui-widget-content ui-menu-icons ui-front").removeAttr("role").removeAttr("tabIndex").removeAttr("aria-labelledby").removeAttr("aria-expanded").removeAttr("aria-hidden").removeAttr("aria-disabled").removeUniqueId().show(),this.element.find(".ui-menu-item").removeClass("ui-menu-item").removeAttr("role").removeAttr("aria-disabled").removeUniqueId().removeClass("ui-state-hover").removeAttr("tabIndex").removeAttr("role").removeAttr("aria-haspopup").children().each(function(){var t=e(this);t.data("ui-menu-submenu-carat")&&t.remove()}),this.element.find(".ui-menu-divider").removeClass("ui-menu-divider ui-widget-content")},_keydown:function(t){function a(e){return e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}var i,r,s,n,o,d=!0;switch(t.keyCode){case e.ui.keyCode.PAGE_UP:this.previousPage(t);break;case e.ui.keyCode.PAGE_DOWN:this.nextPage(t);break;case e.ui.keyCode.HOME:this._move("first","first",t);break;case e.ui.keyCode.END:this._move("last","last",t);break;case e.ui.keyCode.UP:this.previous(t);break;case e.ui.keyCode.DOWN:this.next(t);break;case e.ui.keyCode.LEFT:this.collapse(t);break;case e.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(t);break;case e.ui.keyCode.ENTER:case e.ui.keyCode.SPACE:this._activate(t);break;case e.ui.keyCode.ESCAPE:this.collapse(t);break;default:d=!1,r=this.previousFilter||"",s=String.fromCharCode(t.keyCode),n=!1,clearTimeout(this.filterTimer),s===r?n=!0:s=r+s,o=RegExp("^"+a(s),"i"),i=this.activeMenu.find(this.options.items).filter(function(){return o.test(e(this).text())}),i=n&&-1!==i.index(this.active.next())?this.active.nextAll(".ui-menu-item"):i,i.length||(s=String.fromCharCode(t.keyCode),o=RegExp("^"+a(s),"i"),i=this.activeMenu.find(this.options.items).filter(function(){return o.test(e(this).text())})),i.length?(this.focus(t,i),i.length>1?(this.previousFilter=s,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter):delete this.previousFilter}d&&t.preventDefault()},_activate:function(e){this.active.is(".ui-state-disabled")||(this.active.is("[aria-haspopup='true']")?this.expand(e):this.select(e))},refresh:function(){var t,a,i=this,r=this.options.icons.submenu,s=this.element.find(this.options.menus);this.element.toggleClass("ui-menu-icons",!!this.element.find(".ui-icon").length),s.filter(":not(.ui-menu)").addClass("ui-menu ui-widget ui-widget-content ui-front").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var t=e(this),a=t.parent(),i=e("").addClass("ui-menu-icon ui-icon "+r).data("ui-menu-submenu-carat",!0);a.attr("aria-haspopup","true").prepend(i),t.attr("aria-labelledby",a.attr("id"))}),t=s.add(this.element),a=t.find(this.options.items),a.not(".ui-menu-item").each(function(){var t=e(this);i._isDivider(t)&&t.addClass("ui-widget-content ui-menu-divider")}),a.not(".ui-menu-item, .ui-menu-divider").addClass("ui-menu-item").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),a.filter(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!e.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(e,t){"icons"===e&&this.element.find(".ui-menu-icon").removeClass(this.options.icons.submenu).addClass(t.submenu),"disabled"===e&&this.element.toggleClass("ui-state-disabled",!!t).attr("aria-disabled",t),this._super(e,t)},focus:function(e,t){var a,i;this.blur(e,e&&"focus"===e.type),this._scrollIntoView(t),this.active=t.first(),i=this.active.addClass("ui-state-focus").removeClass("ui-state-active"),this.options.role&&this.element.attr("aria-activedescendant",i.attr("id")),this.active.parent().closest(".ui-menu-item").addClass("ui-state-active"),e&&"keydown"===e.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),a=t.children(".ui-menu"),a.length&&e&&/^mouse/.test(e.type)&&this._startOpening(a),this.activeMenu=t.parent(),this._trigger("focus",e,{item:t})},_scrollIntoView:function(t){var a,i,r,s,n,o;this._hasScroll()&&(a=parseFloat(e.css(this.activeMenu[0],"borderTopWidth"))||0,i=parseFloat(e.css(this.activeMenu[0],"paddingTop"))||0,r=t.offset().top-this.activeMenu.offset().top-a-i,s=this.activeMenu.scrollTop(),n=this.activeMenu.height(),o=t.outerHeight(),0>r?this.activeMenu.scrollTop(s+r):r+o>n&&this.activeMenu.scrollTop(s+r-n+o))},blur:function(e,t){t||clearTimeout(this.timer),this.active&&(this.active.removeClass("ui-state-focus"),this.active=null,this._trigger("blur",e,{item:this.active}))},_startOpening:function(e){clearTimeout(this.timer),"true"===e.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(e)},this.delay))},_open:function(t){var a=e.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(t.parents(".ui-menu")).hide().attr("aria-hidden","true"),t.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(a)},collapseAll:function(t,a){clearTimeout(this.timer),this.timer=this._delay(function(){var i=a?this.element:e(t&&t.target).closest(this.element.find(".ui-menu"));i.length||(i=this.element),this._close(i),this.blur(t),this.activeMenu=i},this.delay)},_close:function(e){e||(e=this.active?this.active.parent():this.element),e.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false").end().find(".ui-state-active").not(".ui-state-focus").removeClass("ui-state-active")},_closeOnDocumentClick:function(t){return!e(t.target).closest(".ui-menu").length},_isDivider:function(e){return!/[^\-\u2014\u2013\s]/.test(e.text())},collapse:function(e){var t=this.active&&this.active.parent().closest(".ui-menu-item",this.element);t&&t.length&&(this._close(),this.focus(e,t))},expand:function(e){var t=this.active&&this.active.children(".ui-menu ").find(this.options.items).first();t&&t.length&&(this._open(t.parent()),this._delay(function(){this.focus(e,t)}))},next:function(e){this._move("next","first",e)},previous:function(e){this._move("prev","last",e)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(e,t,a){var i;this.active&&(i="first"===e||"last"===e?this.active["first"===e?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[e+"All"](".ui-menu-item").eq(0)),i&&i.length&&this.active||(i=this.activeMenu.find(this.options.items)[t]()),this.focus(a,i)},nextPage:function(t){var a,i,r;return this.active?(this.isLastItem()||(this._hasScroll()?(i=this.active.offset().top,r=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return a=e(this),0>a.offset().top-i-r}),this.focus(t,a)):this.focus(t,this.activeMenu.find(this.options.items)[this.active?"last":"first"]())),void 0):(this.next(t),void 0)},previousPage:function(t){var a,i,r;return this.active?(this.isFirstItem()||(this._hasScroll()?(i=this.active.offset().top,r=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return a=e(this),a.offset().top-i+r>0}),this.focus(t,a)):this.focus(t,this.activeMenu.find(this.options.items).first())),void 0):(this.next(t),void 0)},_hasScroll:function(){return this.element.outerHeight()",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,_create:function(){var t,a,i,r=this.element[0].nodeName.toLowerCase(),s="textarea"===r,n="input"===r;this.isMultiLine=s?!0:n?!1:this.element.prop("isContentEditable"),this.valueMethod=this.element[s||n?"val":"text"],this.isNewMenu=!0,this.element.addClass("ui-autocomplete-input").attr("autocomplete","off"),this._on(this.element,{keydown:function(r){if(this.element.prop("readOnly"))return t=!0,i=!0,a=!0,void 0;t=!1,i=!1,a=!1;var s=e.ui.keyCode;switch(r.keyCode){case s.PAGE_UP:t=!0,this._move("previousPage",r);break;case s.PAGE_DOWN:t=!0,this._move("nextPage",r);break;case s.UP:t=!0,this._keyEvent("previous",r);break;case s.DOWN:t=!0,this._keyEvent("next",r);break;case s.ENTER:this.menu.active&&(t=!0,r.preventDefault(),this.menu.select(r));break;case s.TAB:this.menu.active&&this.menu.select(r);break;case s.ESCAPE:this.menu.element.is(":visible")&&(this._value(this.term),this.close(r),r.preventDefault());break;default:a=!0,this._searchTimeout(r)}},keypress:function(i){if(t)return t=!1,(!this.isMultiLine||this.menu.element.is(":visible"))&&i.preventDefault(),void 0;if(!a){var r=e.ui.keyCode;switch(i.keyCode){case r.PAGE_UP:this._move("previousPage",i);break;case r.PAGE_DOWN:this._move("nextPage",i);break;case r.UP:this._keyEvent("previous",i);break;case r.DOWN:this._keyEvent("next",i)}}},input:function(e){return i?(i=!1,e.preventDefault(),void 0):(this._searchTimeout(e),void 0)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(e){return this.cancelBlur?(delete this.cancelBlur,void 0):(clearTimeout(this.searching),this.close(e),this._change(e),void 0)}}),this._initSource(),this.menu=e("