All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.glassfish.flashlight.datatree.impl.AbstractTreeNode Maven / Gradle / Ivy

There is a newer version: 7.2024.1.Alpha1
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2009-2013 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
// Portions Copyright [2018] Payara Foundation and/or affiliates

package org.glassfish.flashlight.datatree.impl;

import static com.sun.enterprise.util.StringUtils.ok;
import com.sun.enterprise.util.ObjectAnalyzer;
import static com.sun.enterprise.util.SystemPropertyConstants.MONDOT;
import static com.sun.enterprise.util.SystemPropertyConstants.SLASH;
import org.glassfish.flashlight.datatree.TreeNode;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * @author Harpreet Singh
 * @author Byron Nevins
 * 12/18/2010 -- Added encode/decode.  Note that the encoded form for a dot is
 * NOT something like "\\." -- there is too much code around making assumptions
 * about dots, splitting strings, etc.  So we replace with ___MONDOT___
 */
public abstract class AbstractTreeNode implements TreeNode, Comparable {
    protected Map children = new ConcurrentHashMap();
    // bnevins 6/25/2011  -- why is normalizedChildren static ?!?
    private static Map normalizedChildren = new ConcurrentHashMap();
    protected String name;    // The node object itself
    protected String category;
    protected String description;
    protected boolean enabled = false;
    private static final String NAME_SEPARATOR = ".";
    private static final String REGEX = "(? getChildNodes() {
        return children.values();
    }

    /**
     * Returns a mutable view of the children
     * @return
     */
    @Override
    public Collection getEnabledChildNodes() {
        List childNodes = new ArrayList();
        for (TreeNode child : children.values()) {
            if (child.isEnabled())
                childNodes.add(child);
        }
        return childNodes;
    }

    public Enumeration getChildNodesImmutable() {
        return ((ConcurrentHashMap) children).elements();
    }

    @Override
    public boolean hasChildNodes() {
        return !children.isEmpty();

    }

    @Override
    public void removeChild(TreeNode oldChild) {
        String child = oldChild.getName();
        if (child != null) {
            children.remove(child);
        }

        // too fragile to hunt for the matching key...
        Iterator it = normalizedChildren.values().iterator();
        while (it.hasNext()) {
            if (it.next() == oldChild) {
                it.remove();
                break;
            }
        }
    }

    @Override
    public String getCategory() {
        return category;
    }

    @Override
    public void setCategory(String category) {
        this.category = category;
    }

    @Override
    public String getDescription() {
        return this.description;
    }

    @Override
    public void setDescription(String description) {
        this.description = description;
    }

    // Byron Nevins 6/25/11
    // JIRA 15964 -- what happened was that the childName was "xxx.yyy"
    // there was a node that actually matched but it was not found because
    // its name was "xxx\.yyy"
    @Override
    public TreeNode getChild(String childName) {
        if (childName == null)
            return null;

        childName = normalizeDots(childName);
        Set> entries = children.entrySet();

        for (Map.Entry entry : entries) {
            String entryKey = entry.getKey();
            String normalizedEntryKey = normalizeDots(entryKey);

            if (childName.equals(entryKey) || childName.equals(normalizedEntryKey))
                return entry.getValue();
        }

        return null;
    }

    @Override
    public TreeNode getNode(String completeName) {
        if (completeName == null) {
            return null;
        }
        completeName = encodePath(completeName);
        Pattern pattern = Pattern.compile(AbstractTreeNode.REGEX);
        String[] tokens = pattern.split(completeName);
        TreeNode n = findNodeInTree(tokens);

        if (n == null)
            n = findNodeInTreeNormalized(completeName);

        return n;
    }

    // confused?  That's expected!  This should be refactored/re-done for 3.2
    // we store dots and slashes encoded.  THe tokens coming in to this method
    // are encoded.  That's because there is lots of other code scattered around
    // that looks for these special meta-characters.  To be safe I'm storing them
    // in the node encoded.
    // But the "children" object has keys that come from the getName() of the node
    // which is the value.
    private TreeNode findNodeInTree(String[] tokens) {
        if (tokens == null) {
            return null;
        }
        TreeNode child = getChild(tokens[0]);

        if (child == null)
            child = getChild(decodeName(tokens[0]));

        if (child == null)
            return null;

        if (tokens.length > 1)
            child = ((AbstractTreeNode) child).findNodeInTree(dropFirstStringToken(tokens));

        return child;

    }

    private String[] dropFirstStringToken(String[] token) {
        if (token.length == 0 || token.length == 1) {
            return null;
        }
        String[] newToken = new String[token.length - 1];
        for (int i = 0; i < newToken.length; i++) {
            newToken[i] = token[i + 1];
        }
        return newToken;
    }

    /**
     * Returns all the nodes under the current tree
     * @return List of all nodes in the current tree
     */
    @Override
    public List traverse(boolean ignoreDisabled) {
        List list = new ArrayList();

        if (ignoreDisabled && !this.enabled) {
                return list;
        }
        list.add(this);

        if (!hasChildNodes()) {
            return list;
        }

        Collection childList = children.values();
        for (TreeNode node : childList) {
            list.addAll(node.traverse(ignoreDisabled));
        }
        return list;
    }

    @Override
    public List getNodes(String pattern, boolean ignoreDisabled, boolean gfv2Compatible) {
        pattern = pattern.replace("\\.", "\\\\\\."); // \.  goes to \\\.

        // bnevins October 2010
        // design gotcha -- It used to be IMPOSSIBLE to tell the difference between
        // a literal slash in a name and a slash used as a delimiter.  Deep down
        // under dozens of calls in the stack -- Strings are concatanated together
        // Simple solution is to replace literal slashes with a token.  The probe
        // provider code needs to do that.  jndi names are an example of this.
        // Here we replace slash in the given pattern with the token to pull out
        // the right stuff.
        // This is a ARCHITECTURE flaw.  This hack can be replaced with an
        // ARCHITECTURAL fix later if desired.

        List list = getNodesInternal(pattern, ignoreDisabled, gfv2Compatible);

        if (list.isEmpty()) {
            list = getNodesInternal(pattern.replace("/", SLASH), ignoreDisabled, gfv2Compatible);
        }
        if (list.isEmpty()){
            list = getNodesInternal(decodeNameToDots(pattern), ignoreDisabled, gfv2Compatible);
        }
        return list;
    }

    private List getNodesInternal(String pattern, boolean ignoreDisabled, boolean gfv2Compatible) {
        List regexMatchedTree = new ArrayList();


        try {
            if (gfv2Compatible)
                pattern = convertGFv2PatternToRegex(pattern);

            Pattern mPattern = Pattern.compile(pattern);
            List completeTree = traverse(ignoreDisabled);

            for (TreeNode node : completeTree) {
                String path = node.getCompletePathName();
                String path2 = null;

                if (path.contains("\\")){
                    path2 = path.replace("\\", "");
                }
                Matcher matcher = mPattern.matcher(path);

                if (matcher.matches()) {
                    regexMatchedTree.add(node);
                } else if (path2 != null) {
                    Matcher matcher2 = mPattern.matcher(path2);
                    if (matcher2.matches()) {
                        regexMatchedTree.add(node);
                    }
                }
            }
        } catch (java.util.regex.PatternSyntaxException e) {
            // log this
            Logger.getLogger("FLASHLIGHT-TREENODE").log(Level.FINEST, "Unable to process Regex", e);
        }
        return regexMatchedTree;
    }

    @Override
    public List getNodes(String pattern) {
        return getNodes(pattern, true, true);
    }

    private String convertGFv2PatternToRegex(String pattern) {
        if (pattern.equals(STAR)) {
            return ".*";
        }
        // Doing this intermediate step as replacing "*" in a pattern with ".*"
        // is too hassling

        String modifiedPattern = pattern.replaceAll("\\*", ":");
        return modifiedPattern.replaceAll(":", ".*");
    }

    @Override
    public int compareTo(TreeNode other) {
        return getName().compareTo(other.getName());
    }

    @Override
    public boolean equals(Object o) {
        if(this == o)
            return true;

        // don't waste time checking o for null
        // if instanceof's first param is null it will return false
        // see JLS, 15.19.2
        if(!(o instanceof AbstractTreeNode))
            return false;

        // guaranteed to not throw an Exception because of above!
        AbstractTreeNode other = (AbstractTreeNode) o;

        // work hard to avoid NPE's !!
        return (name == null ? other.name == null : name.equals(other.name))
                && (category == null ? other.category == null : category.equals(other.category))
                && (description == null ? other.description == null : description.equals(other.description));
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 23 * hash + (this.name != null ? this.name.hashCode() : 0);
        hash = 23 * hash + (this.category != null ? this.category.hashCode() : 0);
        hash = 23 * hash + (this.description != null ? this.description.hashCode() : 0);
        return hash;
    }

    @Override
    public TreeNode getPossibleParentNode(String pattern) {
        // simplify by bailing out early if preconditions are not met...
        if (pattern == null || pattern.length() <= 0 || pattern.indexOf('*') >= 0)
            return null;

        TreeNode node = null;
        int longest = 0;

        for (TreeNode n : traverse(true)) {
            String aname = n.getCompletePathName();

            if (aname == null)
                continue;   // defensive pgming

            // JIRA 15500 -- there may be a backslash in the name!

            if (pattern.startsWith(aname) || pattern.startsWith(aname.replace("\\", ""))) {
                int thisLength = aname.length();

                // keep the longest match ONLY!
                if (node == null || thisLength > longest) {
                    node = n;
                    longest = thisLength;
                }
            }
        }

        return node;
    }

    private String encodeNodeName(String nodeName) {
        // The order is important!!
        return nodeName.replace("\\.", MONDOT).replace(".", MONDOT).replace("\\/", SLASH).replace("/", SLASH);
    }

    private String encodePath(String thePath) {
        // REST encodes (1) to (2)
        //  aaa    bbb.x   cccc
        //  aaa.bbb\\x.cccc
        // we want aaa.bbb___MONDOT___x.cccc

        return thePath.replace("\\/", SLASH).replace("\\.", MONDOT);
    }

    // todo replace with \\. ???
    private String decodeName() {
        return decodeName(name);
    }

    private static String decodeName(String s) {
        return s.replace(SLASH, "/").replace(MONDOT, "\\.");
    }

    private static String decodeNameToDots(String s) {
        return s.replace(SLASH, ".").replace(MONDOT, ".").replace("\\/", ".").replace("\\.", ".").replace('/', '.');
    }

    private TreeNode findNodeInTreeNormalized(String desiredName) {
        // this is ONLY called when there is no match using the tools prior to 2/10/11
        // so the performance hit should be reasonable

        if (!ok(desiredName))
            return null;

        desiredName = decodeNameToDots(desiredName);
        TreeNode node = normalizedChildren.get(desiredName);

        // one more try.  GUI sometimes chops off the starting "server".
        if (node == null && !desiredName.startsWith("server.")) {
            node = normalizedChildren.get("server." + desiredName);

            // one final try.  The string might be "instance1.blah" -- we must change that
            // to "instance1.server.blah"
            if (node == null && (desiredName = insertServerString(desiredName)) != null)
                node = normalizedChildren.get(desiredName);
        }

        return node;
    }

    private static String insertServerString(String desiredName) {
        try {
            String[] bits = desiredName.split("\\.");

            if (bits.length >= 2) {

                if (bits[1].equals("server"))
                    return desiredName;

                StringBuilder sb = new StringBuilder(bits[0]);
                sb.append(".server");

                for (int i = 1; i < bits.length; i++) {
                    sb.append('.').append(bits[i]);
                }
                return sb.toString();
            }
        }
        catch (Exception e) {
            // fall through
        }
        return null;
    }

    private static String normalizeDots(String s) {
        // Normalized means a literal dot is "\."  (one 'real' backslash followed by dot
        // in a java string in source code it would be:  "\\."  -- where one of the backslashes isn't 'real'

        if(s == null || !hasDots(s)) // return quick for performance
            return s;

        // to avoid expensive regexp work I do it this way...
        // it changes all occurences of "." with no preceding '\' with "\." and all
        // MONDOTS also.
        // I.e. all dots leave here with a backslash in front of them!

        return s.replace("\\.", MONDOT).replace(".", MONDOT).replace(MONDOT, "\\.");
    }

    private static boolean hasDots(String s) {
        if(s == null) {
            return false;
        }
        if(s.contains(MONDOT)) {
            return true;
        }
        return s.contains(".");
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy