switch(recursiveDepthValue) {
            case -1:
                // Undefined stays undefined
                nextOp.get(RECURSIVE_DEPTH.getName()).set(op.get(RECURSIVE_DEPTH.getName()));
                break;
            case 0:
                nextOp.get(RECURSIVE_DEPTH.getName()).set(recursiveDepthValue);
                break;
            default:
                nextOp.get(RECURSIVE_DEPTH.getName()).set(recursiveDepthValue - 1);
                break;
        }

/*
 * JBoss, Home of Professional Open Source.
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 */
package org.jboss.as.controller.operations.global;

import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ACCESS_CONTROL;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADDRESS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CHILD_TYPE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FAILURE_DESCRIPTION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.HOST;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.INCLUDE_RUNTIME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OUTCOME;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_ATTRIBUTE_OPERATION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_CHILDREN_NAMES_OPERATION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_CHILDREN_RESOURCES_OPERATION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_RESOURCE_DESCRIPTION_OPERATION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.READ_RESOURCE_OPERATION;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.REMOVE;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESPONSE_HEADERS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RESULT;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RUNNING_SERVER;
import static org.jboss.as.controller.operations.global.GlobalOperationAttributes.RECURSIVE;
import static org.jboss.as.controller.operations.global.GlobalOperationAttributes.RECURSIVE_DEPTH;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import org.jboss.as.controller.ModelVersion;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ProcessType;
import org.jboss.as.controller.UnauthorizedException;
import org.jboss.as.controller._private.OperationFailedRuntimeException;
import org.jboss.as.controller.access.Action;
import org.jboss.as.controller.access.AuthorizationResult;
import org.jboss.as.controller.access.ResourceNotAddressableException;
import org.jboss.as.controller.access.rbac.UnknowRoleException;
import org.jboss.as.controller.logging.ControllerLogger;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.controller.registry.AliasEntry;
import org.jboss.as.controller.registry.AliasEntry.AliasContext;
import org.jboss.as.controller.registry.ImmutableManagementResourceRegistration;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.as.controller.registry.Resource;
import org.jboss.as.controller.registry.WildcardReadResourceDescriptionAddressHack;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;

/**
 * Global {@code OperationHandler}s.
 *
 * @author Kabir Khan
 */
public class GlobalOperationHandlers {

    public static final Set<String> STD_WRITE_OPS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(ADD, REMOVE, WriteAttributeHandler.DEFINITION.getName(), UndefineAttributeHandler.DEFINITION.getName(), MapOperations.MAP_PUT_DEFINITION.getName(), MapOperations.MAP_CLEAR_DEFINITION.getName(), MapOperations.MAP_REMOVE_DEFINITION.getName(), ListOperations.LIST_ADD_DEFINITION.getName(), ListOperations.LIST_CLEAR_DEFINITION.getName(), ListOperations.LIST_REMOVE_DEFINITION.getName())));

    public static final Set<String> STD_READ_OPS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(ReadResourceHandler.DEFINITION.getName(), ReadAttributeHandler.DEFINITION.getName(), ReadAttributeGroupHandler.DEFINITION.getName(), ReadResourceDescriptionHandler.DEFINITION.getName(), ReadAttributeGroupNamesHandler.DEFINITION.getName(), ReadChildrenNamesHandler.DEFINITION.getName(), ReadChildrenTypesHandler.DEFINITION.getName(), ReadChildrenResourcesHandler.DEFINITION.getName(), ReadOperationNamesHandler.DEFINITION.getName(), QueryOperationHandler.DEFINITION.getName(), MapOperations.MAP_GET_DEFINITION.getName(), ListOperations.LIST_GET_DEFINITION.getName(), ReadOperationDescriptionHandler.DEFINITION.getName())));

    public static void registerGlobalOperations(ManagementResourceRegistration root, ProcessType processType) {
        if (processType.isHostController()) {
            root.registerOperationHandler(ReadResourceHandler.DEFINITION, ReadResourceHandler.INSTANCE, true);
            root.registerOperationHandler(ReadAttributeHandler.DEFINITION, ReadAttributeHandler.INSTANCE, true);
            root.registerOperationHandler(ReadAttributeGroupHandler.DEFINITION, ReadAttributeGroupHandler.INSTANCE, true);
        } else {
            root.registerOperationHandler(ReadResourceHandler.RESOLVE_DEFINITION, ReadResourceHandler.RESOLVE_INSTANCE, true);
            root.registerOperationHandler(ReadAttributeHandler.RESOLVE_DEFINITION, ReadAttributeHandler.RESOLVE_INSTANCE, true);
            root.registerOperationHandler(ReadAttributeGroupHandler.RESOLVE_DEFINITION, ReadAttributeGroupHandler.RESOLVE_INSTANCE, true);
        }
        root.registerOperationHandler(ReadResourceDescriptionHandler.DEFINITION, ReadResourceDescriptionHandler.INSTANCE, true);
        root.registerOperationHandler(ReadAttributeGroupNamesHandler.DEFINITION, ReadAttributeGroupNamesHandler.INSTANCE, true);
        root.registerOperationHandler(ReadChildrenNamesHandler.DEFINITION, ReadChildrenNamesHandler.INSTANCE, true);
        root.registerOperationHandler(ReadChildrenTypesHandler.DEFINITION, ReadChildrenTypesHandler.INSTANCE, true);
        root.registerOperationHandler(ReadChildrenResourcesHandler.DEFINITION, ReadChildrenResourcesHandler.INSTANCE, true);
        root.registerOperationHandler(ReadOperationNamesHandler.DEFINITION, ReadOperationNamesHandler.INSTANCE, true);
        root.registerOperationHandler(ReadOperationDescriptionHandler.DEFINITION, ReadOperationDescriptionHandler.INSTANCE, true);
        root.registerOperationHandler(QueryOperationHandler.DEFINITION, QueryOperationHandler.INSTANCE, true);
        // map operations
        root.registerOperationHandler(MapOperations.MAP_PUT_DEFINITION, MapOperations.MAP_PUT_HANDLER, true);
        root.registerOperationHandler(MapOperations.MAP_GET_DEFINITION, MapOperations.MAP_GET_HANDLER, true);
        root.registerOperationHandler(MapOperations.MAP_REMOVE_DEFINITION, MapOperations.MAP_REMOVE_HANDLER, true);
        root.registerOperationHandler(MapOperations.MAP_CLEAR_DEFINITION, MapOperations.MAP_CLEAR_HANDLER, true);
        // list operations
        root.registerOperationHandler(ListOperations.LIST_ADD_DEFINITION, ListOperations.LIST_ADD_HANDLER, true);
        root.registerOperationHandler(ListOperations.LIST_REMOVE_DEFINITION, ListOperations.LIST_REMOVE_HANDLER, true);
        root.registerOperationHandler(ListOperations.LIST_GET_DEFINITION, ListOperations.LIST_GET_HANDLER, true);
        root.registerOperationHandler(ListOperations.LIST_CLEAR_DEFINITION, ListOperations.LIST_CLEAR_HANDLER, true);
        root.registerOperationHandler(ReadResourceDescriptionHandler.CheckResourceAccessHandler.DEFINITION, new OperationStepHandler() {

            @Override
            public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
                // Just use an empty operation handler here, this is a private operation and people who want to call it need to instantiate the step handler
                throw new OperationFailedException("This should never be called");
            }
        }, true);
        root.registerOperationHandler(ReadResourceDescriptionHandler.CheckResourceAccessHandler.DEFAULT_DEFINITION, new OperationStepHandler() {

            @Override
            public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
                // Just use an empty operation handler here, this is a private operation and people who want to call it need to instantiate the step handler
                throw new OperationFailedException("This should never be called");
            }
        }, true);
        root.registerOperationHandler(WriteAttributeHandler.DEFINITION, WriteAttributeHandler.INSTANCE, true);
        root.registerOperationHandler(UndefineAttributeHandler.DEFINITION, UndefineAttributeHandler.INSTANCE, true);
    }

    public static final String CHECK_DEFAULT_RESOURCE_ACCESS = "check-default-resource-access";

    public static final String CHECK_RESOURCE_ACCESS = "check-resource-access";

    private GlobalOperationHandlers() {
    }

    public abstract static class AbstractMultiTargetHandler implements OperationStepHandler {

        public static final ModelNode FAKE_OPERATION;

        static {
            final ModelNode resolve = new ModelNode();
            resolve.get(OP).set("resolve");
            resolve.get(OP_ADDR).setEmptyList();
            resolve.protect();
            FAKE_OPERATION = resolve;
        }

        private final FilteredData filteredData;

        private final boolean ignoreMissingResource;

        private final FilterPredicate predicate;

        private final boolean registryOnly;

        protected AbstractMultiTargetHandler() {
            this(null, false);
        }

        protected AbstractMultiTargetHandler(boolean registryOnly) {
            this(null, false, null, registryOnly);
        }

        protected AbstractMultiTargetHandler(FilteredData filteredData) {
            this(filteredData, false);
        }

        protected AbstractMultiTargetHandler(FilteredData filteredData, boolean ignoreMissingResource) {
            this(filteredData, ignoreMissingResource, null);
        }

        protected AbstractMultiTargetHandler(FilteredData filteredData, boolean ignoreMissingResource, FilterPredicate predicate) {
            this(filteredData, ignoreMissingResource, predicate, false);
        }

        private AbstractMultiTargetHandler(FilteredData filteredData, boolean ignoreMissingResource, FilterPredicate predicate, boolean registryOnly) {
            this.filteredData = filteredData;
            this.ignoreMissingResource = ignoreMissingResource;
            this.predicate = predicate;
            this.registryOnly = registryOnly;
        }

        protected FilteredData getFilteredData() {
            return filteredData;
        }

        @Override
        public void execute(final OperationContext context, final ModelNode operation) throws OperationFailedException {
            final PathAddress address = context.getCurrentAddress();
            // In case if it's a multiTarget operation, resolve the address first
            // This only works for model resources, which can be resolved into a concrete addresses
            if (address.isMultiTarget()) {
                final FilteredData localFilteredData = filteredData == null ? new FilteredData(PathAddress.EMPTY_ADDRESS) : filteredData;
                // The final result should be a list of executed operations
                final ModelNode result = context.getResult().setEmptyList();
                // Trick the context to give us the model-root
                final OperationStepHandler delegateStepHandler = new OperationStepHandler() {

                    @Override
                    public void execute(final OperationContext context, final ModelNode operation) throws OperationFailedException {
                        doExecute(context, operation, localFilteredData, true);
                    }
                };
                final ModelNode fakeOperationResponse = new ModelNode();
                context.addStep(fakeOperationResponse, FAKE_OPERATION.clone(), registryOnly ? new RegistrationAddressResolver(operation, result, delegateStepHandler) : new ModelAddressResolver(operation, result, localFilteredData, delegateStepHandler, predicate), OperationContext.Stage.MODEL, true);
                context.completeStep(new MultiTargetResultHandler(fakeOperationResponse, localFilteredData, result));
            } else {
                doExecute(context, operation, filteredData, ignoreMissingResource);
            }
        }

        /**
         * Execute the actual operation if it is not addressed to multiple targets.
         *
         * @param context      the operation context
         * @param operation    the original operation
         * @param filteredData tracking object for filtered data
         * @param ignoreMissingResource {@code false} if execution should throw
         *                          {@link org.jboss.as.controller.registry.Resource.NoSuchResourceException} if the
         *                          targeted resource does not exist; {@code true} if it should simply
         *                          not provide a result
         * @throws OperationFailedException
         */
        abstract void doExecute(OperationContext context, ModelNode operation, FilteredData filteredData, boolean ignoreMissingResource) throws OperationFailedException;

        private static class MultiTargetResultHandler implements OperationContext.ResultHandler {

            private final FilteredData localFilteredData;

            private final ModelNode result;

            private final ModelNode fakeOperationResponse;

            public MultiTargetResultHandler(ModelNode fakeOperationResponse, FilteredData localFilteredData, ModelNode result) {
                this.localFilteredData = localFilteredData;
                this.result = result;
                this.fakeOperationResponse = fakeOperationResponse;
            }

            @Override
            public void handleResult(OperationContext.ResultAction resultAction, OperationContext context, ModelNode operation) {
                if (fakeOperationResponse != null && fakeOperationResponse.hasDefined(FAILURE_DESCRIPTION)) {
                    context.getFailureDescription().set(fakeOperationResponse.get(FAILURE_DESCRIPTION));
                    return;
                }
                // Report on filtering
                if (localFilteredData.hasFilteredData()) {
                    context.getResponseHeaders().get(ACCESS_CONTROL).set(localFilteredData.toModelNode());
                }
                // Extract any failure info from the individual results and use them
                // to construct an overall failure description if necessary
                if (resultAction == OperationContext.ResultAction.ROLLBACK && !context.hasFailureDescription() && result.isDefined()) {
                    String op = operation.require(OP).asString();
                    Map<PathAddress, ModelNode> failures = new HashMap<PathAddress, ModelNode>();
                    for (ModelNode resultItem : result.asList()) {
                        if (resultItem.hasDefined(FAILURE_DESCRIPTION)) {
                            final PathAddress failedAddress = PathAddress.pathAddress(resultItem.get(ADDRESS));
                            ModelNode failedDesc = resultItem.get(FAILURE_DESCRIPTION);
                            failures.put(failedAddress, failedDesc);
                        }
                    }
                    if (failures.size() == 1) {
                        Map.Entry<PathAddress, ModelNode> entry = failures.entrySet().iterator().next();
                        if (entry.getValue().getType() == ModelType.STRING) {
                            context.getFailureDescription().set(ControllerLogger.ROOT_LOGGER.wildcardOperationFailedAtSingleAddress(op, entry.getKey(), entry.getValue().asString()));
                        } else {
                            context.getFailureDescription().set(ControllerLogger.ROOT_LOGGER.wildcardOperationFailedAtSingleAddressWithComplexFailure(op, entry.getKey()));
                        }
                    } else if (failures.size() > 1) {
                        context.getFailureDescription().set(ControllerLogger.ROOT_LOGGER.wildcardOperationFailedAtMultipleAddresses(op, failures.keySet()));
                    }
                }
            }
        }
    }

    @FunctionalInterface
    interface FilterPredicate extends Predicate<ModelNode> {
    }

    private abstract static class AbstractAddressResolver implements OperationStepHandler {

        private static final FilterPredicate DEFAULT_PREDICATE = item -> !item.isDefined() || !item.hasDefined(OP_ADDR);

        private final ModelNode operation;

        private final ModelNode result;

        private final FilteredData filteredData;

        private final FilterPredicate predicate;

        // handler bypassing further wildcard resolution
        private final OperationStepHandler handler;

        public AbstractAddressResolver(final ModelNode operation, final ModelNode result, final OperationStepHandler delegate, final FilteredData filteredData, final FilterPredicate predicate) {
            this.operation = operation;
            this.result = result;
            this.handler = delegate;
            this.predicate = predicate == null ? DEFAULT_PREDICATE : predicate;
            this.filteredData = filteredData;
        }

        @Override
        public void execute(final OperationContext context, final ModelNode ignored) throws OperationFailedException {
            final PathAddress addr = PathAddress.pathAddress(operation.require(OP_ADDR));
            // This will only return the alias if this is for a read-resource-definition
            final PathAddress aliasAddr = WildcardReadResourceDescriptionAddressHack.detachAliasAddress(context, operation);
            final PathAddress address = aliasAddr == null ? addr : aliasAddr;
            execute(PathAddress.EMPTY_ADDRESS, address, context, context.getRootResourceRegistration(), true);
            context.completeStep(new OperationContext.ResultHandler() {

                @Override
                public void handleResult(OperationContext.ResultAction resultAction, OperationContext context, ModelNode operation) {
                    if (result.getType() == ModelType.LIST) {
                        boolean replace = false;
                        ModelNode replacement = new ModelNode().setEmptyList();
                        for (ModelNode item : result.asList()) {
                            if (predicate.test(item)) {
                                // item will be skipped and the result amended
                                replace = true;
                            } else {
                                replacement.add(item);
                            }
                        }
                        if (replace) {
                            result.set(replacement);
                        }
                    }
                }
            });
        }

        /**
         * Wraps a call to
         * {@link #execute(org.jboss.as.controller.PathAddress, org.jboss.as.controller.PathAddress, org.jboss.as.controller.OperationContext, org.jboss.as.controller.registry.ImmutableManagementResourceRegistration, boolean)}
         * with logic to handle authorization and missing resource exceptions.
         *
         * @param base the portion of the address that has already been processed
         * @param remaining the unprocessed portion of the original address
         * @param context  context of the request
         * @param registration the resource registration for the {@code base} address
         * @param ignoreMissing {@code true} if this is being called for an "optional" portion of an address tree
         *                                  and NoSuchResourceException should be ignored. A portion of an address
         *                                  tree is optional if it is associated with a wildcard or multi-segment
         *                                  element in {@code address}
         */
        protected void safeExecute(final PathAddress base, final PathAddress remaining, final OperationContext context, final ImmutableManagementResourceRegistration registration, boolean ignoreMissing) {
            try {
                ControllerLogger.MGMT_OP_LOGGER.tracef("safeExecute for %s, remaining is %s", base, remaining);
                execute(base, remaining, context, registration, ignoreMissing);
            } catch (UnauthorizedException e) {
                // equivalent to the resource not existing
                // Just report the failure to the filter and complete normally
                filteredData.addReadRestrictedResource(base);
                ControllerLogger.MGMT_OP_LOGGER.tracef("Caught UnauthorizedException in %s", this);
            } catch (ResourceNotAddressableException e) {
                // Just report the failure to the filter and complete normally
                filteredData.addAccessRestrictedResource(base);
                ControllerLogger.MGMT_OP_LOGGER.tracef("Caught ResourceNotAddressableException in %s", this);
            } catch (Resource.NoSuchResourceException e) {
                // It's possible this is a remote failure, in which case we
                // don't get ResourceNotAddressableException. So see if
                // it was due to any authorization denial
                ModelNode toAuthorize = Util.createEmptyOperation(READ_RESOURCE_OPERATION, base);
                AuthorizationResult.Decision decision = context.authorize(toAuthorize, EnumSet.of(Action.ActionEffect.ADDRESS)).getDecision();
                ControllerLogger.MGMT_OP_LOGGER.tracef("Caught NoSuchResourceException in %s. Authorization decision is %s", this, decision);
                if (decision == AuthorizationResult.Decision.DENY) {
                    // Just report the failure to the filter and complete normally
                    filteredData.addAccessRestrictedResource(base);
                } else if (!ignoreMissing) {
                    throw e;
                }
            }
        }

        /**
         * Resolve matching addresses for the portions of {@code address} equal to or below {@code base},
         * adding a step to invoke the {@code this.handler} for each real address that is a leaf. Wildcards
         * and multi-segment addresses are handled.
         *
         * @param base the portion of the address that has already been processed
         * @param remaining the unprocessed portion of the original address
         * @param context  context of the request
         * @param registration the resource registration for the {@code base} address
         * @param ignoreMissing {@code true} if this is being called for an "optional" portion of an address tree
         *                                  and NoSuchResourceException should be ignored. A portion of an address
         *                                  tree is optional if it is associated with a wildcard or multi-segment
         *                                  element in {@code address}
         */
        protected void execute(final PathAddress base, final PathAddress remaining, final OperationContext context, final ImmutableManagementResourceRegistration registration, final boolean ignoreMissing) {
            // Check whether the operation needs to be dispatched to a remote proxy
            if (registration.isRemote()) {
                if (isWFCORE621Needed(registration, remaining)) {
                    executeWFCORE621(base, remaining, context, registration, ignoreMissing);
                } else {
                    executeRemote(base, remaining, context, registration, ignoreMissing);
                }
                // No further processing needed
                return;
            }
            if (!authorize(context, base, operation)) {
                return;
            }
            if (remaining.size() > 0) {
                final PathElement currentElement = remaining.getElement(0);
                final PathAddress newRemaining = remaining.subAddress(1);
                if (currentElement.isMultiTarget()) {
                    executeMultiTargetChildren(base, currentElement, newRemaining, context, registration, ignoreMissing);
                } else {
                    executeSingleTargetChild(base, currentElement, newRemaining, context, ignoreMissing);
                }
            } else {
                final ModelNode newOp = operation.clone();
                newOp.get(OP_ADDR).set(base.toModelNode());
                final ModelNode resultItem = this.result.add();
                ControllerLogger.MGMT_OP_LOGGER.tracef("Added ModelAddressResolver result item for %s", base);
                final ModelNode resultAddress = resultItem.get(OP_ADDR);
                final OperationStepHandler wrapper = new OperationStepHandler() {

                    @Override
                    public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
                        try {
                            handler.execute(context, operation);
                            context.completeStep(new OperationContext.ResultHandler() {

                                @Override
                                public void handleResult(OperationContext.ResultAction resultAction, OperationContext context, ModelNode operation) {
                                    ControllerLogger.MGMT_OP_LOGGER.tracef("ModelAddressResolver result for %s is %s", base, resultItem);
                                    if (resultItem.hasDefined(RESULT)) {
                                        resultAddress.set(base.toModelNode());
                                        if (resultItem.hasDefined(RESPONSE_HEADERS, ACCESS_CONTROL)) {
                                            ModelNode headers = resultItem.get(RESPONSE_HEADERS);
                                            ModelNode acc = headers.remove(ACCESS_CONTROL);
                                            if (headers.asInt() == 0) {
                                                resultItem.remove(RESPONSE_HEADERS);
                                            }
                                            filteredData.populate(acc, PathAddress.EMPTY_ADDRESS);
                                        }
                                    } else {
                                        resultItem.clear();
                                    }
                                }
                            });
                        } catch (Resource.NoSuchResourceException e) {
                            // just discard the result to avoid leaking the inaccessible address
                        }
                    }
                };
                context.addStep(resultItem, newOp, wrapper, OperationContext.Stage.MODEL, true);
            }
        }

        protected abstract void executeSingleTargetChild(PathAddress base, PathElement currentElement, PathAddress newRemaining, OperationContext context, boolean ignoreMissing);

        protected abstract void executeMultiTargetChildren(PathAddress base, PathElement currentElement, PathAddress newRemaining, OperationContext context, ImmutableManagementResourceRegistration registration, boolean ignoreMissing);

        /**
         * If not authorized, this will throw an exception for {@link ModelAddressResolver} for use with the
         * {@link ModelAddressResolver#safeExecute(PathAddress, PathAddress, OperationContext, ImmutableManagementResourceRegistration, boolean)}
         * method. For {@link RegistrationAddressResolver} it will return {@code false}. Otherwise it returns {@code true}
         *
         * @param context the operation context
         * @param base the path address
         * @param operation the operation
         * @return whether or not we were authorized
         */
        protected abstract boolean authorize(OperationContext context, PathAddress base, ModelNode operation);

        private boolean isWFCORE621Needed(ImmutableManagementResourceRegistration registration, PathAddress remaining) {
            if (remaining.size() > 0) {
                PathElement pe = remaining.getElement(0);
                if (pe.isMultiTarget() && RUNNING_SERVER.equals(pe.getKey())) {
                    // We only need this for WildFly 8 and earlier (including EAP 6),
                    // so that's proxied controllers running kernel version 1.x or 2.x
                    ModelVersion modelVersion = registration.getProxyController(PathAddress.EMPTY_ADDRESS).getKernelModelVersion();
                    return modelVersion.getMajor() < 3;
                }
            }
            return false;
        }

        private void executeWFCORE621(PathAddress base, PathAddress remaining, OperationContext context, ImmutableManagementResourceRegistration registration, boolean ignoreMissing) {
            ControllerLogger.MGMT_OP_LOGGER.tracef("Executing WFCORE-621 op for base %s and remaining %s", base, remaining);
            // We have distinct handling for WildFly 8
            // TODO a mixed domain of WildFly > 9 managing WildFly 8 is unlikely to work, so this can likely be dropped
            final boolean wildfly8 = registration.getProxyController(PathAddress.EMPTY_ADDRESS).getKernelModelVersion().getMajor() == 2;
            // We have a request for /host=foo/server=*[/...] targeted at a host that
            // doesn't have the WFCORE-282 fix available and thus can't handle that request.
            // So, we are going to execute a step to have it provide us the names of all
            // its servers, and then a step that will loop through the server names and
            // add the usual execution for each
            final ModelNode serverNameResponse = new ModelNode();
            final AtomicBoolean filtered = new AtomicBoolean(false);
            // We're adding steps to the top of the queue, so add the one that will use the server names first
            context.addStep(new OperationStepHandler() {

                @Override
                public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
                    ControllerLogger.MGMT_OP_LOGGER.tracef("Executing WFCORE-621 2nd step for base %s and remaining %s; filtered? %s serverNames=%s", base, remaining, filtered, serverNameResponse);
                    // If the read of server names was filtered or for some other reason we didn't get them, we are done.
                    if (filtered.get() || !serverNameResponse.hasDefined(RESULT)) {
                        return;
                    }
                    Set<String> targetServers = extractServerNames(serverNameResponse.get(RESULT), operation, remaining, wildfly8);
                    PathAddress afterServer = remaining.size() > 1 ? remaining.subAddress(1) : PathAddress.EMPTY_ADDRESS;
                    for (String targetServer : targetServers) {
                        PathAddress newBase = base.append(PathElement.pathElement(RUNNING_SERVER, targetServer));
                        safeExecute(newBase, afterServer, context, registration, ignoreMissing);
                    }
                }
            }, OperationContext.Stage.MODEL, true);
            // Now add the step to read the server names.
            // For WildFly 8 slaves we use read-children-resources because read-children-names includes
            // server names that have a server-config but aren't started. So in the handler above
            // we use the resource node to distinguish those cases
            final String opName = wildfly8 ? READ_CHILDREN_RESOURCES_OPERATION : READ_CHILDREN_NAMES_OPERATION;
            ModelNode op = Util.createEmptyOperation(opName, base);
            op.get(CHILD_TYPE).set(RUNNING_SERVER);
            OperationStepHandler proxyHandler = registration.getOperationHandler(PathAddress.EMPTY_ADDRESS, opName);
            // Use a custom handler to deal with the remote host not being readable (e.g. RBAC)
            OperationStepHandler filterableHandler = new FilterableRemoteOperationStepHandler(proxyHandler, base, filtered, filteredData, ignoreMissing);
            context.addStep(serverNameResponse, op, filterableHandler, OperationContext.Stage.MODEL, true);
        }

        private static Set<String> extractServerNames(ModelNode serverResultNode, ModelNode operation, PathAddress remaining, boolean wildfly8) {
            PathElement serverPE = remaining.getElement(0);
            Set<String> interestingServers = null;
            if (!serverPE.isWildcard()) {
                interestingServers = new HashSet<String>();
                Collections.addAll(interestingServers, serverPE.getSegments());
            }
            Set<String> result = new LinkedHashSet<>();
            if (wildfly8) {
                // TODO a mixed domain of WildFly > 9 managing WildFly 8 is unlikely to work, so all this can likely be dropped
                // The op we ran was read-children-resources, so we got back an object
                for (String serverName : serverResultNode.keys()) {
                    if (interestingServers == null || interestingServers.contains(serverName)) {
                        ModelNode serverVal = serverResultNode.get(serverName);
                        // If we get an undefined or empty node this indicates there's just a placeholder resource
                        // for a non-started server-config
                        boolean validServer = serverVal.isDefined() && serverVal.asInt() > 0;
                        if (!validServer && remaining.size() == 1) {
                            // Request was for the server node itself.
                            // Begin horrendous hacks for WildFly 8 support, where a runtime-only resource
                            // with a couple of attributes is available for non-started servers.
                            String opName = operation.get(OP).asString();
                            if (READ_ATTRIBUTE_OPERATION.equals(opName)) {
                                String attrName = operation.get(NAME).asString();
                                validServer = "launch-type".equals(attrName) || "server-state".equals(attrName);
                            } else if (READ_RESOURCE_OPERATION.equals(opName)) {
                                validServer = operation.hasDefined(INCLUDE_RUNTIME) && operation.get(INCLUDE_RUNTIME).asBoolean();
                            }
                        }
                        if (validServer) {
                            result.add(serverName);
                        }
                    }
                }
            } else {
                // The op we ran was read-children-names so we got back a list of string
                for (ModelNode serverNameNode : serverResultNode.asList()) {
                    String serverName = serverNameNode.asString();
                    if (interestingServers == null || interestingServers.contains(serverName)) {
                        result.add(serverName);
                    }
                }
            }
            return result;
        }

        private void executeRemote(final PathAddress base, final PathAddress remaining, OperationContext context, ImmutableManagementResourceRegistration registration, final boolean ignoreMissing) {
            // make sure the target address does not contain the unresolved elements of the address
            final ModelNode remoteOp = operation.clone();
            final PathAddress fullAddress = base.append(remaining);
            remoteOp.get(OP_ADDR).set(fullAddress.toModelNode());
            // Temp remote result
            final ModelNode resultItem = new ModelNode();
            final OperationStepHandler proxyHandler = registration.getOperationHandler(PathAddress.EMPTY_ADDRESS, operation.require(OP).asString());
            context.addStep(resultItem, remoteOp, new OperationStepHandler() {

                @Override
                public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
                    try {
                        // Execute the proxy step handler in a separate step
                        // so we have the final response available to our ResultHandler
                        ControllerLogger.MGMT_OP_LOGGER.tracef("sending ModelAddressResolver request %s to remote process using %s", operation, proxyHandler);
                        final AtomicBoolean filtered = new AtomicBoolean(false);
                        context.addStep(new FilterableRemoteOperationStepHandler(proxyHandler, base, filtered, filteredData, ignoreMissing), OperationContext.Stage.MODEL, true);
                        context.completeStep(new OperationContext.ResultHandler() {

                            @Override
                            public void handleResult(OperationContext.ResultAction resultAction, OperationContext context, ModelNode operation) {
                                ControllerLogger.MGMT_OP_LOGGER.tracef("ModelAddressResolver response from remote process is %s", resultItem);
                                if (filtered.get()) {
                                    ControllerLogger.MGMT_OP_LOGGER.trace("Response was filtered");
                                    return;
                                }
                                // Determine the address prefix to prepend to RBAC responses from servers
                                PathAddress rbacPrefix = base.size() > 1 && base.getElement(1).getKey().equals(RUNNING_SERVER) ? base : PathAddress.EMPTY_ADDRESS;
                                // If there are multiple targets remaining, the result should be a list
                                if (remaining.isMultiTarget()) {
                                    if (resultItem.has(RESULT) && resultItem.get(RESULT).getType() == ModelType.LIST) {
                                        for (final ModelNode rr : resultItem.get(RESULT).asList()) {
                                            // Create a new result entry
                                            final ModelNode nr = result.add();
                                            final PathAddress address = PathAddress.pathAddress(rr.get(OP_ADDR));
                                            // Check whether the result of the remote target contains part of the base address
                                            // this might happen for hosts
                                            int max = Math.min(base.size(), address.size());
                                            int match = 0;
                                            for (int i = 0; i < max; i++) {
                                                final PathElement eb = base.getElement(i);
                                                final PathElement ea = address.getElement(i);
                                                if (eb.getKey().equals(ea.getKey())) {
                                                    match = i + 1;
                                                }
                                            }
                                            final PathAddress resolvedAddress = base.append(address.subAddress(match));
                                            ControllerLogger.MGMT_OP_LOGGER.tracef("recording multi-target ModelAddressResolver response " + "to %s at %s", fullAddress, resolvedAddress);
                                            nr.get(OP_ADDR).set(resolvedAddress.toModelNode());
                                            nr.get(OUTCOME).set(rr.get(OUTCOME));
                                            nr.get(RESULT).set(rr.get(RESULT));
                                            if (rr.hasDefined(RESPONSE_HEADERS)) {
                                                ModelNode headers = rr.get(RESPONSE_HEADERS);
                                                ModelNode acc = headers.remove(ACCESS_CONTROL);
                                                if (headers.asInt() > 0) {
                                                    nr.get(RESPONSE_HEADERS).set(headers);
                                                }
                                                if (acc != null && acc.isDefined()) {
                                                    filteredData.populate(acc, rbacPrefix);
                                                    ControllerLogger.MGMT_OP_LOGGER.tracef("Populated local filtered data " + "with remote access control headers %s from result item %s", acc, rr);
                                                }
                                            }
                                        }
                                        if (resultItem.hasDefined(RESPONSE_HEADERS, ACCESS_CONTROL)) {
                                            ModelNode acc = resultItem.get(RESPONSE_HEADERS, ACCESS_CONTROL);
                                            filteredData.populate(acc, PathAddress.EMPTY_ADDRESS);
                                        }
                                    }
                                } else {
                                    ControllerLogger.MGMT_OP_LOGGER.tracef("recording non-multi-target ModelAddressResolver response " + "to %s", fullAddress);
                                    final ModelNode nr = result.add();
                                    nr.get(OP_ADDR).set(fullAddress.toModelNode());
                                    nr.get(OUTCOME).set(resultItem.get(OUTCOME));
                                    nr.get(RESULT).set(resultItem.get(RESULT));
                                    if (resultItem.hasDefined(RESPONSE_HEADERS)) {
                                        ModelNode headers = resultItem.get(RESPONSE_HEADERS);
                                        ModelNode acc = headers.remove(ACCESS_CONTROL);
                                        if (headers.asInt() > 0) {
                                            nr.get(RESPONSE_HEADERS).set(headers);
                                        }
                                        if (acc != null && acc.isDefined()) {
                                            filteredData.populate(acc, PathAddress.EMPTY_ADDRESS);
                                            ControllerLogger.MGMT_OP_LOGGER.tracef("Populated local filtered data " + "with remote access control headers %s from result item %s", acc, resultItem);
                                        }
                                    }
                                }
                            }
                        });
                    } catch (Resource.NoSuchResourceException e) {
                        // just discard the result to avoid leaking the inaccessible address
                    }
                }
            }, OperationContext.Stage.MODEL, true);
        }
    }

    private static final class ModelAddressResolver extends AbstractAddressResolver {

        public ModelAddressResolver(ModelNode operation, ModelNode result, FilteredData filteredData, OperationStepHandler delegate, FilterPredicate predicate) {
            super(operation, result, delegate, filteredData, predicate);
        }

        protected void executeMultiTargetChildren(PathAddress base, PathElement currentElement, PathAddress newRemaining, OperationContext context, ImmutableManagementResourceRegistration registration, boolean ignoreMissing) {
            final Resource resource = context.readResource(base, false);
            final String childType = currentElement.getKey().equals("*") ? null : currentElement.getKey();
            if (registration.isRemote()) {
                // At least for proxies it should use the proxy operation handler
                throw new IllegalStateException();
            }
            // Get the available children
            final Map<String, Set<String>> resolved = getChildAddresses(context, base, registration, resource, childType);
            for (Map.Entry<String, Set<String>> entry : resolved.entrySet()) {
                final String key = entry.getKey();
                final Set<String> children = entry.getValue();
                if (children.isEmpty()) {
                    continue;
                }
                if (currentElement.isWildcard()) {
                    for (final String child : children) {
                        final PathElement e = PathElement.pathElement(key, child);
                        final PathAddress next = base.append(e);
                        // Either require the child or a remote target
                        final ImmutableManagementResourceRegistration nr = context.getResourceRegistration().getSubModel(next);
                        if (resource.hasChild(e) || (nr != null && nr.isRemote())) {
                            safeExecute(next, newRemaining, context, nr, true);
                        }
                    }
                } else {
                    String[] segments = currentElement.getSegments();
                    // If there's more than 1 segment, treat that like a wildcard, and don't
                    // fail on bits that disappear
                    boolean ignore = ignoreMissing || segments.length > 1;
                    for (final String segment : currentElement.getSegments()) {
                        if (children.contains(segment)) {
                            final PathElement e = PathElement.pathElement(key, segment);
                            final PathAddress next = base.append(e);
                            // Either require the child or a remote target
                            final ImmutableManagementResourceRegistration nr = context.getResourceRegistration().getSubModel(next);
                            if (resource.hasChild(e) || (nr != null && nr.isRemote())) {
                                safeExecute(next, newRemaining, context, nr, ignore);
                            }
                        }
                    }
                }
            }
        }

        @Override
        protected void executeSingleTargetChild(PathAddress base, PathElement currentElement, PathAddress newRemaining, OperationContext context, boolean ignoreMissing) {
            final PathAddress next = base.append(currentElement);
            // Either require the child or a remote target
            final Resource resource = context.readResource(base, false);
            final ImmutableManagementResourceRegistration nr = context.getResourceRegistration().getSubModel(next);
            if (resource.hasChild(currentElement) || (nr != null && nr.isRemote())) {
                safeExecute(next, newRemaining, context, nr, ignoreMissing);
            } else // if we are on the wrong host no need to do anything
            if (!resource.hasChild(currentElement)) {
                throw new Resource.NoSuchResourceException(currentElement);
            }
        }

        @Override
        protected boolean authorize(OperationContext context, PathAddress base, ModelNode operation) {
            try {
                context.readResource(base, false);
            } catch (UnknowRoleException ex) {
                context.getFailureDescription().set(ex.getMessage());
                return false;
            }
            // An exception will happen if not allowed
            return true;
        }
    }

    private static class FilterableRemoteOperationStepHandler implements OperationStepHandler {

        private final OperationStepHandler proxyHandler;

        private final PathAddress base;

        private final AtomicBoolean filtered;

        private final FilteredData filteredData;

        private final boolean ignoreMissing;

        public FilterableRemoteOperationStepHandler(OperationStepHandler proxyHandler, PathAddress base, AtomicBoolean filtered, FilteredData filteredData, boolean ignoreMissing) {
            this.proxyHandler = proxyHandler;
            this.base = base;
            this.filtered = filtered;
            this.filteredData = filteredData;
            this.ignoreMissing = ignoreMissing;
        }

        @Override
        public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
            try {
                proxyHandler.execute(context, operation);
                ControllerLogger.MGMT_OP_LOGGER.tracef("Preliminary result for %s is %s", operation, context.hasResult() ? context.getResult() : null);
            } catch (UnauthorizedException e) {
                // equivalent to the resource not existing
                // Just report the failure to the filter and complete normally
                filteredData.addReadRestrictedResource(base);
                filtered.set(true);
                ControllerLogger.MGMT_OP_LOGGER.tracef("Caught UnauthorizedException in remote execution from %s", proxyHandler);
            } catch (ResourceNotAddressableException e) {
                // Just report the failure to the filter and complete normally
                filteredData.addAccessRestrictedResource(base);
                filtered.set(true);
                ControllerLogger.MGMT_OP_LOGGER.tracef("Caught ResourceNotAddressableException in remote execution from %s", proxyHandler);
            } catch (Resource.NoSuchResourceException e) {
                // It's possible this is a remote failure, in which case we
                // don't get ResourceNotAddressableException. So see if
                // it was due to any authorization denial
                ModelNode toAuthorize = operation.clone();
                toAuthorize.get(OP).set(READ_RESOURCE_DESCRIPTION_OPERATION);
                toAuthorize.get(OP_ADDR).set(base.toModelNode());
                AuthorizationResult.Decision decision = context.authorize(toAuthorize, EnumSet.of(Action.ActionEffect.ADDRESS)).getDecision();
                ControllerLogger.MGMT_OP_LOGGER.tracef("Caught NoSuchResourceException in remote execution from %s. Authorization decision is %s", proxyHandler, decision);
                if (decision == AuthorizationResult.Decision.DENY) {
                    // Just report the failure to the filter and complete normally
                    filtered.set(true);
                    if (filteredData != null) {
                        filteredData.addAccessRestrictedResource(base);
                    }
                } else if (!ignoreMissing) {
                    throw e;
                }
            }
        }
    }

    private static class RegistrationAddressResolver extends AbstractAddressResolver {

        RegistrationAddressResolver(final ModelNode operation, final ModelNode result, final OperationStepHandler delegate) {
            super(operation, result, delegate, null, null);
        }

        @Override
        protected void executeMultiTargetChildren(PathAddress base, PathElement currentElement, PathAddress newRemaining, OperationContext context, ImmutableManagementResourceRegistration registration, boolean ignoreMissing) {
            final String childType = currentElement.getKey().equals("*") ? null : currentElement.getKey();
            if (registration.isRemote()) {
                // At least for proxies it should use the proxy operation handler
                throw new IllegalStateException();
            }
            final Set<PathElement> children = context.getResourceRegistration().getChildAddresses(base);
            if (children == null || children.isEmpty()) {
                throw new NoSuchResourceTypeException(base.append(currentElement));
            }
            boolean foundValid = false;
            PathAddress invalid = null;
            for (final PathElement path : children) {
                if (childType != null && !childType.equals(path.getKey())) {
                    continue;
                }
                final boolean isHostWildcardServerAddress = base.size() > 0 && base.getLastElement().getKey().equals(HOST) && path.getKey().equals(RUNNING_SERVER) && path.isWildcard();
                if (isHostWildcardServerAddress && newRemaining.size() > 0) {
                    // Trying to get e.g. /host=xxx/server=*/interface=public will fail, so make sure if there are remaining elements for
                    // a /host=master/server=* that we don't attempt to get those
                    continue;
                }
                final PathAddress next = base.append(path);
                final ImmutableManagementResourceRegistration nr = context.getResourceRegistration().getSubModel(next);
                try {
                    execute(next, newRemaining, context, nr, ignoreMissing);
                    foundValid = true;
                } catch (NoSuchResourceTypeException e) {
                    if (!foundValid) {
                        PathAddress failedAddr = e.getPathAddress();
                        // Store the failed address for error reporting, but only if
                        // 1) this is the first failure, or
                        // 2) The size of the failed address is larger than the currently
                        // cached one, indicating there is some path that has a larger number
                        // of valid elements than the currently cached path. So we want to
                        // report that larger path
                        if (invalid == null || failedAddr.size() > invalid.size()) {
                            PathAddress newBase = base.append(currentElement);
                            invalid = newBase.append(failedAddr.subAddress(newBase.size()));
                        }
                    }
                }
            }
            if (!foundValid) {
                if (invalid == null) {
                    // No children matched currentElement
                    invalid = base.append(currentElement);
                }
                throw new NoSuchResourceTypeException(invalid);
            }
        }

        @Override
        protected void executeSingleTargetChild(PathAddress base, PathElement currentElement, PathAddress newRemaining, OperationContext context, boolean ignoreMissing) {
            final PathAddress next = base.append(currentElement);
            final ImmutableManagementResourceRegistration nr = context.getResourceRegistration().getSubModel(next);
            if (nr != null) {
                execute(next, newRemaining, context, nr, ignoreMissing);
            } else {
                throw new NoSuchResourceTypeException(next);
            }
        }

        @Override
        protected boolean authorize(OperationContext context, PathAddress base, ModelNode operation) {
            if (base.size() > 0) {
                PathElement element = base.getLastElement();
                if (!element.isWildcard() && (element.getKey().equals(HOST))) {
                    // Only do this for host resources. The rest of r-r-d does filtering itself. However, without
                    // - a slave host scoped role doing a /host=*:r-r-d will not get rid of the host=master resource
                    // - a master host scoped role doing a /host=*/server=*/subsystem=thing:r-r-d will not get rid of the host=slave resource
                    ModelNode toAuthorize = operation.clone();
                    toAuthorize.get(OP).set(READ_RESOURCE_DESCRIPTION_OPERATION);
                    toAuthorize.get(OP_ADDR).set(base.toModelNode());
                    AuthorizationResult.Decision decision = context.authorize(toAuthorize, EnumSet.of(Action.ActionEffect.ADDRESS)).getDecision();
                    return decision == AuthorizationResult.Decision.PERMIT;
                }
            }
            return true;
        }
    }

    /**
     * WFCORE-573 Wraps a response to a call where the expected resource may
     * have disappeared with a flag to record that that has happened.
     */
    static class AvailableResponse {

        boolean unavailable;

        final ModelNode response;

        AvailableResponse(ModelNode response) {
            this.response = response;
        }
    }

    /**
     * WFCORE-573 Wrap a read-attribute call and handle disappearance of the target resource
     */
    static class AvailableResponseWrapper implements OperationStepHandler {

        private final OperationStepHandler wrapped;

        private final AvailableResponse availableResponse;

        AvailableResponseWrapper(OperationStepHandler wrapped, AvailableResponse availableResponse) {
            this.wrapped = wrapped;
            this.availableResponse = availableResponse;
        }

        @Override
        public void execute(OperationContext context, ModelNode operation) throws OperationFailedException {
            try {
                wrapped.execute(context, operation);
            } catch (Resource.NoSuchResourceException e) {
                availableResponse.unavailable = true;
            }
        }
    }

    /**
     * Gets the addresses of the child resources under the given resource.
     *
     * @param context        the operation context
     * @param registry       registry entry representing the resource
     * @param resource       the current resource
     * @param validChildType a single child type to which the results should be limited. If {@code null} the result
     *                       should include all child types
     * @return map where the keys are the child types and the values are a set of child names associated with a type
     */
    static Map<String, Set<String>> getChildAddresses(final OperationContext context, final PathAddress addr, final ImmutableManagementResourceRegistration registry, Resource resource, final String validChildType) {
        Map<String, Set<String>> result = new HashMap<>();
        Predicate<String> validChildTypeFilter = childType -> (validChildType == null) || validChildType.equals(childType);
        if (resource != null) {
            for (String childType : registry.getChildNames(PathAddress.EMPTY_ADDRESS)) {
                if (validChildTypeFilter.test(childType)) {
                    List<String> list = new ArrayList<>();
                    for (String child : resource.getChildrenNames(childType)) {
                        if (registry.getSubModel(PathAddress.pathAddress(PathElement.pathElement(childType, child))) != null) {
                            list.add(child);
                        }
                    }
                    result.put(childType, new LinkedHashSet<>(list));
                }
            }
        }
        Set<PathElement> paths = registry.getChildAddresses(PathAddress.EMPTY_ADDRESS);
        for (PathElement path : paths) {
            String childType = path.getKey();
            if (validChildTypeFilter.test(childType)) {
                Set<String> children = result.get(childType);
                if (children == null) {
                    // WFLY-3306 Ensure we have an entry for any valid child type
                    children = new LinkedHashSet<>();
                    result.put(childType, children);
                }
                ImmutableManagementResourceRegistration childRegistration = registry.getSubModel(PathAddress.pathAddress(path));
                if (childRegistration != null) {
                    AliasEntry aliasEntry = childRegistration.getAliasEntry();
                    if (aliasEntry != null) {
                        PathAddress childAddr = addr.append(path);
                        PathAddress target = aliasEntry.convertToTargetAddress(childAddr, AliasContext.create(childAddr, context));
                        assert !childAddr.equals(target) : "Alias was not translated";
                        PathAddress targetParent = target.getParent();
                        Resource parentResource = context.readResourceFromRoot(targetParent, false);
                        if (parentResource != null) {
                            PathElement targetElement = target.getLastElement();
                            if (targetElement.isWildcard()) {
                                children.addAll(parentResource.getChildrenNames(targetElement.getKey()));
                            } else if (parentResource.hasChild(targetElement)) {
                                children.add(path.getValue());
                            }
                        }
                    }
                    if (!path.isWildcard() && childRegistration.isRemote()) {
                        children.add(path.getValue());
                    }
                }
            }
        }
        return result;
    }

    static Locale getLocale(OperationContext context, final ModelNode operation) throws OperationFailedException {
        if (!operation.hasDefined(GlobalOperationAttributes.LOCALE.getName())) {
            return null;
        }
        String unparsed = normalizeLocale(operation.get(GlobalOperationAttributes.LOCALE.getName()).asString());
        try {
            return LocaleResolver.resolveLocale(unparsed);
        } catch (IllegalArgumentException e) {
            reportInvalidLocaleFormat(context, e.getMessage());
            return null;
        }
    }

    static boolean getRecursive(OperationContext context, ModelNode op) throws OperationFailedException {
        ModelNode recursiveNode = RECURSIVE.resolveModelAttribute(context, op);
        final int recursiveValue = recursiveNode.isDefined() ? (recursiveNode.asBoolean() ? 1 : 0) : -1;
        final int recursiveDepthValue = RECURSIVE_DEPTH.resolveModelAttribute(context, op).asInt(-1);
        // WFCORE-76: We are recursing in this round IFF:
        // Recursive is explicitly specified as TRUE and recursiveDepth is UNDEFINED
        // Recursive is either TRUE or UNDEFINED and recursiveDepth is >0
        return recursiveValue > 0 && recursiveDepthValue == -1 || recursiveValue != 0 && recursiveDepthValue > 0;
    }

    static void setNextRecursive(OperationContext context, ModelNode op, ModelNode nextOp) throws OperationFailedException {
        final int recursiveDepthValue = RECURSIVE_DEPTH.resolveModelAttribute(context, op).asInt(-1);
        // WFCORE-76: We are recursing in the next step IFF:
        // Recursive is explicitly specified as TRUE and recursiveDepth is UNDEFINED; or
        // Recursive is either TRUE or UNDEFINED and (recursiveDepth - 1) is >0
        // Recursive value carries through unchanged
        nextOp.get(RECURSIVE.getName()).set(op.get(RECURSIVE.getName()));

    }

    private static String normalizeLocale(String toNormalize) {
        return ("zh_Hans".equalsIgnoreCase(toNormalize) || "zh-Hans".equalsIgnoreCase(toNormalize)) ? "zh_CN" : toNormalize;
    }

    private static void reportInvalidLocaleFormat(OperationContext context, String format) {
        String msg = ControllerLogger.ROOT_LOGGER.invalidLocaleString(format);
        ControllerLogger.MGMT_OP_LOGGER.debug(msg);
        // TODO report the problem to client via out-of-band message.
        // Enable this in 7.2 or later when there is time to test
    }

    private static final class NoSuchResourceTypeException extends OperationFailedRuntimeException {

        private final PathAddress pathAddress;

        private NoSuchResourceTypeException(PathAddress pathAddress) {
            super(ControllerLogger.ROOT_LOGGER.noSuchResourceType(pathAddress));
            this.pathAddress = pathAddress;
        }

        private PathAddress getPathAddress() {
            return pathAddress;
        }
    }
}


switch(c) {
                case '&':
                    toWrite = _AMP_;
                    break;
                case '<':
                    toWrite = _LT_;
                    break;
                case '>':
                    toWrite = _GT_;
                    break;
                case 0xD:
                    toWrite = __XD_;
                    break;
                default:
                    writeCharToUtf8(c, writer);
                    continue;
            }

/*
 * reserved comment block
 */
package com.sun.org.apache.xml.internal.security.c14n.implementations;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import com.sun.org.apache.xml.internal.security.c14n.CanonicalizationException;
import com.sun.org.apache.xml.internal.security.c14n.CanonicalizerSpi;
import com.sun.org.apache.xml.internal.security.c14n.helper.AttrCompare;
import com.sun.org.apache.xml.internal.security.signature.NodeFilter;
import com.sun.org.apache.xml.internal.security.signature.XMLSignatureInput;
import com.sun.org.apache.xml.internal.security.utils.Constants;
import com.sun.org.apache.xml.internal.security.utils.UnsyncByteArrayOutputStream;
import com.sun.org.apache.xml.internal.security.utils.XMLUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.ProcessingInstruction;
import org.xml.sax.SAXException;

/**
 * Abstract base class for canonicalization algorithms.
 *
 * @author Christian Geuer-Pollmann <example@nnthink.com>
 */
