text:
#include "connection.h"
#include "request.h"
#include "utils.h"

#include <iostream>
#include <sstream>

namespace crequests {

    request_t::request_t() {

    }

    request_t::request_t(const request_t& request)
        : m_uri {request.m_uri},
          m_method {request.m_method},
          m_timeout {request.m_timeout},
          m_store_timeout {request.m_store_timeout},
          m_redirect {request.m_redirect},
          m_redirect_count {request.m_redirect_count},
          m_gzip {request.m_gzip},
          m_data {request.m_data},
          m_keep_alive {request.m_keep_alive},
          m_headers {request.m_headers},
          m_final_callback {request.m_final_callback},
          m_auth {request.m_auth},
          m_cache_redirects {request.m_cache_redirects},
          m_cookies {request.m_cookies},
          m_throw_on_error {request.m_throw_on_error},
          m_body_callback {request.m_body_callback},
          m_ssl_auth {request.m_ssl_auth},
          m_ssl_certs {request.m_ssl_certs},
          m_always_verify_peer {request.m_always_verify_peer},
          m_verify_path {request.m_verify_path},
          m_verify_filename {request.m_verify_filename},
          m_certificate_file {request.m_certificate_file},
          m_private_key_file {request.m_private_key_file}
    {

    }

    request_t::request_t(request_t&& request)
        : m_uri {std::move(request.m_uri)},
          m_method {std::move(request.m_method)},
          m_timeout {std::move(request.m_timeout)},
          m_store_timeout {std::move(request.m_store_timeout)},
          m_redirect {std::move(request.m_redirect)},
          m_redirect_count {std::move(request.m_redirect_count)},
          m_gzip {std::move(request.m_gzip)},
          m_data {std::move(request.m_data)},
          m_keep_alive {std::move(request.m_keep_alive)},
          m_headers {std::move(request.m_headers)},
          m_final_callback {std::move(request.m_final_callback)},
          m_auth {std::move(request.m_auth)},
          m_cache_redirects {std::move(request.m_cache_redirects)},
          m_cookies {std::move(request.m_cookies)},
          m_throw_on_error {std::move(request.m_throw_on_error)},
          m_body_callback {std::move(request.m_body_callback)},
          m_ssl_auth {std::move(request.m_ssl_auth)},
          m_ssl_certs {std::move(request.m_ssl_certs)},
          m_always_verify_peer {std::move(request.m_always_verify_peer)},
          m_verify_path {std::move(request.m_verify_path)},
          m_verify_filename {std::move(request.m_verify_filename)},
          m_certificate_file {std::move(request.m_certificate_file)},
          m_private_key_file {std::move(request.m_private_key_file)}
    {

    }

    request_t& request_t::operator=(const request_t& request) {
        if (this != &request) {
            m_uri = request.m_uri;
            m_method = request.m_method;
            m_timeout = request.m_timeout;
            m_store_timeout = request.m_store_timeout;
            m_redirect = request.m_redirect;
            m_redirect_count = request.m_redirect_count;
            m_gzip = request.m_gzip;
            m_data = request.m_data;
            m_keep_alive = request.m_keep_alive;
            m_headers = request.m_headers;
            m_final_callback = request.m_final_callback;
            m_auth = request.m_auth;
            m_cache_redirects = request.m_cache_redirects;
            m_cookies = request.m_cookies;
            m_throw_on_error = request.m_throw_on_error;
            m_body_callback = request.m_body_callback;
            m_ssl_auth = request.m_ssl_auth;
            m_ssl_certs = request.m_ssl_certs;
            m_always_verify_peer = request.m_always_verify_peer;
            m_verify_path = request.m_verify_path;
            m_verify_filename = request.m_verify_filename;
            m_certificate_file = request.m_certificate_file;
            m_private_key_file = request.m_private_key_file;
        }

        return *this;
    }

    request_t::~request_t() {

    }

    /****************************************************************************
     * Set. Constant reference. Uri.
     ***************************************************************************/

    void request_t::uri(const uri_t& uri) {
        m_uri = uri;
    }

    void request_t::url(const string_t& url) {
        m_uri.url(url_t{url});
    }

    void request_t::url(const url_t& url) {
        m_uri.url(url);
    }

    void request_t::protocol(const protocol_t& protocol) {
        m_uri.protocol(protocol);
    }

    void request_t::domain(const domain_t& domain) {
        m_uri.domain(domain);
    }

    void request_t::port(const port_t& port) {
        m_uri.port(port);
    }

    void request_t::path(const path_t& path) {
        m_uri.path(path);
    }

    void request_t::query(const query_t& query) {
        m_uri.query(query);
    }

    void request_t::params(const params_t& params) {
        m_uri.params(params);
    }

    /****************************************************************************
     * Set. Rvalue reference. Uri.
     ***************************************************************************/

    void request_t::uri(uri_t&& uri) {
        m_uri = std::move(uri);
    }

    void request_t::url(string_t&& url) {
        m_uri.url(url_t{std::move(url)});
    }

    void request_t::url(url_t&& url) {
        m_uri.url(std::move(url));
    }

    void request_t::protocol(protocol_t&& protocol) {
        m_uri.protocol(std::move(protocol));
    }

    void request_t::domain(domain_t&& domain) {
        m_uri.domain(std::move(domain));
    }

    void request_t::port(port_t&& port) {
        m_uri.port(std::move(port));
    }

    void request_t::path(path_t&& path) {
        m_uri.path(std::move(path));
    }

    void request_t::query(query_t&& query) {
        m_uri.query(std::move(query));
    }

    void request_t::params(params_t&& params) {
        m_uri.params(std::move(params));
    }

    /****************************************************************************
     * Set. Constant reference.
     ***************************************************************************/

    void request_t::method(const method_t& method) {
        m_method = method;
    }

    void request_t::timeout(const timeout_t& timeout) {
        m_timeout = timeout;
    }

    void request_t::store_timeout(const store_timeout_t& store_timeout) {
        m_store_timeout = store_timeout;
    }

    void request_t::redirect(const redirect_t& redirect) {
        m_redirect = redirect;
    }

    void request_t::redirect_count(const redirect_count_t& redirect_count) {
        m_redirect_count = redirect_count;
    }

    void request_t::gzip(const gzip_t& gzip) {
        m_gzip = gzip;
    }

    void request_t::data(const data_t& data) {
        m_data = data;
    }

    void request_t::headers(const headers_t& headers) {
        m_headers = headers;
    }

    void request_t::final_callback(const final_callback_t& final_callback) {
        m_final_callback = final_callback;
    }

    void request_t::auth(const auth_t& auth) {
        m_auth = auth;
    }

    void request_t::keep_alive(const keep_alive_t& keep_alive) {
        m_keep_alive = keep_alive;
    }

    void request_t::cache_redirects(const cache_redirects_t& cache_redirects) {
        m_cache_redirects = cache_redirects;
    }

    void request_t::cookies(const cookies_t& cookies) {
        m_cookies = cookies;
    }

    void request_t::throw_on_error(const throw_on_error_t& throw_on_error) {
        m_throw_on_error = throw_on_error;
    }

    void request_t::body_callback(const body_callback_t& body_callback) {
        m_body_callback = body_callback;
    }

    void request_t::ssl_auth(const ssl_auth_t& ssl_auth) {
        m_ssl_auth = ssl_auth;
    }

    void request_t::ssl_certs(const ssl_certs_t& ssl_certs) {
        m_ssl_certs = ssl_certs;
    }

    void request_t::always_verify_peer(const always_verify_peer_t& always_verify_peer) {
        m_always_verify_peer = always_verify_peer;
    }

    void request_t::verify_path(const verify_path_t& verify_path) {
        m_verify_path = verify_path;
    }

    void request_t::verify_filename(const verify_filename_t& verify_filename) {
        m_verify_filename = verify_filename;
    }

    void request_t::certificate_file(const certificate_file_t& certificate_file) {
        m_certificate_file = certificate_file;
    }

    void request_t::private_key_file(const private_key_file_t& private_key_file) {
        m_private_key_file = private_key_file;
    }

    /****************************************************************************
     * Set. Rvalue reference.
     ***************************************************************************/

    void request_t::method(method_t&& method) {
        m_method = std::move(method);
    }

    void request_t::timeout(timeout_t&& timeout) {
        m_timeout = std::move(timeout);
    }

    void request_t::store_timeout(store_timeout_t&& store_timeout) {
        m_store_timeout = std::move(store_timeout);
    }

    void request_t::redirect(redirect_t&& redirect) {
        m_redirect = std::move(redirect);
    }

    void request_t::redirect_count(redirect_count_t&& redirect_count) {
        m_redirect_count = std::move(redirect_count);
    }

    void request_t::gzip(gzip_t&& gzip) {
        m_gzip = std::move(gzip);
    }

    void request_t::data(data_t&& data) {
        m_data = std::move(data);
    }

    void request_t::headers(headers_t&& headers) {
        m_headers = std::move(headers);
    }

    void request_t::final_callback(final_callback_t&& final_callback) {
        m_final_callback = std::move(final_callback);
    }

    void request_t::auth(auth_t&& auth) {
        m_auth = std::move(auth);
    }

    void request_t::keep_alive(keep_alive_t&& keep_alive) {
        m_keep_alive = std::move(keep_alive);
    }

    void request_t::cache_redirects(cache_redirects_t&& cache_redirects) {
        m_cache_redirects = std::move(cache_redirects);
    }

    void request_t::cookies(cookies_t&& cookies) {
        m_cookies = std::move(cookies);
    }

    void request_t::throw_on_error(throw_on_error_t&& throw_on_error) {
        m_throw_on_error = std::move(throw_on_error);
    }

    void request_t::body_callback(body_callback_t&& body_callback) {
        m_body_callback = std::move(body_callback);
    }

    void request_t::ssl_auth(ssl_auth_t&& ssl_auth) {
        m_ssl_auth = std::move(ssl_auth);
    }

    void request_t::ssl_certs(ssl_certs_t&& ssl_certs) {
        m_ssl_certs = std::move(ssl_certs);
    }

