class Hasher implements Fingerprinter
    {
        // temporary buffer used for digest. Instance member for performance reasons.
        private final byte[] buffer = new byte[8192];

        @Override
        public byte[] print(File path)
        {
            InputStream input = stream(path);
            if (input != null) {
                try {
                    return new ZDigest(buffer).update(input).data();
                }
                catch (IOException e) {
                    return null;
                }
                finally {
                    try {
                        input.close();
                    }
                    catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return null;
        }

        private InputStream stream(File path)
        {
            if (path.isFile()) {
                try {
                    return new FileInputStream(path);
                }
                catch (FileNotFoundException e) {
                    return null;
                }
            }
            else if (path.isDirectory()) {
                List<String> list = Arrays.asList(path.list());
                Collections.sort(list);
                return new ByteArrayInputStream(list.toString().getBytes(ZMQ.CHARSET));
            }
            return null;
        }
    }

    private interface IFileVisitor
    {
        /**
         * Visits a file.
         * @param file the file to visit.
         * @return true to stop the traversal, false to continue.
         */
        boolean visitFile(File file);

        /**
         * Visits a directory.
         * @param dir the directory to visit.
         * @return true to stop the traversal, false to continue.
         */
        boolean visitDir(File dir);
    }

    //  Directory location
    private final File location;

    // the scanned files (and directories) along with their fingerprint
    private final Map<File, byte[]> fingerprints = new HashMap<>();

    // collected public keys
    private final Map<String, ZMetadata> publicKeys = new HashMap<>();

    private final Fingerprinter finger;

    /**
     * Create a Certificate Store at that file system folder location
     * @param location
     */
    public ZCertStore(String location)
    {
        this(location, new Timestamper());
    }

    public ZCertStore(String location, Fingerprinter fingerprinter)
    {
        this.finger = fingerprinter;
        this.location = new File(location);
        loadFiles();
    }

    private boolean traverseDirectory(File root, IFileVisitor visitor)
    {
        assert (root.exists());
        assert (root.isDirectory());

        if (visitor.visitDir(root)) {
            return true;
        }
        for (File file : root.listFiles()) {
            if (file.isFile()) {
                if (visitor.visitFile(file)) {
                    return true;
                }
            }
            else if (file.isDirectory()) {
                boolean rc = traverseDirectory(file, visitor);
                if (rc) {
                    return true;
                }
            }
            else {
                System.out.printf(
                                  "WARNING: %s is neither file nor directory? This shouldn't happen....SKIPPING%n",
                                  file.getAbsolutePath());
            }
        }
        return false;
    }

    /**
     * Check if a public key is in the certificate store.
     * @param publicKey needs to be a 32 byte array representing the public key
     */
    public boolean containsPublicKey(byte[] publicKey)
    {
        Utils.checkArgument(
                            publicKey.length == 32,
                            "publickey needs to have a size of 32 bytes. got only " + publicKey.length);
        return containsPublicKey(ZMQ.Curve.z85Encode(publicKey));
    }

    /**
     * check if a z85-based public key is in the certificate store.
     * This method will scan the folder for changes on every call
     * @param publicKey
     */
    public boolean containsPublicKey(String publicKey)
    {
        Utils.checkArgument(
                            publicKey.length() == 40,
                            "z85 publickeys should have a length of 40 bytes but got " + publicKey.length());
        reloadIfNecessary();
        return publicKeys.containsKey(publicKey);
    }

    public ZMetadata getMetadata(String publicKey)
    {
        reloadIfNecessary();
        return publicKeys.get(publicKey);
    }

    private void loadFiles()
    {
        final Map<String, ZMetadata> keys = new HashMap<>();
        if (!location.exists()) {
            location.mkdirs();
        }
        final Map<File, byte[]> collected = new HashMap<>();

        traverseDirectory(location, new IFileVisitor()
        {
            @Override
            public boolean visitFile(File file)
            {<FILL_FUNCTION_BODY>

                try {
                    ZConfig zconf = ZConfig.load(file.getAbsolutePath());
                    String publicKey = zconf.getValue("curve/public-key");
                    if (publicKey == null) {
                        System.out.printf(
                                          "Warning!! File %s has no curve/public-key-element. SKIPPING!%n",
                                          file.getAbsolutePath());
                        return false;
                    }
                    if (publicKey.length() == 32) { // we want to store the public-key as Z85-String
                        publicKey = ZMQ.Curve.z85Encode(publicKey.getBytes(ZMQ.CHARSET));
                    }
                    assert (publicKey.length() == 40);
                    keys.put(publicKey, ZMetadata.read(zconf));
                    collected.put(file, finger.print(file));
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
                return false;
            
<no_super_class>