public abstract class CanonicalizerBase extends CanonicalizerSpi {

    // Constants to be outputed, In char array form, so
    // less garbage is generate when outputed.
    private static final byte[] _END_PI = { '?', '>' };

    private static final byte[] _BEGIN_PI = { '<', '?' };

    private static final byte[] _END_COMM = { '-', '-', '>' };

    private static final byte[] _BEGIN_COMM = { '<', '!', '-', '-' };

    private static final byte[] __XA_ = { '&', '#', 'x', 'A', ';' };

    private static final byte[] __X9_ = { '&', '#', 'x', '9', ';' };

    private static final byte[] _QUOT_ = { '&', 'q', 'u', 'o', 't', ';' };

    private static final byte[] __XD_ = { '&', '#', 'x', 'D', ';' };

    private static final byte[] _GT_ = { '&', 'g', 't', ';' };

    private static final byte[] _LT_ = { '&', 'l', 't', ';' };

    private static final byte[] _END_TAG = { '<', '/' };

    private static final byte[] _AMP_ = { '&', 'a', 'm', 'p', ';' };

    final static AttrCompare COMPARE = new AttrCompare();

    final static String XML = "xml";

    final static String XMLNS = "xmlns";

    final static byte[] equalsStr = { '=', '\"' };

    static final int NODE_BEFORE_DOCUMENT_ELEMENT = -1;