    void request_t::always_verify_peer(always_verify_peer_t&& always_verify_peer) {
        m_always_verify_peer = std::move(always_verify_peer);
    }

    void request_t::verify_path(verify_path_t&& verify_path) {
        m_verify_path = std::move(verify_path);
    }

    void request_t::verify_filename(verify_filename_t&& verify_filename) {
        m_verify_filename = std::move(verify_filename);
    }

    void request_t::certificate_file(certificate_file_t&& certificate_file) {
        m_certificate_file = std::move(certificate_file);
    }

    void request_t::private_key_file(private_key_file_t&& private_key_file) {
        m_private_key_file = std::move(private_key_file);
    }

    /****************************************************************************
     * Get. Constant reference.
     ***************************************************************************/

    const uri_t& request_t::uri() const {
        return m_uri;
    }

    const method_t& request_t::method() const {
        return m_method;
    }

    const timeout_t& request_t::timeout() const {
        return m_timeout;
    }

    const store_timeout_t& request_t::store_timeout() const {
        return m_store_timeout;
    }

    const redirect_t& request_t::redirect() const {
        return m_redirect;
    }

    const redirect_count_t& request_t::redirect_count() const {
        return m_redirect_count;
    }

    const gzip_t& request_t::gzip() const {
        return m_gzip;
    }

    const data_t& request_t::data() const {
        return m_data;
    }

    const headers_t& request_t::headers() const {
        return m_headers;
    }

    const final_callback_t& request_t::final_callback() const {
        return m_final_callback;
    }

    const auth_t& request_t::auth() const {
        return m_auth;
    }

    const keep_alive_t& request_t::keep_alive() const {
        return m_keep_alive;
    }

    const cache_redirects_t& request_t::cache_redirects() const {
        return m_cache_redirects;
    }

    const cookies_t& request_t::cookies() const {
        return m_cookies;
    }

    const throw_on_error_t& request_t::throw_on_error() const {
        return m_throw_on_error;
    }

    const body_callback_t& request_t::body_callback() const {
        return m_body_callback;
    }

    const ssl_auth_t& request_t::ssl_auth() const {
        return m_ssl_auth;
    }

    const ssl_certs_t& request_t::ssl_certs() const {
        return m_ssl_certs;
    }

    const always_verify_peer_t& request_t::always_verify_peer() const {
        return m_always_verify_peer;
    }

    const verify_path_t& request_t::verify_path() const {
        return m_verify_path;
    }

    const verify_filename_t& request_t::verify_filename() const {
        return m_verify_filename;
    }

    const certificate_file_t& request_t::certificate_file() const {
        return m_certificate_file;
    }

    const private_key_file_t& request_t::private_key_file() const {
        return m_private_key_file;
    }

    /****************************************************************************
     * Other functions.
     ***************************************************************************/

    string_t request_t::make_request() const {
        assert(not m_method.empty());
        assert(not m_uri.path().empty());
        assert(not m_uri.domain().empty());

        std::ostringstream out;

        out << m_method << " " << m_uri.path();

        if (not m_uri.query().empty())
            out << "?" + m_uri.query().value();

        const auto cookies =
            m_cookies.get(m_uri.domain().value(), m_uri.path().value());

        auto headers_ = m_headers;
        if (not cookies.empty()) {
            headers_.insert("Cookies", cookies.to_string());
        }

        out << " " << "HTTP/1.1\r\n";
        out << headers_.to_string();

        if (not m_data.empty()) {
            if (m_gzip) {
                out << compress(m_data.value());
            }
            else {
                out << m_data;
            }
        }

        return out.str();
    }

    void request_t::prepare()  {
        m_uri.prepare();
        assert(not m_uri.domain().empty() or not m_uri.url().empty());
        if (m_gzip)
            m_headers.insert("Content-Encoding", "gzip");
        if (not m_auth.first.empty() and not m_auth.second.empty())
            m_headers.insert("Authorization",
                             "Basic " + b64encode(m_auth.to_string()));
        if (m_keep_alive)
            m_headers.insert("Connection", "keep-alive");
        if (not m_data.empty())
            m_headers.insert("Content-Length",
                              std::to_string(m_data.value().size()));
        m_headers.insert("Host", m_uri.domain().value());
    }

    bool request_t::is_ssl() const {
        return uri().protocol().value() == "https";
    }

    std::ostream& operator<<(std::ostream& out, const request_t& request) {
        out << "Request: "
            << request.uri()
            << "timeout: " << request.timeout() << "\n\n"
            << request.headers() << "\n\n"
            << request.data() << "\n";

        return out;
    }

} /* namespace crequests */


text:
import argparse
from pprint import pprint, pformat
from sys import stderr
import sys
reload(sys)
from sys import setdefaultencoding
from shutil import rmtree
from copy import deepcopy
from yaml import load, dump

from rosinstall_generator.distro import get_distro, get_package_names
from rosinstall_generator.distro import get_recursive_dependencies, get_release_tag

from collections import defaultdict
import pygraphviz as pgv

from rosdistro import get_distribution_files, get_index, get_index_url
from tempfile import mkdtemp
from logging import info, basicConfig, exception, warning, INFO

import xml.etree.ElementTree as ET
from subprocess import check_call
from os.path import join
from copy import copy
from catkin_pkg import topological_order

setdefaultencoding('utf8')

def dictify(r, root=True):
    if root:
        return {r.tag: dictify(r, False)}
    d = copy(r.attrib)
    if r.text:
        d["_text"] = r.text
    for x in r.findall("./*"):
        if x.tag not in d:
            d[x.tag] = []
        d[x.tag].append(dictify(x, False))
    return d

class CacheAnalyser:

    def __init__(self, distro='kinetic', tags=['lcas'],
        analyse_release=True, analyse_source=True,
        repo_whitelist=None
    ):
        self._distro_name = distro
        self._distro = get_distro(distro)
        self._pkgs = {}
        __repo_template = {
            'packages': {},
            'type': None,
            'url': None,
            'release_url': None,
            'version': None,
            'release_version': None,
            'requires_repositories': set([]),
            'required_by_repositories': set([]),
            'external_dependencies': set([]),
            'internal_dependencies': set([]),
            'status': 'unknown',
            'jenkins_job': None
        }
        __pkg_template = {
            'name': None,
            'authors': [],
            'maintainers': [],
            'url': '',
            'description': '',
            'license': 'unknown',
            'status': 'unknown',
            'deps': set([]),
            'repository': None
        }
        self._repositories = defaultdict(lambda: deepcopy(__repo_template))
        self._pkgs = defaultdict(lambda: deepcopy(__pkg_template))

        self._index = get_index(get_index_url())
        self._distributions = get_distribution_files(self._index, distro)

        self._distribution = None
        for d in self._distributions:
            if set(d.tags).intersection(set(tags)):
                self._distribution = d
                break
        # find only the first release platform and assume it's the right one...
        self._release_platform = self._distribution.release_platforms['ubuntu'][0]
        self.__jenkins_url_template_dev = (
            'https://lcas.lincoln.ac.uk/buildfarm/job/%sdev__%%s__ubuntu_%s_amd64/'
            % (
                self._distro_name[0].upper(),
                self._release_platform
                )
            )

        self._analyse_release = analyse_release
        self._analyse_source = analyse_source
        self._repo_whitelist = repo_whitelist

        self._distro_repositories = self._distribution.repositories
        self._released_packages_set = set(self._distribution.release_packages)

    def _analyse_repos(self):
        tmp_dir = mkdtemp()
        try:
            for r, sg in self._distro_repositories.items():
                if self._repo_whitelist:
                    if r not in self._repo_whitelist:
                        continue
                try:
                    info('analysing repository %s' % r)
                    if sg.release_repository and self._analyse_release:  # released
                        self._repositories[r]['packages'] = self._analyse_released_repo(sg)
                        self._repositories[r]['status'] = 'released'
                        self._repositories[r]['release_url'] = sg.release_repository.url
                        self._repositories[r]['release_version'] = sg.release_repository.version
                        info('-> repository %s is RELEASED as version %s with packages "%s"' % (
                            r, sg.release_repository.version, ', '.join(list(self._repositories[r]['packages']))))
                    elif sg.source_repository and self._analyse_source:  # not released but source available
                        if sg.source_repository.type == 'git':
                            self._repositories[r]['packages'] = self._analyse_non_released_repo(sg, tmp_dir)
                            self._repositories[r]['status'] = 'source'
                            info('-> repository %s is NON-released with packages "%s"' % (
                                r, ', '.join(list(self._repositories[r]['packages']))))
                        else:
                            warning('skipping source repository %s as it is not git' % r)
                    if sg.source_repository:
                        self._repositories[r]['type'] = sg.source_repository.type
                        self._repositories[r]['url'] = sg.source_repository.url
                        self._repositories[r]['version'] = sg.source_repository.version
                        if sg.source_repository.test_commits:
                            self._repositories[r]['jenkins_job'] = self.__jenkins_url_template_dev % r

                    self._pkgs.update(self._repositories[r]['packages'])
                except Exception  as e:
                    exception("skipping %s as exception occured" % r)

        finally:
            rmtree(tmp_dir)
        self._analyse_deps()

    def _analyse_deps(self):
        for p in self._pkgs:
            pkg = self._pkgs[p]
            repo = pkg['repository']
            info('analyse dependencies for package %s' % p)
            internal_deps = pkg['deps'].intersection(set(self._pkgs))
            external_deps = pkg['deps'].difference(set(self._pkgs))
            for d in internal_deps:
                dep_repo = self._pkgs[d]['repository']
                if repo != dep_repo:  # ignore self-dep
                    self._repositories[dep_repo]['required_by_repositories'].add(repo)
                    self._repositories[repo]['requires_repositories'].add(dep_repo)
                    self._repositories[repo]['internal_dependencies'].update(internal_deps)

            self._repositories[repo]['external_dependencies'].update(external_deps)

    def _extract_from_package_xml(self, px):
        return {
            'authors': ([a['_text']
                            for a in px['author']]
                        if 'author' in px
                        else ''),
            'maintainers': ([a['_text']
                                for a in px['maintainer']]
                            if 'maintainer' in px
                            else ''),
            'description': (' '.join(
                            [a['_text']
                                for a in px['description']])
                            if 'description' in px
                            else ''),
            'license': (' '.join(
                            [a['_text']
                                for a in px['license']])
                        if 'license' in px
                        else ''),
            'url': (px['url'][0]['_text'] if '_text' in px['url'][0] else None) if 'url' in px else None
        }

    def _analyse_released_repo(self, sg):
        _pkg={}

        for p in sg.release_repository.package_names:
            deps = get_recursive_dependencies(self._distro, [p], limit_depth=1)
            e = {
                'name': p,
                'status': 'released',
                'deps': deps,
                'repository': self._distribution.release_packages[p].repository_name
            }
            px = self.parse_package_xml(p)['package']
            e.update(self._extract_from_package_xml(px))

            _pkg[p] = e
        return _pkg

    def _analyse_non_released_repo(self, sg, tmp_dir):
        _pkgs={}

        try:
            self.__checkout(
                sg.source_repository.url,
                sg.source_repository.version,
                sg.name, tmp_dir)
        except Exception:
            exception('exception when trying to checkout repository %s. Carrying on regardless.' % sg.name)
        try:
            pkgs = [
                p[1] for p in topological_order.topological_order(
                    join(tmp_dir, sg.name))
                    ]
        except Exception:
            exception('exception when trying to analyse repository %s, returning [].' % sg.name)
            return _pkgs

        for pkg in pkgs:
            #print pkg
            e = {
                'name': pkg.name,
                'status': 'source',
                'deps': set([str(d.name) for d in pkg.build_depends]
                            + [str(d.name) for d in pkg.exec_depends]),
                'repository': sg.name,
                'url': pkg.urls[0] if pkg.urls else None,
                'description': pkg.description,
                'authors': [str(a.name) for a in pkg.authors],
                'maintainers': [str(a.name) for a in pkg.maintainers],
                'license': ', '.join(pkg.licenses)
            }
            _pkgs[pkg.name] = e

        return _pkgs

    def __checkout(self, url, branch, name, dir):
        check_call(["git", "clone", '--depth', '1',
            #'--recurse-submodules',
            '-b', branch, url, name], cwd=dir)

    def parse_package_xml(self, package):
            xml = self._distro.get_release_package_xml(package)
            root = ET.fromstring(xml)
            return dictify(root)

    def write(self, filename):
        with open(filename, 'w') as f:
            doc = {
                'repositories': dict(self._repositories),
                'pkgs': dict(self._pkgs)
            }
            f.write(dump(doc))

    def load(self, filename):
        with open(filename, 'r') as f:
            doc = load(f.read())
            self._repositories.update(doc['repositories'])
            self._pkgs.update(doc['pkgs'])

    def generate_graph(self):
        dot = pgv.AGraph(label="<Dependency Graph of Repositories>",
                         directed=True,
                         strict=True,
                         size=16,
                         splines='true',
                         compound=True,
                         ratio='expand',
                         rankdir='tb',
                         concentrate=True)
        released = dot.add_subgraph(name='released')
        non_released = dot.add_subgraph(name='non_released')

        for repo_name in sorted(self._repositories):
            repo = self._repositories[repo_name]
            info('generating node %s' % repo_name)
            pstr = ''
            for pname in sorted(repo['packages']):
                pkg = repo['packages'][pname]
                # pstr += '  %s (%s)
' % (
                #     pname, ', '.join(pkg['package']['maintainers']))
                pstr += '  %s
' % (
                    pname)
            pstr += ' '
            # maintainers = ''
            # for pn in sg['packages']:
            #     p = sg['packages']
            #     for m in p['maintainers']:
            #         maintainers += '  ' + pn + ': ' + m + '
'
            #     if p['release_status'] is None:
            #         nc = 'red'
            #     else:
            #         if p['release_status'] == 'release':
            #             nc = 'green'
            #         else:
            #             nc = 'yellow'
            #dot.add_node(k, label='<'+k.upper()+'
'+maintainers+'>', color=nc)
            label = (
                '<%s'
                '
'
                '%s>' %
                (repo_name.lower(), pstr))
            tooltip = (
                'externals dependencies: %s' %
                (', '.join(sorted(repo['external_dependencies']))))
            if repo['status'] == 'released':
                node_color = '#00AA00'
            else:
                node_color = '#AA0000'
            dot.add_node(repo_name,
                        label=label,
                        color=node_color,
                        shape='folder',
                        tooltip=tooltip,
                        URL=repo['url'])
            edge_tooltip = '\n  '.join(
                repo['requires_repositories']
            )
            for dr in repo['requires_repositories']:
                if self._repositories[dr]['status'] == 'released':
                    edge_color = '#00AA00'
                    weight = 100
                else:
                    edge_color = '#AA0000'
                    weight = 1

                dot.add_edge(
                    repo_name, dr, color=edge_color, weight=weight, penwidth=2,
                    constraint=True,
                    URL=self._repositories[dr]['url'],
                    edgetooltip=('"%s" requires:\n  %s' % (repo_name, edge_tooltip)))
        return dot

    def generate_md_package(self, pkg):
        s = '| [`%s`](%s): _%s_ | %s | %s | %s |\n' % (
            pkg['name'],
            pkg['url'] if pkg['url'] else '',
            " ".join(pkg['description'].split()) if 'description' in pkg else 'n/a',
            ', '.join(pkg['maintainers']),
            ', '.join(pkg['authors']),
            pkg['license']
        )
        # str  = '  * Depends on pkgs: '
        # for d in pkg['depends']:
        #     str += '[`%s`](#package-%s) ' % (d, d)
        # str += '\n'
        # str += '  * Maintainers: %s\n' % ', '.join(pkg['package']['maintainers'])
        # str += '  * Authors: %s\n' % ', '.join(pkg['package']['authors'])
        # str += '  * License: %s\n' % pkg['package']['license']
        return s

    def generate_rosinstall(self, repo_name):
        str = (
            "- git:\n"
            "    local-name: %s\n"
            "    uri: %s\n"
            "    version: %s\n")
        str = str % (
            repo_name,
            self._repositories[repo_name]['url'],
            self._repositories[repo_name]['version']
        )
        return str

    def generate_md_repo(self, repo_name):
        repo = self._repositories[repo_name]
        str = '---\n\n# %s\n' % repo_name

        if repo['release_version'] and repo['release_url']:
            str += '## Install from released Ubuntu packages\n'
            str += 'Install using `apt install %s`.\n\n' % (
                ' '.join(
                    ['ros-%s-%s' % (
                        self._distro_name, pname.replace('_','-')
                    ) for pname in repo['packages']])
            )
            str += ' released version: **`%s`** (via release repository: %s)\n\n' % (
                repo['release_version'], repo['release_url']
            )

        if repo['type'] == 'git':
            if repo['jenkins_job']:
                str += ' source code: %s (branch: `%s`) [![buildStatus](%s/badge/icon)](%s)\n\n' % (
                    repo['url'], repo['version'], repo['jenkins_job'], repo['jenkins_job']
                )
            else:
                str += ' source code: %s (branch: `%s`)\n\n' % (
                    repo['url'], repo['version']
                )

            if repo['status'] == 'source':
                str += '\n## Install from source\n'
                str += '\n`rosinstall` definition (including any unreleased dependencies), to be used with [`wstool`](http://wiki.ros.org/wstool):\n'
                str += '\n```\n%s' % (
                    self.generate_rosinstall(repo_name)
                )
                if repo['requires_repositories']:
                    for d in repo['requires_repositories']:
                        if self._repositories[d]['status'] != 'released':
                            str += '%s' % (
                                self.generate_rosinstall(d)
                            )
                str += '```\n'

        if repo['requires_repositories']:
            str += '\n## Repository dependencies\n'
            str += 'depends on these other repositories: '
            str += ', '.join(['[`%s`](#%s)\n' % (d, d) for d in repo['requires_repositories']])
        str += '\n\n'

        str += '\n## Included packages\n\n'
        tablehead = '| package | maintainer | authors | licence |\n'
        tableline = '| ------- | ---------- | ------- | ------- |\n'
        str += tablehead + tableline
        for p in repo['packages']:
            # str+='## Package **%s**\n*%s*\n' % (
            #     p, repo['packages'][p]['package']['description']
            # )
            str+=self.generate_md_package(repo['packages'][p])
        return str

    def generate_markdown_repos(self):
        repos = self._repositories
        # outstr = u''
        # tablehead = '| package | maintainer | authors | licence | depends on |\n'
        # tableline = '| ------- | ---------- | ------- | ------- | ---------- |\n'
        outstr = u''
        outstr += ('\n'
                   '## Install released packages\n'
                   'See the [documentation]'
                   '(https://github.com/LCAS/rosdistro/wiki'
                   '#using-the-l-cas-repository-if-you-'
                   'just-want-to-use-our-software) '
                   'to enable the Ubuntu repositories to be ready to '
                   'install binary releases. '
                   'To install all packages documented here, '
                   'simply run \n\n```\n'
                   'sudo apt install <PACKAGENAME>\n```\n\n'
                   'after having enabled the repositories.\n\n')
        # outstr += ('\n'
        #            '## Cloning all repositories\n'
        #            'Copy the following code block into the file '
        #            '`.rosinstall` in your `src/` directory of your '
        #            'workspace and run `wstool up` to pull in all '
        #            'sources at once. An easy way to do it is '
        #            '`cat >> .rosinstall` and pasting the block below, '
        #            'followed by `[Ctrl-D]`, and then runnning `wstool up`. '
        #            'In order to the install all dependencies required '
        #            'to compile the code, simply do this in your source dir:\n'
        #            '1. `rosdep update`\n'
        #            '2. `rosdep install -i --from-paths .`\n'
        #            )
        # outstr += '\n\n```\n'
        # for repo in sorted(repos):
        #     outstr += self.generate_rosinstall(repo)
        # outstr += '\n```\n\n'

        for repo in sorted(repos):
            outstr += self.generate_md_repo(repo)

        return outstr

    def preamble(self):
        return ('This is an overview of repositories and packages '
                'that form part of the distribution. '
                '_This page is autogenerated for ROS distribution `%s`._\n'
                '\n'
                '_Dependency Graph (download as [PDF](repos-%s.pdf))_\n'
                '[![repos](repos-%s.svg)](repos-%s.svg?sanitize=true)' %
                (
                    self._distro_name, self._distro_name, self._distro_name, self._distro_name
                    )
                )

def main():
    parser = argparse.ArgumentParser(
        description='analyse a rosdistro',
        epilog='(c) Marc Hanheide 2019',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter
    )
    parser.add_argument(
        '--distro', '-d',
        help='name of ROS distro, default: kinetic',
        default='kinetic'
    )
    parser.add_argument(
        '--tags', '-t',
        help='list of repository tags, space-separated',
        default='lcas'
    )
    parser.add_argument(
        '--repo-whitelist', '-r',
        help='list of whitelisted repositories, space-separated. default: all',
        default=None
    )
    parser.add_argument(
        '--write', '-w',
        help='write the gathered data to a file. default: None',
        default=None
    )
    parser.add_argument(
        '--load', '-l',
        help='load previous data from a file. default: None',
        default=None
    )
    args = parser.parse_args()

    _tags = args.tags.split(' ') if len(args.tags)>0 else []
    _repo_whitelist = args.repo_whitelist.split(' ') if args.repo_whitelist else None
    #print _orgas
    ca = CacheAnalyser(
        distro=args.distro, tags=_tags, repo_whitelist=_repo_whitelist)

    if args.load:
        ca.load(args.load)
    else:
        ca._analyse_repos()
    if args.write:
        ca.write(args.write)
    dot = ca.generate_graph()
    dot.layout(prog='dot')
    dot.draw('repos-%s.svg' % args.distro)
    dot.draw('repos-%s.pdf' % args.distro)
    dot.draw('repos-%s.png' % args.distro)

    print(ca.preamble())
    print(ca.generate_markdown_repos())

if __name__ == "__main__":
    basicConfig(level=INFO)
    main()

redpajama_set_name: RedPajamaGithub


text:
/**
 * Created with JetBrains WebStorm.
 * User: bbulic
 * Date: 27.03.13.
 * Time: 11:35
 * To change this template use File | Settings | File Templates.
 */

/*global define:false */
define(['backbone', 'jquery', 'views/MainView', 'views/NewEdit'], function (Backbone, $, MainView, NewEdit) {
    'use strict';

    var Router = Backbone.Router.extend({
        routes: {
            '': 'home',
            'edit/:id': 'edit',
            'new': 'edit'
        }
    });

    return Router;
});


text:
import os
import shutil
import sys

from pkg_resources import resource_filename

from migrate.versioning.config import *
from migrate.versioning import pathed

class Collection(pathed.Pathed):
    """A collection of templates of a specific type"""
    _mask = None

    def get_path(self, file):
        return os.path.join(self.path, str(file))

class RepositoryCollection(Collection):
    _mask = '%s'

class ScriptCollection(Collection):
    _mask = '%s.py_tmpl'

class ManageCollection(Collection):
    _mask = '%s.py_tmpl'

class SQLScriptCollection(Collection):
    _mask = '%s.py_tmpl'

class Template(pathed.Pathed):
    """Finds the paths/packages of various Migrate templates.

    :param path: Templates are loaded from migrate package
    if `path` is not provided.
    """
    pkg = 'migrate.versioning.templates'

    def __new__(cls, path=None):
        if path is None:
            path = cls._find_path(cls.pkg)
        return super(Template, cls).__new__(cls, path)

    def __init__(self, path=None):
        if path is None:
            path = Template._find_path(self.pkg)
        super(Template, self).__init__(path)
        self.repository = RepositoryCollection(os.path.join(path, 'repository'))
        self.script = ScriptCollection(os.path.join(path, 'script'))
        self.manage = ManageCollection(os.path.join(path, 'manage'))
        self.sql_script = SQLScriptCollection(os.path.join(path, 'sql_script'))

    @classmethod
    def _find_path(cls, pkg):
        """Returns absolute path to dotted python package."""
        tmp_pkg = pkg.rsplit('.', 1)

        if len(tmp_pkg) != 1:
            return resource_filename(tmp_pkg[0], tmp_pkg[1])
        else:
            return resource_filename(tmp_pkg[0], '')

    def _get_item(self, collection, theme=None):
        """Locates and returns collection.

        :param collection: name of collection to locate
        :param type_: type of subfolder in collection (defaults to "_default")
        :returns: (package, source)
        :rtype: str, str
        """
        item = getattr(self, collection)
        theme_mask = getattr(item, '_mask')
        theme = theme_mask % (theme or 'default')
        return item.get_path(theme)

    def get_repository(self, *a, **kw):
        """Calls self._get_item('repository', *a, **kw)"""
        return self._get_item('repository', *a, **kw)

    def get_script(self, *a, **kw):
        """Calls self._get_item('script', *a, **kw)"""
        return self._get_item('script', *a, **kw)

    def get_sql_script(self, *a, **kw):
        """Calls self._get_item('sql_script', *a, **kw)"""
        return self._get_item('sql_script', *a, **kw)

    def get_manage(self, *a, **kw):
        """Calls self._get_item('manage', *a, **kw)"""
        return self._get_item('manage', *a, **kw)


text:
import os
from unittest import mock

import numpy as np
import tensorflow as tf

from tensorboard import data_compat
from tensorboard import dataclass_compat
from tensorboard.backend.event_processing import plugin_event_accumulator as ea
from tensorboard.compat.proto import config_pb2
from tensorboard.compat.proto import event_pb2
from tensorboard.compat.proto import graph_pb2
from tensorboard.compat.proto import meta_graph_pb2
from tensorboard.compat.proto import summary_pb2
from tensorboard.plugins.audio import metadata as audio_metadata
from tensorboard.plugins.audio import summary as audio_summary
from tensorboard.plugins.graph import metadata as graph_metadata
from tensorboard.plugins.image import metadata as image_metadata
from tensorboard.plugins.image import summary as image_summary
from tensorboard.plugins.scalar import metadata as scalar_metadata
from tensorboard.plugins.scalar import summary as scalar_summary
from tensorboard.util import tb_logging
from tensorboard.util import tensor_util
from tensorboard.util import test_util

logger = tb_logging.get_logger()

class _EventGenerator(object):
    """Class that can add_events and then yield them back.

    Satisfies the EventGenerator API required for the EventAccumulator.
    Satisfies the EventWriter API required to create a tf.summary.FileWriter.

    Has additional convenience methods for adding test events.
    """

    def __init__(self, testcase, zero_out_timestamps=False):
        self._testcase = testcase
        self.items = []
        self.zero_out_timestamps = zero_out_timestamps
        self._initial_metadata = {}

    def Load(self):
        while self.items:
            event = self.items.pop(0)
            event = data_compat.migrate_event(event)
            events = dataclass_compat.migrate_event(
                event, self._initial_metadata
            )
            for event in events:
                yield event

    def AddScalarTensor(self, tag, wall_time=0, step=0, value=0):
        """Add a rank-0 tensor event.

        Note: This is not related to the scalar plugin; it's just a
        convenience function to add an event whose contents aren't
        important.
        """
        tensor = tensor_util.make_tensor_proto(float(value))
        event = event_pb2.Event(
            wall_time=wall_time,
            step=step,
            summary=summary_pb2.Summary(
                value=[summary_pb2.Summary.Value(tag=tag, tensor=tensor)]
            ),
        )
        self.AddEvent(event)

    def AddEvent(self, event):
        event = event_pb2.Event.FromString(event.SerializeToString())
        if self.zero_out_timestamps:
            event.wall_time = 0.0
        self.items.append(event)

    def add_event(self, event):  # pylint: disable=invalid-name
        """Match the EventWriter API."""
        self.AddEvent(event)

    def get_logdir(self):  # pylint: disable=invalid-name
        """Return a temp directory for asset writing."""
        return self._testcase.get_temp_dir()

class EventAccumulatorTest(tf.test.TestCase):
    def assertTagsEqual(self, actual, expected):
        """Utility method for checking the return value of the Tags() call.

        It fills out the `expected` arg with the default (empty) values for every
        tag type, so that the author needs only specify the non-empty values they
        are interested in testing.

        Args:
          actual: The actual Accumulator tags response.
          expected: The expected tags response (empty fields may be omitted)
        """

        empty_tags = {
            ea.GRAPH: False,
            ea.META_GRAPH: False,
            ea.RUN_METADATA: [],
            ea.TENSORS: [],
        }

        # Verifies that there are no unexpected keys in the actual response.
        # If this line fails, likely you added a new tag type, and need to update
        # the empty_tags dictionary above.
        self.assertItemsEqual(actual.keys(), empty_tags.keys())

        for key in actual:
            expected_value = expected.get(key, empty_tags[key])
            if isinstance(expected_value, list):
                self.assertItemsEqual(actual[key], expected_value)
            else:
                self.assertEqual(actual[key], expected_value)

