[TIC-Web] Add dependency analysis for requires and provides tag 14/111614/2
authorChulwoo Shin <cw1.shin@samsung.com>
Mon, 23 Jan 2017 14:46:49 +0000 (23:46 +0900)
committerChulwoo Shin <cw1.shin@samsung.com>
Mon, 23 Jan 2017 14:58:07 +0000 (23:58 +0900)
- Modify data structure with tic-core
- Provides dependency analysis for requires and provides tag

Change-Id: I99ea3075f2956e790cbd55d87a4aa7c0d402dd33
Signed-off-by: Chulwoo Shin <cw1.shin@samsung.com>
public/src/js/page/package.js

index 3662370..936e425 100644 (file)
@@ -16,18 +16,34 @@ define([
     var logger = Logger('package.js');
 
     var $tree = $('#tic-package-left-col-tree');
-    var packages = [];
     var repos = [];
+    var packages = [];
+    var pkgInfo = null;
+    var provides = null;
+    var files = null;
+    var groups = null;
+    var groupId = 0;
+
+    if (!Date.now) { Date.now = function now() {
+            return new Date().getTime();
+        };
+    }
 
     function _getChecked() {
-        return _.filter(packages, ['state.checked', true]);
+        var checkedList = [];
+        _.forEach(pkgInfo, function(value, key){
+            if(value.view[0].state.checked === true){
+                checkedList.push(value);
+            }
+        });
+        return checkedList;
     }
 
     function _find(name, data) {
         if (_.isEmpty(data)) {
-            data = packages;
+            data = pkgInfo;
         }
-        return _.find(data, {'text' : name});
+        return data[name];
     }
 
     function _findTreeNode(name) {
@@ -36,15 +52,438 @@ define([
     }
 
     function _setDefaultPackage(defaultPackages) {
-        var nodes = _.filter(packages, function (node) {
-            return _.includes(defaultPackages, node.text);
+        var nodes = []
+        var pickDefault = _.pick(pkgInfo, defaultPackages);
+        _.forEach(pickDefault, function(value, key){
+            // One or more of the same nodes can exist
+            _.forEach(value.view, function(node){
+                nodes.push(node);
+            });
         });
+
         $tree.treeview('checkNode', [nodes, { silent: true }]);
         _.forEach(nodes, function(node) {
             node.state.checked = true;
         });
     }
 
+    // Compares two rpm version numbers (e.g. "1.7.1" or "1.2b").
+    // This function is based on https://gist.github.com/em92/d58944f21c68b69433cefb6c49e0defd
+    function versionCompare(v1, v2, options) {
+        if(!v1 && !v2){
+            return 0;
+        } else if(!v2){
+            return 1;
+        } else if(!v1){
+            return -1;
+        }
+
+        var v1parts = v1.split(/[.+-/_]/);
+        var v2parts = v2.split(/[.+-/_]/);
+
+        function compareParts(v1parts, v2parts, options) {
+            var zeroExtend = options && options.zeroExtend;
+
+            if (zeroExtend) {
+                while (v1parts.length < v2parts.length) v1parts.push("0");
+                while (v2parts.length < v1parts.length) v2parts.push("0");
+            }
+
+            for (var i = 0; i < v1parts.length; ++i) {
+                if (v2parts.length == i) {
+                    return 1;
+                }
+
+                var v1part = parseInt(v1parts[i]);
+                var v2part = parseInt(v2parts[i]);
+                // (NaN == NaN) -> false
+                var v1part_is_string = !(v1part == v1part);
+                var v2part_is_string = !(v2part == v2part);
+                v1part = v1part_is_string ? v1parts[i] : v1part;
+                v2part = v2part_is_string ? v2parts[i] : v2part;
+
+                if (v1part_is_string == v2part_is_string) {
+                    if (v1part_is_string == false) {
+                        // integer compare
+                        if (v1part == v2part) {
+                            continue;
+                        } else if (v1part > v2part) {
+                            return 1;
+                        } else {
+                            return -1;
+                        }
+                    } else {
+                        // letters and numbers in string
+                        // split letters and numbers
+                        var v1subparts = v1part.match(/[a-zA-Z]+|[0-9]+/g);
+                        var v2subparts = v2part.match(/[a-zA-Z]+|[0-9]+/g);
+                        if ( (v1subparts.length == 1) && (v2subparts.length == 1) ) {
+                            // only letters in string
+                            v1part = v1subparts[0];
+                            v2part = v2subparts[0];
+                            if (v1part == v2part) {
+                                continue;
+                            } else if (v1part > v2part) {
+                                return 1;
+                            } else {
+                                return -1;
+                            }
+                        }
+                        var result = compareParts(v1subparts, v2subparts);
+                        if (result == 0) {
+                            continue;
+                        } else {
+                            return result;
+                        }
+                    }
+                } else {
+                    return v2part_is_string ? 1 : -1;
+                }
+            }
+
+            if (v1parts.length != v2parts.length) {
+                return -1;
+            }
+            return 0;
+        }
+
+        return compareParts(v1parts, v2parts, options);
+    }
+
+
+
+    // analyze install-dependency (requires)
+    function _analyzeInstallDependency(checkNode) {
+        var stack = [];
+        var pkg_count = Object.keys(pkgInfo).length
+        var number = 0;
+        var sccNum = 0;
+        var groupNum = 0;
+        var selected = new Array(pkg_count);
+        var scc_id = new Array(pkg_count);
+        var min_num = new Array(pkg_count);
+
+        // init check type
+        _.forEach(pkgInfo, function(value, key){
+            // check type: -1(unchecked), 0(checked), 1~N(checking)
+            selected[value.id] = value.view[0].state.checked ? 0 : -1;
+            scc_id[value.id] = 0;
+        });
+
+        function _comapre_ver(ver1, ver2, flag){
+            var epoch1 = ver1.epoch;
+            var epoch2 = ver2.epoch;
+            // epoch is a number (optional, default=0)
+            epoch1 = epoch1 ? epoch1 : 0;
+            epoch2 = epoch2 ? epoch2 : 0;
+
+            if (epoch1 == epoch2){
+                var result = versionCompare(ver1.ver, ver2.ver);
+                // version match (ver1 == ver2)
+                if(result == 0){
+                    // flag == true means to compare versions between require and provide
+                    if(!flag || ver1.rel){
+                        return versionCompare(ver1.rel, ver2.rel);
+                    }
+                    return result;
+                }
+                return result;
+            } else if(epoch1 > epoch2){
+                return 1
+            } else {
+                return -1;
+            }
+        }
+
+        function _makeSCC(pkgId){
+            sccNum += 1;
+            var sccList = [];
+
+            // make scc
+            while(!_.isEmpty(stack)){
+                var pkg = stack.pop();
+                scc_id[pkg.id] = sccNum;
+                sccList.push(pkg);
+
+                if(pkg.id === pkgId)
+                    break;
+            }
+
+            // circular depependency
+            if (sccList.length > 1){
+                groupId += 1;
+                var groupPkgList = [];
+                _.forEach(sccList, function(pkg){
+                    pkg.group = groupId;
+                    groupPkgList.push(pkg.name);
+                });
+                groups[groupId] = groupPkgList;
+                logger.info('circular dependency: groupId(' + groupId + '), list(' + groupPkgList + ')');
+            }
+        }
+
+        function _createReference(node1, node2){
+            if(!_.isEmpty(node1.forward) && _.includes(node1.forward, node2.name))
+                return
+
+            if(!_.isEmpty(node1.forward)){
+                node1.forward.push(node2.name);
+            } else {
+                node1.forward = [node2.name];
+            }
+
+            if(!_.isEmpty(node2.backward)){
+                node2.backward.push(node1.name);
+            } else {
+                node2.backward = [node1.name];
+            }
+        }
+
+        function _select_rpm(capability, require){
+            if(capability.length == 1){
+                return pkgInfo[capability[0].name];
+            }
+
+            var provideList = [];
+            // 1. Choose the rpm included in version from provides
+            if (require.ver){
+                _.forEach(capability, function(provide){
+                    var cmpResult = _comapre_ver(require, provide.data, true);
+                    if (cmpResult === 0 && _.includes(['EQ', 'GE', 'LE'], require.flags)){
+                        provideList.push(provide);
+                    } else if(cmpResult === 1 && _.includes(['LT', 'LE'], require.flags)){
+                        provideList.push(provide);
+                    } else if(cmpResult === -1 && _.includes(['GT', 'GE'], require.flags)){
+                        provideList.push(provide);
+                    }
+                });
+            } else {
+                provideList = capability;
+            }
+
+            // error case (the rpm does not exist)
+            if (_.isEmpty(provideList)){
+                return null;
+            }
+
+            if (provideList.length == 1){
+                return pkgInfo[provideList[0].name];
+            }
+
+            // 2 Select one of the rpms by priority
+            // 2-1. Choose the default rpm or the selected rpm
+            // TODO: default profile rpm should be selected
+            _.forEach(provideList, function(provide){
+                var tmpInfo = pkgInfo[provide.name];
+                if(tmpInfo.view[0].state.checked || selected[tmpInfo.id] >= 1){
+                    return tmpInfo;
+                }
+            });
+
+            var maxVersion = null;
+            // # 2-2. Select the latest version of rpm
+            _.forEach(provideList, function(provide){
+                if(maxVersion){
+                    var ret = _comapre_ver(maxVersion.data, provide.data)
+                    if(ret == -1){
+                        maxVersion = provide;
+                    }
+                } else {
+                    maxVersion = provide;
+                }
+            });
+
+            return pkgInfo[maxVersion.name];
+        }
+
+        function _analyzeDep(node){
+            var pkgId = node.id;
+            number += 1;
+            selected[pkgId] = number;
+            min_num[pkgId] = number;
+            stack.push(node);
+
+            var dependents = {};
+            dependents[node.name] = node;
+
+            // installation dependency analysis of package
+            // TODO: recommends
+            _.forEach(['requires'], function(depTag){
+                if(_.has(node, depTag)){
+                    _.forEach(node.requires, function(require){
+                        var choose = null;
+
+                        if(_.has(provides, require.name)){
+                            var capList = provides[require.name];
+                            choose = _select_rpm(capList, require);
+                        } else if(_.has(files, require.name)){
+                            choose = pkgInfo[files[require.name][0]];
+                        }
+
+                        if (choose){
+                            // add forward/backward reference
+                            _createReference(node, choose);
+
+                            if(selected[choose.id] == -1){
+                                var result = _analyzeDep(choose);
+                                _.forEach(result, function(value, key){
+                                     if(!_.has(dependents, key)){
+                                         dependents[key] = value;
+                                     }
+                                });
+                                min_num[pkgId] = Math.min(min_num[pkgId], min_num[choose.id])
+                            } else if(selected[choose.id] >= 1 && scc_id[choose.id] == 0){
+                                // cross edge that can not be ignored
+                                min_num[pkgId] = Math.min(min_num[pkgId], min_num[choose.id])
+                            }
+                        } else {
+                            logger.info('the capability('+require.name+')does not exist. should be checked for error');
+                        }
+                    });
+                }
+            });
+
+            if (min_num[pkgId] == selected[pkgId]){
+                _makeSCC(pkgId);
+            }
+
+            return dependents;
+        }
+
+        return _analyzeDep(checkNode);
+    }
+
+    // analyze uncheck dependency
+    function _analyzeUncheckDependency(uncheckNode) {
+
+        function _checkCircularDependency(node){
+            var groupId = node.group
+            var groupPkgList = groups[groupId];
+            var groupObj = {};
+
+            // Set object for group
+            _.forEach(groupPkgList, function(pkgName){
+                groupObj[pkgName] = null;
+            });
+
+            var isUncheckable = true;
+            _.forEach(groupPkgList, function(pkgName) {
+                var pkg = _find(pkgName);
+
+                // the node is selfChecked or uncheckable
+                if(pkg.selfChecked || !isUncheckable){
+                    isUncheckable = false;
+                    return false;
+                }
+
+                _.forEach(pkg.backward, function(backRef) {
+                    // If node is Referenced by another node (Not a node in the group), 
+                    // unable to uncheck group nodes
+                    if(!_.has(groupObj, backRef)){
+                        isUncheckable = false;
+                        return false;
+                    }
+                });
+            });
+
+            if(isUncheckable){
+                // init visited obj
+                groupVisited[groupId] = {};
+
+                // Delete backward/forward reference of group node
+                _.forEach(groupPkgList, function(pkgName) {
+                    var pkg = _find(pkgName);
+                    // backward ref.
+                    pkg.backward = null;
+                    // visited init
+                    groupVisited[groupId][pkg.name] = -1;
+                });
+                logger.info('Group(' + groupId +  ') is uncheckable : ' + groupPkgList);
+                return true;
+            } else {
+                logger.info('Group(' + groupId +  ') is Not uncheckable: ' + groupPkgList);
+                return false;
+            }
+        }
+
+        function _analyzeUncheck(parent, node){
+            if(!_.isEmpty(parent)){
+                // TODO: performance
+                if(!_.isEmpty(node.backward)){
+                    var bIndex = node.backward.indexOf(parent.name);
+                    if(bIndex > -1){
+                        // TODO: performance
+                        // remove backward reference (parent)
+                        node.backward.splice(bIndex, 1);
+                    }
+                }
+                // selfCheck node is not unchecked
+                if (node.selfChecked == true)
+                    return null;
+            }
+
+            var uncheckPkgs = null;
+            // Check that the selected node is uncheckable
+            if(!_.isEmpty(node.backward)){
+                // check circular dependency
+                if(!node.group || !_checkCircularDependency(node)){
+                    return null;
+                }
+            }
+
+            // the selected node is uncheckable
+            uncheckPkgs = {};
+            uncheckPkgs[node.name] = node;
+            // uncheckable pkg of group
+            if(node.group && !_.isEmpty(groupVisited[node.group])){
+                groupVisited[node.group][node.name] = 1;
+            }
+
+            // if selected node has forward references
+            if(!_.isEmpty(node.forward)){
+                _.forEach(node.forward, function(fname){
+                    var forwardNode = _find(fname);
+
+                    // If pkg has a circular dependency and is unchekcable,
+                    // circular dep. pkgs can only be visited once
+                    var fvisit = groupVisited[forwardNode.group];
+                    if(!_.isEmpty(fvisit) && fvisit[fname] === 1){
+                        return; // continue;
+                    }
+
+                    var result = _analyzeUncheck(node, forwardNode);
+
+                    // updates pkgs for uncheck
+                    if(!_.isEmpty(result)){
+                        _.forEach(result, function(value, key){
+                            if(!_.has(uncheckPkgs, key)){
+                                uncheckPkgs[key] = value;
+                            }
+                        });
+                    }
+                });
+                // forward reference reset
+                node.forward = null;
+                node.group = null;
+            }
+
+            return uncheckPkgs;
+        }
+
+        // { 'group_id' : {pkgName: -1, ... }, ... }
+        var groupVisited = {};
+        var uncheckPkgs = _analyzeUncheck(null, uncheckNode);
+
+        // delete groupId from groups
+        _.forEach(groupVisited, function(groupList, groupId){
+            if(!_.isEmpty(groups[groupId])){
+                delete groups[groupId];
+            }
+        });
+
+        return uncheckPkgs;
+    }
+
     /**
      * Summary page
      */
@@ -56,6 +495,7 @@ define([
 
         var list = _getChecked();
         var count = _.size(list);
+
         var imageSize = _.sumBy(list, function getImageSize(item) {
             return _.toNumber(item.size || 0);
         });
@@ -73,7 +513,7 @@ define([
             packageListBadge.html(count);
         }
         if (!_.isEmpty(list)) {
-            packageList.html(_.orderBy(_.map(list, 'text')).join('<br>'));
+            packageList.html(_.orderBy(_.map(list, 'name')).join('<br>'));
         }
 
         $('#tic-package-create').toggleClass('disabled', count === 0);
@@ -95,69 +535,87 @@ define([
         var dependency = $('#tic-package-info-dependency').empty();
         var dependencyBadge = $('#tic-package-info-dependency-badge').empty();
 
-        if (!_.isEmpty(node.text)) {
-            text.html(node.text);
-        }
-        if (!_.isEmpty(node.version)) {
-            version.html(node.version);
+        var pkg = pkgInfo[node.text];
+
+        if (!_.isEmpty(pkg.name)) {
+            text.html(pkg.name);
         }
-        if (!_.isEmpty(node.arch)) {
-            arch.html(node.arch);
+        if (!_.isEmpty(pkg.version)) {
+            if(!_.isEmpty(pkg.version.rel)){
+                version.html(pkg.version.ver + '-' + pkg.version.rel);
+            } else {
+                version.html(pkg.version.ver);
+            }
         }
-        if (!_.isEmpty(node.size)) {
-            size.html(Util.bytesToSize(node.size));
+        if (!_.isEmpty(pkg.arch)) {
+            arch.html(pkg.arch);
         }
-        if (!_.isEmpty(node.installed)) {
-            installedSize.html(Util.bytesToSize(node.installed));
+        if (!_.isEmpty(pkg.size)) {
+            size.html(Util.bytesToSize(pkg.size));
         }
-        if (!_.isEmpty(node.summary)) {
-            summary.html(node.summary);
+        if (!_.isEmpty(pkg.installed)) {
+            installedSize.html(Util.bytesToSize(pkg.installed));
         }
-        if (!_.isEmpty(node.description)) {
-            description.html(node.description);
+        if (!_.isEmpty(pkg.summary)) {
+            summary.html(pkg.summary);
         }
-        if (!_.isEmpty(node.dependency)) {
-            dependencyBadge.html(node.dependency.length)
-            dependency.html(_.orderBy(node.dependency).join('<br>'));
+        if (!_.isEmpty(pkg.description)) {
+            description.html(pkg.description);
         }
+        // if (!_.isEmpty(info.dependency)) {
+        //     dependencyBadge.html(info.dependency.length)
+        //     dependency.html(_.orderBy(info.dependency).join('<br>'));
+        // }
     }
 
     /**
      * Treeview: A node is checked.
      */
     function _onNodeChecked(event, node) {
-        if (_find(node.text).state.checked === true) {
+        var startTS = Date.now();
+        var localNode = _find(node.text);
+        if (localNode.view[0].state.checked === true) {
             $tree.treeview('uncheckNode', [node, { silent: false }]);
             return;
         }
 
-        logger.info('checked: ' + node.text);
+        logger.info('checked: ' + localNode.name);
 
         _nodeSelected(event, node);
-
-        var localNode = _find(node.text);
         localNode.selfChecked = true;
-        localNode.state.checked = true;
+        localNode.view[0].state.checked = true;
+
+        // analyze install-dependency (requires)
+        var depPkg = _analyzeInstallDependency(localNode)
+
+        var analyzeTS = Date.now();
+
+        // TODO: temporary code
+        var tempcode = [];
+        _.forEach(depPkg, function(value, key){ tempcode.push(value.name); });
+        logger.info(localNode.name + ' install-dependency(' + tempcode.length +'): ' + tempcode);
 
         var toggleNode = [];
-        if (!_.isEmpty(localNode.dependency)) {
-            _.forEach(localNode.dependency, function(name) { // FIXME: Performance
-                var depNode = _find(name);
-                if (_.isEmpty(depNode)) {
-                    Util.showAlertDialog('Can not find dependency package. \'' + name + '\'');
-                } else {
-                    if (depNode.state.checked === false) {
-                        toggleNode.push(depNode);
+        if (!_.isEmpty(depPkg)) {
+            _.forEach(depPkg, function(value, key) {
+                _.forEach(value.view, function(node) {
+                    if (node.state.checked === false) {
+                        toggleNode.push(node);
                     }
-                }
+                });
             });
-            $tree.treeview('checkNode', [toggleNode, { silent: true }]);
 
+            $tree.treeview('checkNode', [toggleNode, { silent: true }]);
             // update local data
             _.forEach(toggleNode, function(node) {
                 node.state.checked = true;
             });
             _updateSummary();
+
+            var endTS = Date.now();
+            logger.info('[Check] Total time: ' + (endTS - startTS) + 'ms');
+            logger.info('[Check] Analyze dep. time: ' + (analyzeTS - startTS) + 'ms');
+            logger.info('[Check] Update view time: ' + (endTS - analyzeTS) + 'ms');
         }
     }
 
@@ -165,79 +623,55 @@ define([
      * Treeview: A node is unchecked.
      */
     function _onNodeUnchecked(event, node) {
-        if (_find(node.text).state.checked === false) {
+        var startTS = Date.now();
+        var localNode = _find(node.text);
+        if (localNode.view[0].state.checked === false) {
             $tree.treeview('checkNode', [node, { silent: false }]);
             return;
         }
 
         logger.info('unchecked: ' + node.text);
-        Util.showLoadingDialog(true);
 
-        // TODO: Refactoring
-        var localNode = _find(node.text);
+        var uncheckPkgs = _analyzeUncheckDependency(localNode);
 
-        // check reference node
-        var checkedRefNode = [];
-        if (!_.isEmpty(localNode.reference)) {
-            _.forEach(localNode.reference, function(refNode) {
-                if (refNode.state.checked === true && !_.isEqual(refNode.text, localNode.text) && refNode.selfChecked === true) {
-                    checkedRefNode.push(refNode);
-                }
-            });
-            if (!_.isEmpty(checkedRefNode)) {
-                Util.showLoadingDialog(false);
-                Util.showAlertDialog('\'' +localNode.text + '\'' + ' package repuired from ' + '\'' + _.toString(_.map(checkedRefNode, 'text')) + '\'');
+        var analyzeTS = Date.now();
 
-                // cancel for unchecked
-                $tree.treeview('checkNode', [node, { silent: true }]);
-                localNode.state.checked = true;
-                _updateSummary();
-                return;
-            }
-        }
+        // TODO: temporary code
+        var tempcode = [];
+        _.forEach(uncheckPkgs, function(value, key){ tempcode.push(value.name); });
+        logger.info(localNode.name + ' uncheck-dependency(' + tempcode.length +'): ' + tempcode);
 
-        // check dependency node
         var toggleNode = [];
-        if (!_.isEmpty(localNode.dependency) && localNode.selfChecked === true) {
-            _.forEach(localNode.dependency, function(name) { // FIXME: Performance
-                var depNode = _find(name);
-                if (!_.isEqual(localNode.text, depNode.text)) {
-                    if (depNode.state.checked === true) {
-                        if (depNode.selfChecked === true) {
-                            logger.warn('\'' + depNode.text + '\'' + ' package was still checked because user checked item.');
-                        } else {
-                            toggleNode.push(depNode);
-                        }
+        if(!_.isEmpty(uncheckPkgs)) {
+            _.forEach(uncheckPkgs, function(value, key){
+                _.forEach(value.view, function(node){
+                    if (node.state.checked === true) {
+                        toggleNode.push(node);
                     }
-                }
+                });
             });
 
-            // check reference of dependency node
-            var checkedRefNode2 = [];
+            $tree.treeview('uncheckNode', [toggleNode, { silent: true }]);
+            // update local data
             _.forEach(toggleNode, function(node) {
-                if (!_.isEmpty(node.reference)) {
-                    checkedRefNode2.push(node);
-                    _.forEach(node.reference, function(refNode) {
-                        if (!_.isEqual(refNode.text, node.text) && !_.isEqual(refNode.text, localNode.text)) {
-                            if (refNode.state.checked === true && refNode.selfChecked === true) {
-                                checkedRefNode2.pop();
-                                return false;
-                            }
-                        }
-                    });
-                }
-            });
-
-            $tree.treeview('uncheckNode', [checkedRefNode2, { silent: true }]);
-            _.forEach(checkedRefNode2, function(node) {
                 node.state.checked = false;
             });
+
+            //node.state.checked = false;
+            localNode.selfChecked = false;
+            _updateSummary();
+        } else {
+            Util.showAlertDialog('Could not uncheck the \'' +localNode.name + '\'' + '<br> because the \'' +  localNode.backward + '\' packages depends on it');
+
+            // selected node change to check state
+            $tree.treeview('checkNode', [[node], { silent: true }]);
+            node.state.checked = true;
         }
-        localNode.state.checked = false;
-        localNode.selfChecked = false;
-        _updateSummary();
 
-        Util.showLoadingDialog(false);
+        var endTS = Date.now();
+        logger.info('[Uncheck] Total time: ' + (endTS - startTS) + 'ms');
+        logger.info('[Uncheck] Analyze dep. time: ' + (analyzeTS - startTS) + 'ms');
+        logger.info('[Uncheck] Update view time: ' + (endTS - analyzeTS) + 'ms');
     }
 
     /**
@@ -245,22 +679,29 @@ define([
      */
     function updatePackageTree(rawData) {
         repos = rawData.repos;
+        pkgInfo = rawData.data.packages
+        provides = rawData.data.provides;
+        files = rawData.data.files;
+        groups = rawData.data.groups;
+
+        _.forEach(groups, function(value, key){
+            groupId += 1;
+        })
 
         return new Promise(function (resolve, reject) {
 
             function _onRendered(event, nodes) {
-                packages = _.uniqBy(_.values(nodes), 'text');
-                _.forEach(packages, function(node) {
-                    _.forEach(node.dependency, function(name) { // FIXME: Performance
-                        var depNode = _.find(packages, {'text' : name});
-                        depNode.selfChecked = false;
-                        if (_.isEmpty(depNode.reference)) {
-                            depNode.reference = [];
-                        }
-                        if (!_.isEqual(depNode.text, node.text)) {
-                            depNode.reference.push(node);
-                        }
-                    });
+                packages = _.values(nodes);
+                _.forEach(nodes, function(node, key) {
+                     // add a reference variable for treeview
+                     var pkg = pkgInfo[node.text]
+                     if (pkg) {
+                         if(_.isEmpty(pkg.view)){
+                             pkg.view = [node];
+                         } else {
+                             pkg.view.push(node);
+                         }
+                     }
                 });
 
                 _setDefaultPackage(rawData.defaultpackages);
@@ -280,7 +721,7 @@ define([
             }
 
             $tree.treeview({
-                data: rawData.packages,
+                data: rawData.view,
                 showIcon: false,
                 showCheckbox: true,
                 onRendered: _onRendered,
@@ -368,7 +809,7 @@ define([
     return {
         /**
          * Initialize for treeview
-         * @method updatePackageTree
+         * @method updatePackageTreepkginfo
          * @param {array} array of objects
          * @return Promise
          */