    static final int NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT = 0;

    static final int NODE_AFTER_DOCUMENT_ELEMENT = 1;

    // The null xmlns definiton.
    protected static final Attr nullNode;

    static {
        try {
            nullNode = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument().createAttributeNS(Constants.NamespaceSpecNS, XMLNS);
            nullNode.setValue("");
        } catch (Exception e) {
            throw new RuntimeException("Unable to create nullNode" + e);
        }
    }

    List nodeFilter;

    boolean _includeComments;

    Set _xpathNodeSet = null;

    /**
     * The node to be skiped/excluded from the DOM tree
     * in subtree canonicalizations.
     */
    Node _excludeNode = null;

    OutputStream _writer = new UnsyncByteArrayOutputStream();

    /**
     * Constructor CanonicalizerBase
     *
     * @param includeComments
     */
    public CanonicalizerBase(boolean includeComments) {
        this._includeComments = includeComments;
    }

    /**
     * Method engineCanonicalizeSubTree
     * @inheritDoc
     * @param rootNode
     * @throws CanonicalizationException
     */
    public byte[] engineCanonicalizeSubTree(Node rootNode) throws CanonicalizationException {
        return engineCanonicalizeSubTree(rootNode, (Node) null);
    }

    /**
     * Canonicalizes a Subtree node.
     * @param input the root of the subtree to canicalize
     * @return The canonicalize stream.
     * @throws CanonicalizationException
     */
    public byte[] engineCanonicalize(XMLSignatureInput input) throws CanonicalizationException {
        try {
            if (input.isExcludeComments())
                _includeComments = false;
            byte[] bytes;
            if (input.isOctetStream()) {
                return engineCanonicalize(input.getBytes());
            }
            if (input.isElement()) {
                bytes = engineCanonicalizeSubTree(input.getSubNode(), input.getExcludeNode());
                return bytes;
            } else if (input.isNodeSet()) {
                nodeFilter = input.getNodeFilters();
                Document doc = null;
                if (input.getSubNode() != null) {
                    doc = XMLUtils.getOwnerDocument(input.getSubNode());
                } else {
                    doc = XMLUtils.getOwnerDocument(input.getNodeSet());
                }
                if (input.isNeedsToBeExpanded()) {
                    XMLUtils.circumventBug2650(doc);
                }
                if (input.getSubNode() != null) {
                    bytes = engineCanonicalizeXPathNodeSetInternal(input.getSubNode());
                } else {
                    bytes = engineCanonicalizeXPathNodeSet(input.getNodeSet());
                }
                return bytes;
            }
            return null;
        } catch (CanonicalizationException ex) {
            throw new CanonicalizationException("empty", ex);
        } catch (ParserConfigurationException ex) {
            throw new CanonicalizationException("empty", ex);
        } catch (IOException ex) {
            throw new CanonicalizationException("empty", ex);
        } catch (SAXException ex) {
            throw new CanonicalizationException("empty", ex);
        }
    }