class MockingEventAccumulatorTest(EventAccumulatorTest):
    def setUp(self):
        super(MockingEventAccumulatorTest, self).setUp()
        self.stubs = tf.compat.v1.test.StubOutForTesting()

    def tearDown(self):
        super(MockingEventAccumulatorTest, self).tearDown()
        self.stubs.CleanUp()

    def _make_accumulator(self, generator, **kwargs):
        patcher = mock.patch.object(ea, "_GeneratorFromPath", autospec=True)
        mock_impl = patcher.start()
        mock_impl.return_value = generator
        self.addCleanup(patcher.stop)
        return ea.EventAccumulator("path/is/ignored", **kwargs)

    def testEmptyAccumulator(self):
        gen = _EventGenerator(self)
        x = self._make_accumulator(gen)
        x.Reload()
        self.assertTagsEqual(x.Tags(), {})

    def testReload(self):
        """EventAccumulator contains suitable tags after calling Reload."""
        gen = _EventGenerator(self)
        acc = self._make_accumulator(gen)
        acc.Reload()
        self.assertTagsEqual(acc.Tags(), {})
        gen.AddScalarTensor("s1", wall_time=1, step=10, value=50)
        gen.AddScalarTensor("s2", wall_time=1, step=10, value=80)
        acc.Reload()
        self.assertTagsEqual(
            acc.Tags(),
            {
                ea.TENSORS: ["s1", "s2"],
            },
        )

    def testKeyError(self):
        """KeyError should be raised when accessing non-existing keys."""
        gen = _EventGenerator(self)
        acc = self._make_accumulator(gen)
        acc.Reload()
        with self.assertRaises(KeyError):
            acc.Tensors("s1")

    def testNonValueEvents(self):
        """Non-value events in the generator don't cause early exits."""
        gen = _EventGenerator(self)
        acc = self._make_accumulator(gen)
        gen.AddScalarTensor("s1", wall_time=1, step=10, value=20)
        gen.AddEvent(
            event_pb2.Event(wall_time=2, step=20, file_version="nots2")
        )
        gen.AddScalarTensor("s3", wall_time=3, step=100, value=1)

        acc.Reload()
        self.assertTagsEqual(
            acc.Tags(),
            {
                ea.TENSORS: ["s1", "s3"],
            },
        )

    def testExpiredDataDiscardedAfterRestartForFileVersionLessThan2(self):
        """Tests that events are discarded after a restart is detected.

        If a step value is observed to be lower than what was previously seen,
        this should force a discard of all previous items with the same tag
        that are outdated.

        Only file versions < 2 use this out-of-order discard logic. Later versions
        discard events based on the step value of SessionLog.START.
        """
        warnings = []
        self.stubs.Set(logger, "warning", warnings.append)

        gen = _EventGenerator(self)
        acc = self._make_accumulator(gen)

        gen.AddEvent(
            event_pb2.Event(wall_time=0, step=0, file_version="brain.Event:1")
        )
        gen.AddScalarTensor("s1", wall_time=1, step=100, value=20)
        gen.AddScalarTensor("s1", wall_time=1, step=200, value=20)
        gen.AddScalarTensor("s1", wall_time=1, step=300, value=20)
        acc.Reload()
        ## Check that number of items are what they should be
        self.assertEqual([x.step for x in acc.Tensors("s1")], [100, 200, 300])

        gen.AddScalarTensor("s1", wall_time=1, step=101, value=20)
        gen.AddScalarTensor("s1", wall_time=1, step=201, value=20)
        gen.AddScalarTensor("s1", wall_time=1, step=301, value=20)
        acc.Reload()
        ## Check that we have discarded 200 and 300 from s1
        self.assertEqual(
            [x.step for x in acc.Tensors("s1")], [100, 101, 201, 301]
        )

    def testOrphanedDataNotDiscardedIfFlagUnset(self):
        """Tests that events are not discarded if purge_orphaned_data is
        false."""
        gen = _EventGenerator(self)
        acc = self._make_accumulator(gen, purge_orphaned_data=False)

        gen.AddEvent(
            event_pb2.Event(wall_time=0, step=0, file_version="brain.Event:1")
        )
        gen.AddScalarTensor("s1", wall_time=1, step=100, value=20)
        gen.AddScalarTensor("s1", wall_time=1, step=200, value=20)
        gen.AddScalarTensor("s1", wall_time=1, step=300, value=20)
        acc.Reload()
        ## Check that number of items are what they should be
        self.assertEqual([x.step for x in acc.Tensors("s1")], [100, 200, 300])

        gen.AddScalarTensor("s1", wall_time=1, step=101, value=20)
        gen.AddScalarTensor("s1", wall_time=1, step=201, value=20)
        gen.AddScalarTensor("s1", wall_time=1, step=301, value=20)
        acc.Reload()
        ## Check that we have NOT discarded 200 and 300 from s1
        self.assertEqual(
            [x.step for x in acc.Tensors("s1")], [100, 200, 300, 101, 201, 301]
        )

    def testEventsDiscardedPerTagAfterRestartForFileVersionLessThan2(self):
        """Tests that event discards after restart, only affect the misordered
        tag.

        If a step value is observed to be lower than what was previously seen,
        this should force a discard of all previous items that are outdated, but
        only for the out of order tag. Other tags should remain unaffected.

        Only file versions < 2 use this out-of-order discard logic. Later versions
        discard events based on the step value of SessionLog.START.
        """
        warnings = []
        self.stubs.Set(logger, "warning", warnings.append)

        gen = _EventGenerator(self)
        acc = self._make_accumulator(gen)

        gen.AddEvent(
            event_pb2.Event(wall_time=0, step=0, file_version="brain.Event:1")
        )
        gen.AddScalarTensor("s1", wall_time=1, step=100, value=20)
        gen.AddScalarTensor("s2", wall_time=1, step=101, value=20)
        gen.AddScalarTensor("s1", wall_time=1, step=200, value=20)
        gen.AddScalarTensor("s2", wall_time=1, step=201, value=20)
        gen.AddScalarTensor("s1", wall_time=1, step=300, value=20)
        gen.AddScalarTensor("s2", wall_time=1, step=301, value=20)
        gen.AddScalarTensor("s1", wall_time=1, step=101, value=20)
        gen.AddScalarTensor("s3", wall_time=1, step=101, value=20)
        gen.AddScalarTensor("s1", wall_time=1, step=201, value=20)
        gen.AddScalarTensor("s1", wall_time=1, step=301, value=20)

        acc.Reload()
        ## Check that we have discarded 200 and 300 for s1
        self.assertEqual(
            [x.step for x in acc.Tensors("s1")], [100, 101, 201, 301]
        )

        ## Check that s1 discards do not affect s2 (written before out-of-order)
        ## or s3 (written after out-of-order).
        ## i.e. check that only events from the out of order tag are discarded
        self.assertEqual([x.step for x in acc.Tensors("s2")], [101, 201, 301])
        self.assertEqual([x.step for x in acc.Tensors("s3")], [101])

    def testOnlySummaryEventsTriggerDiscards(self):
        """Test that file version event does not trigger data purge."""
        gen = _EventGenerator(self)
        acc = self._make_accumulator(gen)
        gen.AddScalarTensor("s1", wall_time=1, step=100, value=20)
        ev1 = event_pb2.Event(wall_time=2, step=0, file_version="brain.Event:1")
        graph_bytes = tf.compat.v1.GraphDef().SerializeToString()
        ev2 = event_pb2.Event(wall_time=3, step=0, graph_def=graph_bytes)
        gen.AddEvent(ev1)
        gen.AddEvent(ev2)
        acc.Reload()
        self.assertEqual([x.step for x in acc.Tensors("s1")], [100])

    def testSessionLogStartMessageDiscardsExpiredEvents(self):
        """Test that SessionLog.START message discards expired events.

        This discard logic is preferred over the out-of-order step
        discard logic, but this logic can only be used for event protos
        which have the SessionLog enum, which was introduced to
        event.proto for file_version >= brain.Event:2.
        """
        gen = _EventGenerator(self)
        acc = self._make_accumulator(gen)
        slog = event_pb2.SessionLog(status=event_pb2.SessionLog.START)

        gen.AddEvent(
            event_pb2.Event(wall_time=0, step=1, file_version="brain.Event:2")
        )

        gen.AddScalarTensor("s1", wall_time=1, step=100, value=20)
        gen.AddEvent(event_pb2.Event(wall_time=1, step=100, session_log=slog))
        gen.AddScalarTensor("s1", wall_time=1, step=200, value=20)
        gen.AddScalarTensor("s1", wall_time=1, step=300, value=20)
        gen.AddScalarTensor("s1", wall_time=1, step=400, value=20)

        gen.AddScalarTensor("s2", wall_time=1, step=202, value=20)
        gen.AddScalarTensor("s2", wall_time=1, step=203, value=20)

        gen.AddEvent(event_pb2.Event(wall_time=2, step=201, session_log=slog))
        acc.Reload()
        self.assertEqual([x.step for x in acc.Tensors("s1")], [100, 200])
        self.assertEqual([x.step for x in acc.Tensors("s2")], [])

    def testFirstEventTimestamp(self):
        """Test that FirstEventTimestamp() returns wall_time of the first
        event."""
        gen = _EventGenerator(self)
        acc = self._make_accumulator(gen)
        gen.AddEvent(
            event_pb2.Event(wall_time=10, step=20, file_version="brain.Event:2")
        )
        gen.AddScalarTensor("s1", wall_time=30, step=40, value=20)
        self.assertEqual(acc.FirstEventTimestamp(), 10)

    def testReloadPopulatesFirstEventTimestamp(self):
        """Test that Reload() means FirstEventTimestamp() won't load events."""
        gen = _EventGenerator(self)
        acc = self._make_accumulator(gen)
        gen.AddEvent(
            event_pb2.Event(wall_time=1, step=2, file_version="brain.Event:2")
        )

        acc.Reload()

        def _Die(*args, **kwargs):  # pylint: disable=unused-argument
            raise RuntimeError("Load() should not be called")

        self.stubs.Set(gen, "Load", _Die)
        self.assertEqual(acc.FirstEventTimestamp(), 1)

    def testFirstEventTimestampLoadsEvent(self):
        """Test that FirstEventTimestamp() doesn't discard the loaded event."""
        gen = _EventGenerator(self)
        acc = self._make_accumulator(gen)
        gen.AddEvent(
            event_pb2.Event(wall_time=1, step=2, file_version="brain.Event:2")
        )

        self.assertEqual(acc.FirstEventTimestamp(), 1)
        acc.Reload()
        self.assertEqual(acc.file_version, 2.0)

    def testGetSourceWriter(self):
        gen = _EventGenerator(self)
        acc = self._make_accumulator(gen)
        gen.AddEvent(
            event_pb2.Event(
                wall_time=10,
                step=20,
                source_metadata=event_pb2.SourceMetadata(
                    writer="custom_writer"
                ),
            )
        )
        gen.AddScalarTensor("s1", wall_time=30, step=40, value=20)
        self.assertEqual(acc.GetSourceWriter(), "custom_writer")

    def testReloadPopulatesSourceWriter(self):
        """Test that Reload() means GetSourceWriter() won't load events."""
        gen = _EventGenerator(self)
        acc = self._make_accumulator(gen)
        gen.AddEvent(
            event_pb2.Event(
                wall_time=1,
                step=2,
                source_metadata=event_pb2.SourceMetadata(
                    writer="custom_writer"
                ),
            )
        )
        acc.Reload()

        def _Die(*args, **kwargs):  # pylint: disable=unused-argument
            raise RuntimeError("Load() should not be called")

        self.stubs.Set(gen, "Load", _Die)
        self.assertEqual(acc.GetSourceWriter(), "custom_writer")

    def testGetSourceWriterLoadsEvent(self):
        """Test that GetSourceWriter() doesn't discard the loaded event."""
        gen = _EventGenerator(self)
        acc = self._make_accumulator(gen)
        gen.AddEvent(
            event_pb2.Event(
                wall_time=1,
                step=2,
                file_version="brain.Event:2",
                source_metadata=event_pb2.SourceMetadata(
                    writer="custom_writer"
                ),
            )
        )
        self.assertEqual(acc.GetSourceWriter(), "custom_writer")
        acc.Reload()
        self.assertEqual(acc.file_version, 2.0)

    def testNewStyleScalarSummary(self):
        """Verify processing of tensorboard.plugins.scalar.summary."""
        event_sink = _EventGenerator(self, zero_out_timestamps=True)
        writer = test_util.FileWriter(self.get_temp_dir())
        writer.event_writer = event_sink
        with tf.compat.v1.Graph().as_default():
            with self.test_session() as sess:
                step = tf.compat.v1.placeholder(tf.float32, shape=[])
                scalar_summary.op(
                    "accuracy", 1.0 - 1.0 / (step + tf.constant(1.0))
                )
                scalar_summary.op("xent", 1.0 / (step + tf.constant(1.0)))
                merged = tf.compat.v1.summary.merge_all()
                writer.add_graph(sess.graph)
                for i in range(10):
                    summ = sess.run(merged, feed_dict={step: float(i)})
                    writer.add_summary(summ, global_step=i)

        accumulator = self._make_accumulator(event_sink)
        accumulator.Reload()

        tags = [
            graph_metadata.RUN_GRAPH_NAME,
            "accuracy/scalar_summary",
            "xent/scalar_summary",
        ]
        self.assertTagsEqual(
            accumulator.Tags(),
            {
                ea.TENSORS: tags,
                ea.GRAPH: True,
                ea.META_GRAPH: False,
            },
        )

        self.assertItemsEqual(
            accumulator.ActivePlugins(),
            [scalar_metadata.PLUGIN_NAME, graph_metadata.PLUGIN_NAME],
        )

    def testNewStyleAudioSummary(self):
        """Verify processing of tensorboard.plugins.audio.summary."""
        event_sink = _EventGenerator(self, zero_out_timestamps=True)
        writer = test_util.FileWriter(self.get_temp_dir())
        writer.event_writer = event_sink
        with tf.compat.v1.Graph().as_default():
            with self.test_session() as sess:
                ipt = tf.random.normal(shape=[5, 441, 2])
                with tf.name_scope("1"):
                    audio_summary.op(
                        "one", ipt, sample_rate=44100, max_outputs=1
                    )
                with tf.name_scope("2"):
                    audio_summary.op(
                        "two", ipt, sample_rate=44100, max_outputs=2
                    )
                with tf.name_scope("3"):
                    audio_summary.op(
                        "three", ipt, sample_rate=44100, max_outputs=3
                    )
                merged = tf.compat.v1.summary.merge_all()
                writer.add_graph(sess.graph)
                for i in range(10):
                    summ = sess.run(merged)
                    writer.add_summary(summ, global_step=i)

        accumulator = self._make_accumulator(event_sink)
        accumulator.Reload()

        tags = [
            graph_metadata.RUN_GRAPH_NAME,
            "1/one/audio_summary",
            "2/two/audio_summary",
            "3/three/audio_summary",
        ]
        self.assertTagsEqual(
            accumulator.Tags(),
            {
                ea.TENSORS: tags,
                ea.GRAPH: True,
                ea.META_GRAPH: False,
            },
        )

        self.assertItemsEqual(
            accumulator.ActivePlugins(),
            [audio_metadata.PLUGIN_NAME, graph_metadata.PLUGIN_NAME],
        )

    def testNewStyleImageSummary(self):
        """Verify processing of tensorboard.plugins.image.summary."""
        event_sink = _EventGenerator(self, zero_out_timestamps=True)
        writer = test_util.FileWriter(self.get_temp_dir())
        writer.event_writer = event_sink
        with tf.compat.v1.Graph().as_default():
            with self.test_session() as sess:
                ipt = tf.ones([10, 4, 4, 3], tf.uint8)
                # This is an interesting example, because the old tf.image_summary op
                # would throw an error here, because it would be tag reuse.
                # Using the tf node name instead allows argument re-use to the image
                # summary.
                with tf.name_scope("1"):
                    image_summary.op("images", ipt, max_outputs=1)
                with tf.name_scope("2"):
                    image_summary.op("images", ipt, max_outputs=2)
                with tf.name_scope("3"):
                    image_summary.op("images", ipt, max_outputs=3)
                merged = tf.compat.v1.summary.merge_all()
                writer.add_graph(sess.graph)
                for i in range(10):
                    summ = sess.run(merged)
                    writer.add_summary(summ, global_step=i)

        accumulator = self._make_accumulator(event_sink)
        accumulator.Reload()

        tags = [
            graph_metadata.RUN_GRAPH_NAME,
            "1/images/image_summary",
            "2/images/image_summary",
            "3/images/image_summary",
        ]
        self.assertTagsEqual(
            accumulator.Tags(),
            {
                ea.TENSORS: tags,
                ea.GRAPH: True,
                ea.META_GRAPH: False,
            },
        )

        self.assertItemsEqual(
            accumulator.ActivePlugins(),
            [image_metadata.PLUGIN_NAME, graph_metadata.PLUGIN_NAME],
        )

    def testTFSummaryTensor(self):
        """Verify processing of tf.summary.tensor."""
        event_sink = _EventGenerator(self, zero_out_timestamps=True)
        writer = test_util.FileWriter(self.get_temp_dir())
        writer.event_writer = event_sink
        with tf.compat.v1.Graph().as_default():
            with self.test_session() as sess:
                tensor_summary = tf.compat.v1.summary.tensor_summary
                tensor_summary("scalar", tf.constant(1.0))
                tensor_summary("vector", tf.constant([1.0, 2.0, 3.0]))
                tensor_summary("string", tf.constant(b"foobar"))
                merged = tf.compat.v1.summary.merge_all()
                summ = sess.run(merged)
                writer.add_summary(summ, 0)

        accumulator = self._make_accumulator(event_sink)
        accumulator.Reload()

        self.assertTagsEqual(
            accumulator.Tags(),
            {
                ea.TENSORS: ["scalar", "vector", "string"],
            },
        )

        scalar_proto = accumulator.Tensors("scalar")[0].tensor_proto
        scalar = tensor_util.make_ndarray(scalar_proto)
        vector_proto = accumulator.Tensors("vector")[0].tensor_proto
        vector = tensor_util.make_ndarray(vector_proto)
        string_proto = accumulator.Tensors("string")[0].tensor_proto
        string = tensor_util.make_ndarray(string_proto)

        self.assertTrue(np.array_equal(scalar, 1.0))
        self.assertTrue(np.array_equal(vector, [1.0, 2.0, 3.0]))
        self.assertTrue(np.array_equal(string, b"foobar"))

        self.assertItemsEqual(accumulator.ActivePlugins(), [])

    def _testTFSummaryTensor_SizeGuidance(
        self, plugin_name, tensor_size_guidance, steps, expected_count
    ):
        event_sink = _EventGenerator(self, zero_out_timestamps=True)
        writer = test_util.FileWriter(self.get_temp_dir())
        writer.event_writer = event_sink
        with tf.compat.v1.Graph().as_default():
            with self.test_session() as sess:
                summary_metadata = summary_pb2.SummaryMetadata(
                    plugin_data=summary_pb2.SummaryMetadata.PluginData(
                        plugin_name=plugin_name, content=b"{}"
                    )
                )
                tf.compat.v1.summary.tensor_summary(
                    "scalar",
                    tf.constant(1.0),
                    summary_metadata=summary_metadata,
                )
                merged = tf.compat.v1.summary.merge_all()
                for step in range(steps):
                    writer.add_summary(sess.run(merged), global_step=step)

        accumulator = self._make_accumulator(
            event_sink, tensor_size_guidance=tensor_size_guidance
        )
        accumulator.Reload()

        tensors = accumulator.Tensors("scalar")
        self.assertEqual(len(tensors), expected_count)

    def testTFSummaryTensor_SizeGuidance_DefaultToTensorGuidance(self):
        self._testTFSummaryTensor_SizeGuidance(
            plugin_name="jabberwocky",
            tensor_size_guidance={},
            steps=ea.DEFAULT_SIZE_GUIDANCE[ea.TENSORS] + 1,
            expected_count=ea.DEFAULT_SIZE_GUIDANCE[ea.TENSORS],
        )

    def testTFSummaryTensor_SizeGuidance_UseSmallSingularPluginGuidance(self):
        size = int(ea.DEFAULT_SIZE_GUIDANCE[ea.TENSORS] / 2)
        assert size < ea.DEFAULT_SIZE_GUIDANCE[ea.TENSORS], size
        self._testTFSummaryTensor_SizeGuidance(
            plugin_name="jabberwocky",
            tensor_size_guidance={"jabberwocky": size},
            steps=ea.DEFAULT_SIZE_GUIDANCE[ea.TENSORS] + 1,
            expected_count=size,
        )

    def testTFSummaryTensor_SizeGuidance_UseLargeSingularPluginGuidance(self):
        size = ea.DEFAULT_SIZE_GUIDANCE[ea.TENSORS] + 5
        self._testTFSummaryTensor_SizeGuidance(
            plugin_name="jabberwocky",
            tensor_size_guidance={"jabberwocky": size},
            steps=ea.DEFAULT_SIZE_GUIDANCE[ea.TENSORS] + 10,
            expected_count=size,
        )

    def testTFSummaryTensor_SizeGuidance_IgnoreIrrelevantGuidances(self):
        size_small = int(ea.DEFAULT_SIZE_GUIDANCE[ea.TENSORS] / 3)
        size_large = int(ea.DEFAULT_SIZE_GUIDANCE[ea.TENSORS] / 2)
        assert size_small < size_large < ea.DEFAULT_SIZE_GUIDANCE[ea.TENSORS], (
            size_small,
            size_large,
        )
        self._testTFSummaryTensor_SizeGuidance(
            plugin_name="jabberwocky",
            tensor_size_guidance={
                "jabberwocky": size_small,
                "wnoorejbpxl": size_large,
            },
            steps=ea.DEFAULT_SIZE_GUIDANCE[ea.TENSORS] + 1,
            expected_count=size_small,
        )

class RealisticEventAccumulatorTest(EventAccumulatorTest):
    def testTensorsRealistically(self):
        """Test accumulator by writing values and then reading them."""

        def FakeScalarSummary(tag, value):
            value = summary_pb2.Summary.Value(tag=tag, simple_value=value)
            summary = summary_pb2.Summary(value=[value])
            return summary

        directory = os.path.join(self.get_temp_dir(), "values_dir")
        if tf.io.gfile.isdir(directory):
            tf.io.gfile.rmtree(directory)
        tf.io.gfile.mkdir(directory)

        writer = test_util.FileWriter(directory, max_queue=100)

        with tf.Graph().as_default() as graph:
            _ = tf.constant([2.0, 1.0])
            # Add a graph to the summary writer.
            writer.add_graph(graph)
            graph_def = graph.as_graph_def(add_shapes=True)
            meta_graph_def = tf.compat.v1.train.export_meta_graph(
                graph_def=graph_def
            )
            writer.add_meta_graph(meta_graph_def)

        run_metadata = config_pb2.RunMetadata()
        device_stats = run_metadata.step_stats.dev_stats.add()
        device_stats.device = "test device"
        writer.add_run_metadata(run_metadata, "test run")

        # Write a bunch of events using the writer.
        for i in range(30):
            summ_id = FakeScalarSummary("id", i)
            summ_sq = FakeScalarSummary("sq", i * i)
            writer.add_summary(summ_id, i * 5)
            writer.add_summary(summ_sq, i * 5)
        writer.flush()

        # Verify that we can load those events properly
        acc = ea.EventAccumulator(directory)
        acc.Reload()
        self.assertTagsEqual(
            acc.Tags(),
            {
                ea.TENSORS: [
                    graph_metadata.RUN_GRAPH_NAME,
                    "id",
                    "sq",
                    "test run",
                ],
                ea.GRAPH: True,
                ea.META_GRAPH: True,
                ea.RUN_METADATA: [],
            },
        )
        id_events = acc.Tensors("id")
        sq_events = acc.Tensors("sq")
        self.assertEqual(30, len(id_events))
        self.assertEqual(30, len(sq_events))
        for i in range(30):
            self.assertEqual(i * 5, id_events[i].step)
            self.assertEqual(i * 5, sq_events[i].step)
            self.assertEqual(
                i, tensor_util.make_ndarray(id_events[i].tensor_proto).item()
            )
            self.assertEqual(
                i * i,
                tensor_util.make_ndarray(sq_events[i].tensor_proto).item(),
            )

        # Write a few more events to test incremental reloading
        for i in range(30, 40):
            summ_id = FakeScalarSummary("id", i)
            summ_sq = FakeScalarSummary("sq", i * i)
            writer.add_summary(summ_id, i * 5)
            writer.add_summary(summ_sq, i * 5)
        writer.flush()

        # Verify we can now see all of the data
        acc.Reload()
        id_events = acc.Tensors("id")
        sq_events = acc.Tensors("sq")
        self.assertEqual(40, len(id_events))
        self.assertEqual(40, len(sq_events))
        for i in range(40):
            self.assertEqual(i * 5, id_events[i].step)
            self.assertEqual(i * 5, sq_events[i].step)
            self.assertEqual(
                i, tensor_util.make_ndarray(id_events[i].tensor_proto).item()
            )
            self.assertEqual(
                i * i,
                tensor_util.make_ndarray(sq_events[i].tensor_proto).item(),
            )

        expected_graph_def = graph_pb2.GraphDef.FromString(
            graph.as_graph_def(add_shapes=True).SerializeToString()
        )
        self.assertProtoEquals(expected_graph_def, acc.Graph())
        self.assertProtoEquals(
            expected_graph_def,
            graph_pb2.GraphDef.FromString(acc.SerializedGraph()),
        )

        expected_meta_graph = meta_graph_pb2.MetaGraphDef.FromString(
            meta_graph_def.SerializeToString()
        )
        self.assertProtoEquals(expected_meta_graph, acc.MetaGraph())

    def testGraphFromMetaGraphBecomesAvailable(self):
        """Test accumulator by writing values and then reading them."""

        directory = os.path.join(
            self.get_temp_dir(), "metagraph_test_values_dir"
        )
        if tf.io.gfile.isdir(directory):
            tf.io.gfile.rmtree(directory)
        tf.io.gfile.mkdir(directory)

        writer = test_util.FileWriter(directory, max_queue=100)

        with tf.Graph().as_default() as graph:
            _ = tf.constant([2.0, 1.0])
            # Add a graph to the summary writer.
            graph_def = graph.as_graph_def(add_shapes=True)
            meta_graph_def = tf.compat.v1.train.export_meta_graph(
                graph_def=graph_def
            )
            writer.add_meta_graph(meta_graph_def)
            writer.flush()

        # Verify that we can load those events properly
        acc = ea.EventAccumulator(directory)
        acc.Reload()
        self.assertTagsEqual(
            acc.Tags(),
            {
                ea.GRAPH: True,
                ea.META_GRAPH: True,
            },
        )

        expected_graph_def = graph_pb2.GraphDef.FromString(
            graph.as_graph_def(add_shapes=True).SerializeToString()
        )
        self.assertProtoEquals(expected_graph_def, acc.Graph())
        self.assertProtoEquals(
            expected_graph_def,
            graph_pb2.GraphDef.FromString(acc.SerializedGraph()),
        )

        expected_meta_graph = meta_graph_pb2.MetaGraphDef.FromString(
            meta_graph_def.SerializeToString()
        )
        self.assertProtoEquals(expected_meta_graph, acc.MetaGraph())

    def _writeMetadata(self, logdir, summary_metadata, nonce=""):
        """Write to disk a summary with the given metadata.

        Arguments:
          logdir: a string
          summary_metadata: a `SummaryMetadata` protobuf object
          nonce: optional; will be added to the end of the event file name
            to guarantee that multiple calls to this function do not stomp the
            same file
        """

        summary = summary_pb2.Summary()
        summary.value.add(
            tensor=tensor_util.make_tensor_proto(
                ["po", "ta", "to"], dtype=tf.string
            ),
            tag="you_are_it",
            metadata=summary_metadata,
        )
        writer = test_util.FileWriter(logdir, filename_suffix=nonce)
        writer.add_summary(summary.SerializeToString())
        writer.close()

    def testSummaryMetadata(self):
        logdir = self.get_temp_dir()
        summary_metadata = summary_pb2.SummaryMetadata(
            display_name="current tagee",
            summary_description="no",
            plugin_data=summary_pb2.SummaryMetadata.PluginData(
                plugin_name="outlet"
            ),
        )
        self._writeMetadata(logdir, summary_metadata)
        acc = ea.EventAccumulator(logdir)
        acc.Reload()
        self.assertProtoEquals(
            summary_metadata, acc.SummaryMetadata("you_are_it")
        )

    def testSummaryMetadata_FirstMetadataWins(self):
        logdir = self.get_temp_dir()
        summary_metadata_1 = summary_pb2.SummaryMetadata(
            display_name="current tagee",
            summary_description="no",
            plugin_data=summary_pb2.SummaryMetadata.PluginData(
                plugin_name="outlet", content=b"120v"
            ),
        )
        self._writeMetadata(logdir, summary_metadata_1, nonce="1")
        acc = ea.EventAccumulator(logdir)
        acc.Reload()
        summary_metadata_2 = summary_pb2.SummaryMetadata(
            display_name="tagee of the future",
            summary_description="definitely not",
            plugin_data=summary_pb2.SummaryMetadata.PluginData(
                plugin_name="plug", content=b"110v"
            ),
        )
        self._writeMetadata(logdir, summary_metadata_2, nonce="2")
        acc.Reload()

        self.assertProtoEquals(
            summary_metadata_1, acc.SummaryMetadata("you_are_it")
        )

    def testPluginTagToContent_PluginsCannotJumpOnTheBandwagon(self):
        # If there are multiple `SummaryMetadata` for a given tag, and the
        # set of plugins in the `plugin_data` of second is different from
        # that of the first, then the second set should be ignored.
        logdir = self.get_temp_dir()
        summary_metadata_1 = summary_pb2.SummaryMetadata(
            display_name="current tagee",
            summary_description="no",
            plugin_data=summary_pb2.SummaryMetadata.PluginData(
                plugin_name="outlet", content=b"120v"
            ),
        )
        self._writeMetadata(logdir, summary_metadata_1, nonce="1")
        acc = ea.EventAccumulator(logdir)
        acc.Reload()
        summary_metadata_2 = summary_pb2.SummaryMetadata(
            display_name="tagee of the future",
            summary_description="definitely not",
            plugin_data=summary_pb2.SummaryMetadata.PluginData(
                plugin_name="plug", content=b"110v"
            ),
        )
        self._writeMetadata(logdir, summary_metadata_2, nonce="2")
        acc.Reload()

        self.assertEqual(
            acc.PluginTagToContent("outlet"), {"you_are_it": b"120v"}
        )
        with self.assertRaisesRegex(KeyError, "plug"):
            acc.PluginTagToContent("plug")
        self.assertItemsEqual(acc.ActivePlugins(), ["outlet"])