    /**
     * Canonicalizes a Subtree node.
     *
     * @param rootNode
     *            the root of the subtree to canicalize
     * @param excludeNode
     *            a node to be excluded from the canicalize operation
     * @return The canonicalize stream.
     * @throws CanonicalizationException
     */
    byte[] engineCanonicalizeSubTree(Node rootNode, Node excludeNode) throws CanonicalizationException {
        this._excludeNode = excludeNode;
        try {
            NameSpaceSymbTable ns = new NameSpaceSymbTable();
            if (rootNode instanceof Element) {
                // Fills the nssymbtable with the definitions of the parent of the root subnode
                getParentNameSpaces((Element) rootNode, ns);
            }
            this.canonicalizeSubTree(rootNode, ns, rootNode);
            this._writer.close();
            if (this._writer instanceof ByteArrayOutputStream) {
                byte[] result = ((ByteArrayOutputStream) this._writer).toByteArray();
                if (reset) {
                    ((ByteArrayOutputStream) this._writer).reset();
                }
                return result;
            }
            return null;
        } catch (UnsupportedEncodingException ex) {
            throw new CanonicalizationException("empty", ex);
        } catch (IOException ex) {
            throw new CanonicalizationException("empty", ex);
        }
    }

    /**
     * Method canonicalizeSubTree, this function is a recursive one.
     *
     * @param currentNode
     * @param ns
     * @param endnode
     * @throws CanonicalizationException
     * @throws IOException
     */
    final void canonicalizeSubTree(Node currentNode, NameSpaceSymbTable ns, Node endnode) throws CanonicalizationException, IOException {
        Node sibling = null;
        Node parentNode = null;
        final OutputStream writer = this._writer;
        final Node excludeNode = this._excludeNode;
        final boolean includeComments = this._includeComments;
        do {
            switch(currentNode.getNodeType()) {
                case Node.DOCUMENT_TYPE_NODE:
                default:
                    break;
                case Node.ENTITY_NODE:
                case Node.NOTATION_NODE:
                case Node.ATTRIBUTE_NODE:
                    // illegal node type during traversal
                    throw new CanonicalizationException("empty");
                case Node.DOCUMENT_FRAGMENT_NODE:
                case Node.DOCUMENT_NODE:
                    ns.outputNodePush();
                    sibling = currentNode.getFirstChild();
                    break;
                case Node.COMMENT_NODE:
                    if (includeComments) {
                        outputCommentToWriter((Comment) currentNode, writer);
                    }
                    break;
                case Node.PROCESSING_INSTRUCTION_NODE:
                    outputPItoWriter((ProcessingInstruction) currentNode, writer);
                    break;
                case Node.TEXT_NODE:
                case Node.CDATA_SECTION_NODE:
                    outputTextToWriter(currentNode.getNodeValue(), writer);
                    break;
                case Node.ELEMENT_NODE:
                    if (currentNode == excludeNode) {
                        break;
                    }
                    Element currentElement = (Element) currentNode;
                    // Add a level to the nssymbtable. So latter can be pop-back.
                    ns.outputNodePush();
                    writer.write('<');
                    String name = currentElement.getTagName();
                    writeStringToUtf8(name, writer);
                    Iterator attrs = this.handleAttributesSubtree(currentElement, ns);
                    if (attrs != null) {
                        // we output all Attrs which are available
                        while (attrs.hasNext()) {
                            Attr attr = (Attr) attrs.next();
                            outputAttrToWriter(attr.getNodeName(), attr.getNodeValue(), writer);
                        }
                    }
                    writer.write('>');
                    sibling = currentNode.getFirstChild();
                    if (sibling == null) {
                        writer.write(_END_TAG);
                        writeStringToUtf8(name, writer);
                        writer.write('>');
                        // We fineshed with this level, pop to the previous definitions.
                        ns.outputNodePop();
                        if (parentNode != null) {
                            sibling = currentNode.getNextSibling();
                        }
                    } else {
                        parentNode = currentElement;
                    }
                    break;
            }
            while (sibling == null && parentNode != null) {
                writer.write(_END_TAG);
                writeStringToUtf8(((Element) parentNode).getTagName(), writer);
                writer.write('>');
                // We fineshed with this level, pop to the previous definitions.
                ns.outputNodePop();
                if (parentNode == endnode)
                    return;
                sibling = parentNode.getNextSibling();
                parentNode = parentNode.getParentNode();
                if (!(parentNode instanceof Element)) {
                    parentNode = null;
                }
            }
            if (sibling == null)
                return;
            currentNode = sibling;
            sibling = currentNode.getNextSibling();
        } while (true);
    }

    /**
     * Checks whether a Comment or ProcessingInstruction is before or after the
     * document element. This is needed for prepending or appending "\n"s.
     *
     * @param currentNode comment or pi to check
     * @return NODE_BEFORE_DOCUMENT_ELEMENT, NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT or NODE_AFTER_DOCUMENT_ELEMENT
     * @see #NODE_BEFORE_DOCUMENT_ELEMENT
     * @see #NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT
     * @see #NODE_AFTER_DOCUMENT_ELEMENT
     */
    final static int getPositionRelativeToDocumentElement(Node currentNode) {
        if ((currentNode == null) || (currentNode.getParentNode().getNodeType() != Node.DOCUMENT_NODE)) {
            return CanonicalizerBase.NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
        }
        Element documentElement = currentNode.getOwnerDocument().getDocumentElement();
        if ((documentElement == null) || (documentElement == currentNode)) {
            return CanonicalizerBase.NODE_NOT_BEFORE_OR_AFTER_DOCUMENT_ELEMENT;
        }
        for (Node x = currentNode; x != null; x = x.getNextSibling()) {
            if (x == documentElement) {
                return CanonicalizerBase.NODE_BEFORE_DOCUMENT_ELEMENT;
            }
        }
        return CanonicalizerBase.NODE_AFTER_DOCUMENT_ELEMENT;
    }

    /**
     * Method engineCanonicalizeXPathNodeSet
     * @inheritDoc
     * @param xpathNodeSet
     * @throws CanonicalizationException
     */
    public byte[] engineCanonicalizeXPathNodeSet(Set xpathNodeSet) throws CanonicalizationException {
        this._xpathNodeSet = xpathNodeSet;
        return engineCanonicalizeXPathNodeSetInternal(XMLUtils.getOwnerDocument(this._xpathNodeSet));
    }

    private byte[] engineCanonicalizeXPathNodeSetInternal(Node doc) throws CanonicalizationException {
        try {
            this.canonicalizeXPathNodeSet(doc, doc);
            this._writer.close();
            if (this._writer instanceof ByteArrayOutputStream) {
                byte[] sol = ((ByteArrayOutputStream) this._writer).toByteArray();
                if (reset) {
                    ((ByteArrayOutputStream) this._writer).reset();
                }
                return sol;
            }
            return null;
        } catch (UnsupportedEncodingException ex) {
            throw new CanonicalizationException("empty", ex);
        } catch (IOException ex) {
            throw new CanonicalizationException("empty", ex);
        }
    }

    /**
     * Canoicalizes all the nodes included in the currentNode and contained in the
     * _xpathNodeSet field.
     *
     * @param currentNode
     * @param endnode
     * @throws CanonicalizationException
     * @throws IOException
     */
    final void canonicalizeXPathNodeSet(Node currentNode, Node endnode) throws CanonicalizationException, IOException {
        boolean currentNodeIsVisible = false;
        NameSpaceSymbTable ns = new NameSpaceSymbTable();
        Node sibling = null;
        Node parentNode = null;
        OutputStream writer = this._writer;
        do {
            switch(currentNode.getNodeType()) {
                case Node.DOCUMENT_TYPE_NODE:
                default:
                    break;
                case Node.ENTITY_NODE:
                case Node.NOTATION_NODE:
                case Node.ATTRIBUTE_NODE:
                    // illegal node type during traversal
                    throw new CanonicalizationException("empty");
                case Node.DOCUMENT_FRAGMENT_NODE:
                case Node.DOCUMENT_NODE:
                    ns.outputNodePush();
                    sibling = currentNode.getFirstChild();
                    break;
                case Node.COMMENT_NODE:
                    if (this._includeComments && isVisible(currentNode)) {
                        outputCommentToWriter((Comment) currentNode, writer);
                    }
                    break;
                case Node.PROCESSING_INSTRUCTION_NODE:
                    if (isVisible(currentNode))
                        outputPItoWriter((ProcessingInstruction) currentNode, writer);
                    break;
                case Node.TEXT_NODE:
                case Node.CDATA_SECTION_NODE:
                    if (isVisible(currentNode)) {
                        outputTextToWriter(currentNode.getNodeValue(), writer);
                        for (Node nextSibling = currentNode.getNextSibling(); (nextSibling != null) && ((nextSibling.getNodeType() == Node.TEXT_NODE) || (nextSibling.getNodeType() == Node.CDATA_SECTION_NODE)); nextSibling = nextSibling.getNextSibling()) {
                            /* The XPath data model allows to select only the first of a
                * sequence of mixed text and CDATA nodes. But we must output
                * them all, so we must search:
                *
                * @see http://example.com
                */
                            outputTextToWriter(nextSibling.getNodeValue(), writer);
                            currentNode = nextSibling;
                            sibling = currentNode.getNextSibling();
                        }
                    }
                    break;
                case Node.ELEMENT_NODE:
                    Element currentElement = (Element) currentNode;
                    // Add a level to the nssymbtable. So latter can be pop-back.
                    String name = null;
                    currentNodeIsVisible = isVisible(currentNode);
                    if (currentNodeIsVisible) {
                        ns.outputNodePush();
                        writer.write('<');
                        name = currentElement.getTagName();
                        writeStringToUtf8(name, writer);
                    } else {
                        ns.push();
                    }
                    Iterator attrs = handleAttributes(currentElement, ns);
                    if (attrs != null) {
                        // we output all Attrs which are available
                        while (attrs.hasNext()) {
                            Attr attr = (Attr) attrs.next();
                            outputAttrToWriter(attr.getNodeName(), attr.getNodeValue(), writer);
                        }
                    }
                    if (currentNodeIsVisible) {
                        writer.write('>');
                    }
                    sibling = currentNode.getFirstChild();
                    if (sibling == null) {
                        if (currentNodeIsVisible) {
                            writer.write(_END_TAG);
                            writeStringToUtf8(name, writer);
                            writer.write('>');
                            // We fineshed with this level, pop to the previous definitions.
                            ns.outputNodePop();
                        } else {
                            ns.pop();
                        }
                        if (parentNode != null) {
                            sibling = currentNode.getNextSibling();
                        }
                    } else {
                        parentNode = currentElement;
                    }
                    break;
            }
            while (sibling == null && parentNode != null) {
                if (isVisible(parentNode)) {
                    writer.write(_END_TAG);
                    writeStringToUtf8(((Element) parentNode).getTagName(), writer);
                    writer.write('>');
                    // We fineshed with this level, pop to the previous definitions.
                    ns.outputNodePop();
                } else {
                    ns.pop();
                }
                if (parentNode == endnode)
                    return;
                sibling = parentNode.getNextSibling();
                parentNode = parentNode.getParentNode();
                if (!(parentNode instanceof Element)) {
                    parentNode = null;
                }
            }
            if (sibling == null)
                return;
            currentNode = sibling;
            sibling = currentNode.getNextSibling();
        } while (true);
    }