if __name__ == "__main__":
    tf.test.main()


text:
package org.join.ws.ui.widget;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

/**
 * @brief 文件浏览组件
 * @author join
 */
public class FileBrowser extends ListView implements android.widget.AdapterView.OnItemClickListener {

    static final String TAG = "FileBrowser";

    private final String namespace = "http://github.com/joinAero/AndroidWebServ/";

    /** 根目录 */
    private String rootDir;

    /** 上级目录名称 */
    private String parentDirName = ". .";

    /**
     * @brief 保存当前目录中所有的File对象(每一个File对象表示目录或文件)
     */
    private List<File> fileList = new ArrayList<File>();

    /**
     * @brief 当前显示的目录
     */
    private File currentDir;

    /**
     * @brief folder_def属性值
     * @details 表示目录的默认图片资源id。
     */
    private int folderResId;

    /**
     * @brief folder_parent属性值
     * @details 表示上级目录的图片资源id。
     */
    private int folderParentResId;

    /**
     * @brief file_def属性值
     * @details 表示文件的默认图片资源id。
     */
    private int fileResId;

    /**
     * @brief 文件对应扩展名的图片资源id集合
     * @details key表示文件扩展名,value表示该扩展名对应的图像资源ID。
     */
    private Map<String, Integer> extResIdMap = new HashMap<String, Integer>();