    boolean isVisible(Node currentNode) {
        if (nodeFilter != null) {
            Iterator it = nodeFilter.iterator();
            while (it.hasNext()) {
                if (!((NodeFilter) it.next()).isNodeInclude(currentNode))
                    return false;
            }
        }
        if ((this._xpathNodeSet != null) && !this._xpathNodeSet.contains(currentNode))
            return false;
        return true;
    }

    /**
     * Adds to ns the definitons from the parent elements of el
     * @param el
     * @param ns
     */
    final static void getParentNameSpaces(Element el, NameSpaceSymbTable ns) {
        List parents = new ArrayList();
        Node n1 = el.getParentNode();
        if (!(n1 instanceof Element)) {
            return;
        }
        // Obtain all the parents of the elemnt
        Element parent = (Element) el.getParentNode();
        while (parent != null) {
            parents.add(parent);
            Node n = parent.getParentNode();
            if (!(n instanceof Element)) {
                break;
            }
            parent = (Element) n;
        }
        // Visit them in reverse order.
        ListIterator it = parents.listIterator(parents.size());
        while (it.hasPrevious()) {
            Element ele = (Element) it.previous();
            if (!ele.hasAttributes()) {
                continue;
            }
            NamedNodeMap attrs = ele.getAttributes();
            int attrsLength = attrs.getLength();
            for (int i = 0; i < attrsLength; i++) {
                Attr N = (Attr) attrs.item(i);
                if (!Constants.NamespaceSpecNS.equals(N.getNamespaceURI())) {
                    // Not a namespace definition, ignore.
                    continue;
                }
                String NName = N.getLocalName();
                String NValue = N.getNodeValue();
                if (XML.equals(NName) && Constants.XML_LANG_SPACE_SpecNS.equals(NValue)) {
                    continue;
                }
                ns.addMapping(NName, NValue, N);
            }
        }
        Attr nsprefix;
        if (((nsprefix = ns.getMappingWithoutRendered("xmlns")) != null) && "".equals(nsprefix.getValue())) {
            ns.addMappingAndRender("xmlns", "", nullNode);
        }
    }

    /**
     * Outputs an Attribute to the internal Writer.
     *
     * The string value of the node is modified by replacing
     * <UL>
     * <LI>all ampersands (&) with <CODE>&amp;</CODE></LI>
     * <LI>all open angle brackets (<) with <CODE>&lt;</CODE></LI>
     * <LI>all quotation mark characters with <CODE>&quot;</CODE></LI>
     * <LI>and the whitespace characters <CODE>#x9</CODE>, #xA, and #xD, with character
     * references. The character references are written in uppercase
     * hexadecimal with no leading zeroes (for example, <CODE>#xD</CODE> is represented
     * by the character reference <CODE>&#xD;</CODE>)</LI>
     * </UL>
     *
     * @param name
     * @param value
     * @param writer
     * @throws IOException
     */
    static final void outputAttrToWriter(final String name, final String value, final OutputStream writer) throws IOException {
        writer.write(' ');
        writeStringToUtf8(name, writer);
        writer.write(equalsStr);
        byte[] toWrite;
        final int length = value.length();
        for (int i = 0; i < length; i++) {
            char c = value.charAt(i);
            switch(c) {
                case '&':
                    toWrite = _AMP_;
                    break;
                case '<':
                    toWrite = _LT_;
                    break;
                case '"':
                    toWrite = _QUOT_;
                    break;
                case 0x09:
                    toWrite = __X9_;
                    break;
                case 0x0A:
                    toWrite = __XA_;
                    break;
                case 0x0D:
                    toWrite = __XD_;
                    break;
                default:
                    writeCharToUtf8(c, writer);
                    continue;
            }
            writer.write(toWrite);
        }
        writer.write('\"');
    }

    final static void writeCharToUtf8(final char c, final OutputStream out) throws IOException {
        char ch;
        if ((c <= 0x007F)) {
            out.write(c);
            return;
        }
        int bias;
        int write;
        if (c > 0x07FF) {
            ch = (char) (c >>> 12);
            write = 0xE0;
            if (ch > 0) {
                write |= (ch & 0x0F);
            }
            out.write(write);
            write = 0x80;
            bias = 0x3F;
        } else {
            write = 0xC0;
            bias = 0x1F;
        }
        ch = (char) (c >>> 6);
        if (ch > 0) {
            write |= (ch & bias);
        }
        out.write(write);
        out.write(0x80 | ((c) & 0x3F));
    }

    final static void writeStringToUtf8(final String str, final OutputStream out) throws IOException {
        final int length = str.length();
        int i = 0;
        char c;
        while (i < length) {
            c = str.charAt(i++);
            if ((c <= 0x007F)) {
                out.write(c);
                continue;
            }
            char ch;
            int bias;
            int write;
            if (c > 0x07FF) {
                ch = (char) (c >>> 12);
                write = 0xE0;
                if (ch > 0) {
                    write |= (ch & 0x0F);
                }
                out.write(write);
                write = 0x80;
                bias = 0x3F;
            } else {
                write = 0xC0;
                bias = 0x1F;
            }
            ch = (char) (c >>> 6);
            if (ch > 0) {
                write |= (ch & bias);
            }
            out.write(write);
            out.write(0x80 | ((c) & 0x3F));
            continue;
        }
    }

    /**
     * Outputs a PI to the internal Writer.
     *
     * @param currentPI
     * @param writer where to write the things
     * @throws IOException
     */
    static final void outputPItoWriter(ProcessingInstruction currentPI, OutputStream writer) throws IOException {
        final int position = getPositionRelativeToDocumentElement(currentPI);
        if (position == NODE_AFTER_DOCUMENT_ELEMENT) {
            writer.write('\n');
        }
        writer.write(_BEGIN_PI);
        final String target = currentPI.getTarget();
        int length = target.length();
        for (int i = 0; i < length; i++) {
            char c = target.charAt(i);
            if (c == 0x0D) {
                writer.write(__XD_);
            } else {
                writeCharToUtf8(c, writer);
            }
        }
        final String data = currentPI.getData();
        length = data.length();
        if (length > 0) {
            writer.write(' ');
            for (int i = 0; i < length; i++) {
                char c = data.charAt(i);
                if (c == 0x0D) {
                    writer.write(__XD_);
                } else {
                    writeCharToUtf8(c, writer);
                }
            }
        }
        writer.write(_END_PI);
        if (position == NODE_BEFORE_DOCUMENT_ELEMENT) {
            writer.write('\n');
        }
    }

    /**
     * Method outputCommentToWriter
     *
     * @param currentComment
     * @param writer writer where to write the things
     * @throws IOException
     */
    static final void outputCommentToWriter(Comment currentComment, OutputStream writer) throws IOException {
        final int position = getPositionRelativeToDocumentElement(currentComment);
        if (position == NODE_AFTER_DOCUMENT_ELEMENT) {
            writer.write('\n');
        }
        writer.write(_BEGIN_COMM);
        final String data = currentComment.getData();
        final int length = data.length();
        for (int i = 0; i < length; i++) {
            char c = data.charAt(i);
            if (c == 0x0D) {
                writer.write(__XD_);
            } else {
                writeCharToUtf8(c, writer);
            }
        }
        writer.write(_END_COMM);
        if (position == NODE_BEFORE_DOCUMENT_ELEMENT) {
            writer.write('\n');
        }
    }

    /**
     * Outputs a Text of CDATA section to the internal Writer.
     *
     * @param text
     * @param writer writer where to write the things
     * @throws IOException
     */
    static final void outputTextToWriter(final String text, final OutputStream writer) throws IOException {
        final int length = text.length();
        byte[] toWrite;
        for (int i = 0; i < length; i++) {
            char c = text.charAt(i);

            writer.write(toWrite);
        }
    }

    /**
     * Obtain the attributes to output for this node in XPathNodeSet c14n.
     *
     * @param E
     * @param ns
     * @return the attributes nodes to output.
     * @throws CanonicalizationException
     */
    abstract Iterator handleAttributes(Element E, NameSpaceSymbTable ns) throws CanonicalizationException;

    /**
     * Obtain the attributes to output for this node in a Subtree c14n.
     *
     * @param E
     * @param ns
     * @return the attributes nodes to output.
     * @throws CanonicalizationException
     */
    abstract Iterator handleAttributesSubtree(Element E, NameSpaceSymbTable ns) throws CanonicalizationException;

    /**
     * @param _writer The _writer to set.
     */
    public void setWriter(OutputStream _writer) {
        this._writer = _writer;
    }
}