    /**
     * @brief display属性值。
     * @details true:只显示当前目录中的文件列表。
     */
    private int display = DISPLAY_ALL;

    public static final int DISPLAY_ALL = -1;
    public static final int DISPLAY_FILE = 0;
    public static final int DISPLAY_FOLDER = 1;

    /**
     * @brief back_resp属性值
     * @details true:响应返回键回上级目录。
     */
    private boolean isBackResp;

    /**
     * @brief sort属性值
     * @details true:以上一级、文件夹、文件顺序排序。
     */
    private boolean isSort;

    private FileListAdapter mListAdapter;
    private OnBrowserListener mBrowserListener;

    public FileBrowser(Context context) {
        super(context);
        initFileBrowser(context);
    }

    public FileBrowser(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FileBrowser(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs);
        initFileBrowser(context);

        folderResId = attrs.getAttributeResourceValue(namespace, "folder_def", 0);
        folderParentResId = attrs
                .getAttributeResourceValue(namespace, "folder_parent", folderResId);
        fileResId = attrs.getAttributeResourceValue(namespace, "file_def", 0);
        String value = attrs.getAttributeValue(namespace, "display");
        if (value != null) {
            if (value.equals("file")) {
                display = DISPLAY_FILE;
            } else if (value.equals("folder")) {
                display = DISPLAY_FOLDER;
            }
        }
        isBackResp = attrs.getAttributeBooleanValue(namespace, "back_resp", false);
        isSort = attrs.getAttributeBooleanValue(namespace, "sort", true);

        // 动态扩展名属性
        int index = 1;
        while (true) {
            String extName = attrs.getAttributeValue(namespace, "ext_name" + index);
            int fileImageResId = attrs.getAttributeResourceValue(namespace, "ext_image" + index, 0);
            // 如果读取不到extName或fileImage属性时跳出循环
            if ("".equals(extName) || extName == null || fileImageResId == 0) {
                break;
            }
            extResIdMap.put(extName, fileImageResId);
            index++;
        }
    }

    private void initFileBrowser(Context context) {
        setOnItemClickListener(this);

        mListAdapter = new FileListAdapter(context);
        setAdapter(mListAdapter);

        rootDir = "/"; // 根目录设为'/'
        browse(rootDir); // 初始浏览根目录
    }

    /**
     * @brief 浏览某一目录
     * @param dir 目录
     */
    public void browse(String dir) {
        File file = new File(dir);
        if (!file.exists() || !file.isDirectory() || !file.canRead()) {
            Log.e(TAG, dir + " access denied!");
            return;
        }
        currentDir = file;
        refresh();
    }

    /**
     * @brief 刷新显示内容
     */
    public void refresh() {
        updateFiles();
        mListAdapter.notifyDataSetChanged();
    }

    /**
     * @brief 获得文件后缀名
     */
    private String getExtName(String filename) {
        int position = filename.lastIndexOf(".");
        if (position >= 0)
            return filename.substring(position + 1).toLowerCase();
        else
            return "";
    }

    /**
     * @brief 扫描当前目录,并将当前目录的File对象集合添加到fileList变量中
     * @return true:扫描目录有成功;false:扫描目录有错误。
     */
    private void updateFiles() {
        fileList.clear();

        // 非根目录时,增加null以显示一个".."
        if (!isRootDir())
            fileList.add(null);

        File[] files = currentDir.listFiles();

        for (File file : files) {
            if (display == DISPLAY_ALL) {
                fileList.add(file);
            } else if (display == DISPLAY_FILE && file.isFile()) {
                fileList.add(file);
            } else if (display == DISPLAY_FOLDER && file.isDirectory()) {
                fileList.add(file);
            }
        }

        // 排序
        if (isSort) {
            sortFileList(fileList);
        }
    }

    private boolean isRootDir() {
        return currentDir.getPath().equals(rootDir);
    }

    private void sortFileList(List<File> list) {
        Collections.sort(list, new Comparator<File>() {
            @Override
            public int compare(File f1, File f2) {
                if (null == f1) {
                    return -1;
                } else if (null == f2) {
                    return 1;
                } else if (f1.isDirectory() && !f2.isDirectory()) {
                    return -1;
                } else if (!f1.isDirectory() && f2.isDirectory()) {
                    return 1;
                } else {
                    return f1.toString().compareToIgnoreCase(f2.toString());
                }
            }
        });
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        File file = fileList.get(position);
        if (file == null) { // 上级时
            toParentDir();
        } else if (file.isDirectory()) { // 目录时
            if (file.exists() && file.canRead()) {
                currentDir = file;
                refresh(); // 刷新
                if (mBrowserListener != null) {
                    mBrowserListener.onDirItemClick(currentDir.getPath());
                }
            } else {
                Toast.makeText(getContext(), "Access denied!", Toast.LENGTH_SHORT).show();
            }
        } else { // 文件时
            if (mBrowserListener != null) {
                mBrowserListener.onFileItemClick(file.getPath());
            }
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        // 如果按下的是返回键、相应返回上级目录且不是根目录
        if (isBackResp && keyCode == KeyEvent.KEYCODE_BACK && !isRootDir()) {
            toParentDir();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    private void toParentDir() {
        currentDir = currentDir.getParentFile();
        refresh(); // 刷新
        if (mBrowserListener != null) {
            mBrowserListener.onDirItemClick(currentDir.getPath());
        }
    }

    /**
     * @brief 浏览监听接口
     * @author join
     */
    public interface OnBrowserListener {

        /**
         * @brief 文件点击
         * @param filename 文件名
         */
        public void onFileItemClick(String filename);

        /**
         * @brief 目录点击
         * @param path 目录路径
         */
        public void onDirItemClick(String path);

    }

    /**
     * @brief 设定文件浏览监听接口
     */
    public void setOnBrowserListener(OnBrowserListener listener) {
        this.mBrowserListener = listener;
    }

    /**
     * @brief 自定义Adapter类
     */
    private class FileListAdapter extends BaseAdapter {

        private Context context;

        public FileListAdapter(Context context) {
            this.context = context;
        }

        @Override
        public int getCount() {
            return fileList.size();
        }

        @Override
        public Object getItem(int position) {
            return fileList.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            return createView(position, convertView);
        }

        private View createView(int position, View convertView) {
            View v;
            if (convertView == null) {
                LinearLayout fileLayout = new LinearLayout(context);
                fileLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
                        LayoutParams.WRAP_CONTENT));
                fileLayout.setOrientation(LinearLayout.HORIZONTAL);
                fileLayout.setGravity(Gravity.CENTER_VERTICAL);
                fileLayout.setPadding(5, 10, 0, 10);

                ImageView ivFile = new ImageView(context);
                ivFile.setTag(1);
                ivFile.setLayoutParams(new LayoutParams(48, 48));

                TextView tvFile = new TextView(context);
                tvFile.setTag(2);
                tvFile.setTextColor(android.graphics.Color.WHITE);
                tvFile.setTextAppearance(context, android.R.style.TextAppearance_Large);
                tvFile.setPadding(5, 5, 0, 0);
                tvFile.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
                        LayoutParams.WRAP_CONTENT));

                fileLayout.addView(ivFile);
                fileLayout.addView(tvFile);

                v = fileLayout;
            } else {
                v = convertView;
            }
            bindView(position, v);
            return v;
        }

        private void bindView(int position, View view) {
            ImageView ivFile = (ImageView) view.findViewWithTag(1);
            TextView tvFile = (TextView) view.findViewWithTag(2);

            if (fileList.get(position) == null) { // 上一级
                if (folderResId > 0)
                    ivFile.setImageResource(folderParentResId);
                tvFile.setText(parentDirName);
            } else if (fileList.get(position).isDirectory()) { // 文件夹时
                if (folderResId > 0)
                    ivFile.setImageResource(folderResId);
                tvFile.setText(fileList.get(position).getName());
            } else { // 文件时
                tvFile.setText(fileList.get(position).getName());
                Integer resId = extResIdMap.get(getExtName(fileList.get(position).getName()));
                if (resId != null && resId > 0) {
                    ivFile.setImageResource(resId);
                } else if (fileResId > 0) {
                    ivFile.setImageResource(fileResId);
                }
            }
        }

    }

}