switch(rule) {
            case "*":
            case "*.*":
                return true;
        }

package org.develnext.jphp.zend.ext.standard;

import php.runtime.Information;
import php.runtime.Memory;
import php.runtime.common.Constants;
import php.runtime.common.StringUtils;
import php.runtime.env.Environment;
import php.runtime.env.TraceInfo;
import php.runtime.ext.core.classes.lib.ItemsUtils;
import php.runtime.ext.core.classes.stream.Stream;
import php.runtime.ext.core.classes.stream.WrapIOException;
import php.runtime.ext.support.compile.FunctionsContainer;
import php.runtime.invoke.ObjectInvokeHelper;
import php.runtime.memory.*;
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.util.*;
import static org.develnext.jphp.zend.ext.standard.FileConstants.*;
import static php.runtime.annotation.Runtime.Immutable;

public class FileFunctions extends FunctionsContainer {

    @Immutable
    public static String basename(String path, String suffix) {
        String result = new File(path).getName();
        if (suffix != null && !suffix.isEmpty() && result.endsWith(suffix))
            result = result.substring(0, result.length() - suffix.length());
        return result;
    }

    @Immutable
    public static String basename(String path) {
        return basename(path, null);
    }

    public static boolean chgrp(String fileName, Memory group) {
        return false;
    }

    public static boolean copy(Environment env, TraceInfo trace, String source, String dest) throws Throwable {
        Stream stream = Stream.create(env, source, "r");
        if (stream == null) {
            env.warning("copy(): Invalid source path");
            return false;
        }
        Memory value = stream.readFully(env);
        RandomAccessFile outputStream;
        try {
            outputStream = new RandomAccessFile(dest, "rw");
        } catch (FileNotFoundException e) {
            return false;
        }
        try {
            outputStream.setLength(0);
            outputStream.write(value.getBinaryBytes(env.getDefaultCharset()));
            return true;
        } catch (IOException e) {
            env.warning("copy(): " + e.getMessage());
            return false;
        } finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                return false;
            }
        }
    }

    @Immutable
    public static String dirname(String path) {
        String r = new File(path).getParent();
        if (r == null)
            return "";
        return r;
    }

    public static Memory disk_free_space(String path) {
        return LongMemory.valueOf(new File(path).getFreeSpace());
    }

    public static Memory disk_total_space(String path) {
        return LongMemory.valueOf(new File(path).getTotalSpace());
    }

    public static Memory diskfreespace(String path) {
        return disk_free_space(path);
    }

    public static boolean file_exists(String path) {
        return new File(path).getAbsoluteFile().exists();
    }

    public static Memory file(Environment env, TraceInfo trace, String path, int flags) throws Throwable {
        return file(env, trace, path, flags, Memory.NULL);
    }

    public static Memory file(Environment env, TraceInfo trace, String path) throws Throwable {
        return file(env, trace, path, 0, Memory.NULL);
    }

    public static Memory file(Environment env, TraceInfo trace, String path, int flags, Memory context) throws Throwable {
        Stream stream = null;
        try {
            stream = Stream.create(env, path, "r");
            if (stream == null) {
                env.warning(trace, "file(): failed to open stream");
                return Memory.FALSE;
            }
            stream.setContext(env, context);
            Memory value = env.invokeMethod(trace, stream, "readFully");
            byte[] bytes = value.getBinaryBytes(env.getDefaultCharset());
            ArrayMemory result = new ArrayMemory();
            int prev = 0;
            boolean ignoreNewLines = (flags & FileConstants.FILE_IGNORE_NEW_LINES) == FileConstants.FILE_IGNORE_NEW_LINES;
            int i;
            for (i = 0; i < bytes.length; i++) {
                byte ch = bytes[i];
                if (ch == '\n') {
                    if (prev == i && (flags & FileConstants.FILE_SKIP_EMPTY_LINES) == FileConstants.FILE_SKIP_EMPTY_LINES) {
                        prev += 1;
                        continue;
                    }
                    byte[] chunk = ignoreNewLines ? Arrays.copyOfRange(bytes, prev, i - 1) : Arrays.copyOfRange(bytes, prev, i);
                    prev = i + 1;
                    result.add(new BinaryMemory(chunk));
                }
            }
            if (prev != i) {
                byte[] chunk = Arrays.copyOfRange(bytes, prev, i);
                result.add(new BinaryMemory(chunk));
            }
            return result.toConstant();
        } catch (WrapIOException e) {
            if (stream == null && (flags & FileConstants.FILE_USE_INCLUDE_PATH) == FileConstants.FILE_USE_INCLUDE_PATH) {
                path = env.findInIncludePaths(path);
                if (path != null)
                    return file(env, trace, path, flags ^ FileConstants.FILE_USE_INCLUDE_PATH, context);
            }
            env.warning(trace, "file(): " + e.getMessage());
            return Memory.FALSE;
        } finally {
            if (stream != null)
                stream.close(env);
        }
    }

    public static Memory file_get_contents(Environment env, TraceInfo trace, String path, boolean useIncludePaths, Memory context, Memory offset, Memory maxLength) throws Throwable {
        Stream stream = null;
        try {
            stream = Stream.create(env, path, "r");
            if (stream == null) {
                env.warning(trace, "file_get_contents(): failed to open stream");
                return Memory.FALSE;
            }
            stream.setContext(env, context);
            if (offset.toLong() > 0)
                stream.seek(env, offset);
            if (maxLength.isNull())
                return stream.readFully(env, LongMemory.valueOf(4096));
            else
                return stream.read(env, maxLength);
        } catch (WrapIOException | IOException e) {
            if (stream == null && useIncludePaths) {
                path = env.findInIncludePaths(path);
                if (path != null)
                    return file_get_contents(env, trace, path, false, context, offset, maxLength);
            }
            env.warning(trace, "file_get_contents(): " + e.getMessage());
            return Memory.FALSE;
        } finally {
            if (stream != null)
                stream.close(env);
        }
    }

    public static Memory file_get_contents(Environment env, TraceInfo trace, String path, boolean useIncludePaths, Memory context, Memory offset) throws Throwable {
        return file_get_contents(env, trace, path, useIncludePaths, context, offset, Memory.NULL);
    }

    public static Memory file_get_contents(Environment env, TraceInfo trace, String path, boolean useIncludePaths, Memory context) throws Throwable {
        return file_get_contents(env, trace, path, useIncludePaths, context, Memory.CONST_INT_M1, Memory.NULL);
    }

    public static Memory file_get_contents(Environment env, TraceInfo trace, String path, boolean useIncludePaths) throws Throwable {
        return file_get_contents(env, trace, path, useIncludePaths, Memory.NULL, Memory.CONST_INT_M1, Memory.NULL);
    }

    public static Memory file_get_contents(Environment env, TraceInfo trace, String path) throws Throwable {
        return file_get_contents(env, trace, path, true, Memory.NULL, Memory.CONST_INT_M1, Memory.NULL);
    }

    public static Memory file_put_contents(Environment env, TraceInfo trace, String path, Memory data, int flags, Memory context) throws Throwable {
        Stream stream = null;
        try {
            String mode = "w";
            if ((flags & FileConstants.FILE_APPEND) == FileConstants.FILE_APPEND)
                mode = "a";
            stream = Stream.create(env, path, mode);
            if (stream == null) {
                env.warning(trace, "file_put_contents(): failed to open stream");
                return Memory.FALSE;
            }
            stream.setContext(env, context);
            if (data.instanceOf(Stream.CLASS_NAME)) {
                data = env.invokeMethod(trace, data, "readFully");
            }
            return stream.write(env, data, Memory.NULL);
        } catch (WrapIOException e) {
            env.warning(trace, "file_put_contents(): " + e.getMessage());
            return Memory.FALSE;
        } finally {
            if (stream != null)
                stream.close(env);
        }
    }

    public static Memory file_put_contents(Environment env, TraceInfo trace, String path, Memory data, int flags) throws Throwable {
        return file_put_contents(env, trace, path, data, flags, Memory.NULL);
    }

    public static Memory file_put_contents(Environment env, TraceInfo trace, String path, Memory data) throws Throwable {
        return file_put_contents(env, trace, path, data, 0, Memory.NULL);
    }

    public static boolean is_dir(String path) {
        return new File(path).isDirectory();
    }

    public static boolean is_file(String path) {
        return new File(path).isFile();
    }

    public static boolean is_link(String path) {
        try {
            File file = new File(path);
            File canon;
            if (file.getParent() == null) {
                canon = file;
            } else {
                File canonDir = file.getParentFile().getCanonicalFile();
                canon = new File(canonDir, file.getName());
            }
            return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
        } catch (IOException e) {
            return false;
        }
    }

    public static boolean is_executable(String path) {
        return new File(path).canExecute();
    }

    public static boolean is_readable(String path) {
        return new File(path).canRead();
    }

    public static boolean is_writable(String path) {
        return new File(path).canWrite();
    }

    public static boolean is_writeable(String path) {
        return new File(path).canWrite();
    }

    public static boolean mkdir(String path, int mode, boolean recursive) {
        if (recursive)
            return new File(path).mkdirs();
        else
            return new File(path).mkdir();
    }

    public static boolean mkdir(String path, int mode) {
        return mkdir(path, mode, false);
    }

    public static boolean mkdir(String path) {
        return mkdir(path, 777, false);
    }

    public static Memory filemtime(Environment env, TraceInfo trace, String path) {
        Path file = Paths.get(path);
        try {
            return LongMemory.valueOf(Files.getLastModifiedTime(file).toMillis() / 1000);
        } catch (IOException e) {
            env.warning(trace, e.getMessage());
            return Memory.FALSE;
        } catch (UnsupportedOperationException e) {
            return Memory.FALSE;
        }
    }

    public static Memory fileatime(Environment env, TraceInfo trace, String path) {
        Path file = Paths.get(path);
        try {
            BasicFileAttributes attributes = Files.readAttributes(file, BasicFileAttributes.class);
            return LongMemory.valueOf(attributes.lastAccessTime().toMillis() / 1000);
        } catch (IOException e) {
            env.warning(trace, e.getMessage());
            return Memory.FALSE;
        } catch (UnsupportedOperationException e) {
            return Memory.FALSE;
        }
    }

    public static Memory filectime(Environment env, TraceInfo trace, String path) {
        Path file = Paths.get(path);
        try {
            BasicFileAttributes attributes = Files.readAttributes(file, BasicFileAttributes.class);
            return LongMemory.valueOf(attributes.creationTime().toMillis() / 1000);
        } catch (IOException e) {
            env.warning(trace, e.getMessage());
            return Memory.FALSE;
        } catch (UnsupportedOperationException e) {
            return Memory.FALSE;
        }
    }

    public static Memory filesize(Environment env, TraceInfo trace, String path) {
        try {
            return LongMemory.valueOf(new File(path).length());
        } catch (Exception e) {
            env.warning(trace, "filesize(): file not found - %s", path);
            return Memory.FALSE;
        }
    }

    public static Memory filetype(Environment env, TraceInfo trace, String path) {
        File file = new File(path);
        if (file.isFile())
            return new StringMemory("file");
        else if (file.isDirectory())
            return new StringMemory("dir");
        else {
            try {
                BasicFileAttributes attributes = Files.readAttributes(Paths.get(path), BasicFileAttributes.class);
                if (attributes.isSymbolicLink()) {
                    return new StringMemory("link");
                }
                return new StringMemory("unknown");
            } catch (IOException e) {
                env.warning(trace, e.getMessage());
                return Memory.FALSE;
            }
        }
    }

    public static Memory filegroup(Environment env, TraceInfo trace, String path) {
        Path file = Paths.get(path);
        try {
            int attribute = (int) Files.getAttribute(file, "unix:gid");
            return LongMemory.valueOf(attribute);
        } catch (IOException | SecurityException e) {
            env.warning(trace, e.getMessage());
            return Memory.FALSE;
        } catch (UnsupportedOperationException e) {
            return Memory.FALSE;
        }
    }

    public static Memory fileowner(Environment env, TraceInfo trace, String path) {
        Path file = Paths.get(path);
        try {
            int attribute = (int) Files.getAttribute(file, "unix:uid");
            return LongMemory.valueOf(attribute);
        } catch (IOException | SecurityException e) {
            env.warning(trace, e.getMessage());
            return Memory.FALSE;
        } catch (UnsupportedOperationException e) {
            return Memory.FALSE;
        }
    }

    public static Memory fileperms(Environment env, TraceInfo trace, String path) {
        Path file = Paths.get(path);
        try {
            int attribute = (int) Files.getAttribute(file, "unix:mode");
            return LongMemory.valueOf(attribute);
        } catch (IOException | SecurityException e) {
            env.warning(trace, e.getMessage());
            return Memory.FALSE;
        } catch (UnsupportedOperationException e) {
            return Memory.FALSE;
        }
    }

    public static Memory pathinfo(String path, int options) {
        File file = new File(path);
        ArrayMemory result = new ArrayMemory();
        String basename = file.getName();
        int pos = basename.lastIndexOf('.');
        String ext = null;
        if (pos > -1)
            ext = basename.substring(pos + 1);
        if ((options & PATHINFO_DIRNAME) == PATHINFO_DIRNAME)
            result.refOfIndex("dirname").assign(file.getParent());
        if ((options & PATHINFO_DIRNAME) == PATHINFO_DIRNAME)
            result.refOfIndex("basename").assign(file.getName());
        if (ext != null && (options & PATHINFO_EXTENSION) == PATHINFO_EXTENSION)
            result.refOfIndex("extension").assign(ext);
        if ((options & PATHINFO_FILENAME) == PATHINFO_FILENAME)
            result.refOfIndex("filename").assign(pos > -1 ? basename.substring(0, pos) : basename);
        return result.toConstant();
    }

    public static Memory pathinfo(String path) {
        return pathinfo(path, PATHINFO_BASENAME | PATHINFO_DIRNAME | PATHINFO_EXTENSION | PATHINFO_FILENAME);
    }

    public static boolean rename(String oldname, String newname) {
        try {
            return new File(oldname).renameTo(new File(newname));
        } catch (Exception e) {
            return false;
        }
    }

    public static boolean rmdir(String path) {
        File file = new File(path);
        if (file.isDirectory()) {
            try {
                return file.delete();
            } catch (Exception e) {
                return false;
            }
        } else
            return false;
    }

    public static boolean unlink(String path) {
        File file = new File(path);
        if (file.isDirectory())
            return false;
        try {
            return file.delete();
        } catch (Exception e) {
            return false;
        }
    }

    public static boolean touch(Environment env, TraceInfo trace, String path, long time, long atime) {
        File file = new File(path);
        if (!file.exists())
            try {
                if (!file.createNewFile())
                    return false;
            } catch (IOException e) {
                env.warning(trace, e.getMessage());
                return false;
            }
        return file.setLastModified(time * 1000);
    }

    public static boolean touch(Environment env, TraceInfo trace, String path, long time) {
        return touch(env, trace, path, time, 0);
    }

    public static boolean touch(Environment env, TraceInfo trace, String path) {
        return touch(env, trace, path, System.currentTimeMillis() / 1000, 0);
    }

    public static Memory tempnam(String dir, String prefix) {
        try {
            return new StringMemory(File.createTempFile(prefix, "", new File(dir)).getPath());
        } catch (IOException e) {
            return Memory.FALSE;
        }
    }

    public static Memory realpath(String path) {
        try {
            return new StringMemory(new File(path).getCanonicalPath());
        } catch (IOException e) {
            return Memory.FALSE;
        }
    }

    public static Memory readfile(Environment env, TraceInfo trace, String path, boolean useIncludePaths, Memory stream) throws Throwable {
        File file = new File(path);
        if (useIncludePaths && !file.exists()) {
            path = env.findInIncludePaths(path);
            if (path == null)
                return Memory.FALSE;
            file = new File(path);
        }
        try {
            RandomAccessFile accessFile = new RandomAccessFile(file, "r");
            try {
                if (!stream.isNull()) {
                    if (!stream.instanceOf(Stream.CLASS_NAME)) {
                        env.warning(trace, "readfile(): Argument 3 must be stream, %s given", stream.getRealType().toString());
                        return Memory.FALSE;
                    }
                    byte[] buff = new byte[4096];
                    int len = 0;
                    int read = 0;
                    while ((len = accessFile.read(buff)) != -1) {
                        read += len;
                        ObjectInvokeHelper.invokeMethod(stream, "write", env, trace, new BinaryMemory(buff), LongMemory.valueOf(len));
                    }
                    return LongMemory.valueOf(read);
                } else {
                    byte[] buff = new byte[4096];
                    int len = 0;
                    int read = 0;
                    while ((len = accessFile.read(buff)) != -1) {
                        read += len;
                        env.echo(buff, len);
                    }
                    return LongMemory.valueOf(read);
                }
            } finally {
                accessFile.close();
            }
        } catch (FileNotFoundException e) {
            env.warning(trace, "readfile(): File not found - %s", path);
            return Memory.FALSE;
        } catch (IOException e) {
            env.warning(trace, "readfile(): %s", e.getMessage());
            return Memory.FALSE;
        }
    }

    public static Memory readfile(Environment env, TraceInfo trace, String path, boolean useIncludePaths) throws Throwable {
        return readfile(env, trace, path, useIncludePaths, Memory.NULL);
    }

    public static Memory readfile(Environment env, TraceInfo trace, String path) throws Throwable {
        return readfile(env, trace, path, false, Memory.NULL);
    }

    public static Memory fopen(Environment env, TraceInfo trace, String path, String mode) {
        try {
            return ObjectMemory.valueOf(Stream.create(env, path, mode));
        } catch (Throwable throwable) {
            env.warning(trace, "fopen(): failed to open stream, " + throwable.getMessage());
            return Memory.FALSE;
        }
    }

    public static Memory ftell(Environment env, TraceInfo trace, Memory stream) {
        if (stream.instanceOf(Stream.CLASS_NAME)) {
            try {
                return env.invokeMethod(trace, stream, "getPosition");
            } catch (Throwable throwable) {
                env.warning(trace, "ftell(): " + throwable.getMessage());
                return Memory.FALSE;
            }
        }
        env.warning(trace, "ftell(): unable to get position from a non-stream");
        return Memory.FALSE;
    }

    public static Memory feof(Environment env, TraceInfo trace, Memory stream) {
        if (stream.instanceOf(Stream.CLASS_NAME)) {
            try {
                return env.invokeMethod(trace, stream, "eof");
            } catch (Throwable throwable) {
                env.warning(trace, "feof(): " + throwable.getMessage());
                return Memory.FALSE;
            }
        }
        env.warning(trace, "feof(): unable get eof from a non-stream");
        return Memory.FALSE;
    }

    public static Memory fread(Environment env, TraceInfo trace, Memory stream, int length) {
        if (stream.instanceOf(Stream.CLASS_NAME)) {
            try {
                return env.invokeMethod(trace, stream, "read", LongMemory.valueOf(length));
            } catch (Throwable throwable) {
                env.warning(trace, "fread(): " + throwable.getMessage());
                return Memory.FALSE;
            }
        }
        env.warning(trace, "fread(): unable to read from a non-stream");
        return Memory.FALSE;
    }

    public static Memory fgetc(Environment env, TraceInfo trace, Memory stream) {
        if (stream.instanceOf(Stream.CLASS_NAME)) {
            try {
                Memory memory = env.invokeMethod(trace, stream, "read", Memory.CONST_INT_1);
                return memory.isNull() ? Memory.FALSE : memory;
            } catch (Throwable throwable) {
                env.warning(trace, "fgetc(): " + throwable.getMessage());
                return Memory.FALSE;
            }
        }
        env.warning(trace, "fgetc(): unable to read from a non-stream");
        return Memory.FALSE;
    }

    public static Memory fseek(Environment env, TraceInfo trace, Memory stream, long offset) {
        return fseek(env, trace, stream, offset, SEEK_SET);
    }

    public static Memory fseek(Environment env, TraceInfo trace, Memory stream, long offset, int whence) {
        if (stream.instanceOf(Stream.CLASS_NAME)) {
            try {
                switch(whence) {
                    case SEEK_CUR:
                        offset += env.invokeMethod(trace, stream, "getPosition").toLong();
                        break;
                    case SEEK_END:
                        env.error(trace, "fseek(): flag SEEK_END is not supported.");
                        break;
                    default:
                    case SEEK_SET:
                        break;
                }
                env.invokeMethod(trace, stream, "seek", LongMemory.valueOf(offset));
                return Memory.CONST_INT_0;
            } catch (Throwable throwable) {
                env.warning(trace, "fseek(): " + throwable.getMessage());
                return Memory.CONST_INT_M1;
            }
        }
        env.warning(trace, "fseek(): unable to seek in a non-stream");
        return Memory.CONST_INT_M1;
    }

    public static Memory fputs(Environment env, TraceInfo trace, Memory stream, Memory value) {
        return fwrite(env, trace, stream, value);
    }

    public static Memory fputs(Environment env, TraceInfo trace, Memory stream, Memory value, Memory length) {
        return fwrite(env, trace, stream, value, length);
    }

    public static Memory fgets(Environment env, TraceInfo trace, Memory stream) {
        return fgets(env, trace, stream, Memory.NULL);
    }

    public static Memory fgets(Environment env, TraceInfo trace, Memory stream, Memory length) {
        if (stream.instanceOf(Stream.CLASS_NAME)) {
            InputStream in = Stream.getInputStream(env, stream);
            if (in != null) {
                int read;
                StringBuilder sb = new StringBuilder();
                try {
                    while ((read = in.read()) != -1) {
                        if (length.isNotNull() && sb.length() >= length.toInteger()) {
                            break;
                        }
                        if (read == '\n' || read == '\r') {
                            break;
                        }
                        sb.append((char) read);
                    }
                    return StringMemory.valueOf(sb.toString());
                } catch (IOException e) {
                    env.warning(trace, "fgets(): " + e.getMessage());
                    return Memory.FALSE;
                }
            }
        }
        env.warning(trace, "fgets(): unable to get from a non-stream");
        return Memory.FALSE;
    }

    public static Memory fwrite(Environment env, TraceInfo trace, Memory stream, Memory value) {
        return fwrite(env, trace, stream, value, Memory.NULL);
    }

    public static Memory fwrite(Environment env, TraceInfo trace, Memory stream, Memory value, Memory length) {
        if (stream.instanceOf(Stream.CLASS_NAME)) {
            try {
                return env.invokeMethod(trace, stream, "write", value, length);
            } catch (Throwable throwable) {
                env.warning(trace, "fwrite(): " + throwable.getMessage());
                return Memory.FALSE;
            }
        }
        env.warning(trace, "fwrite(): unable to write to a non-stream");
        return Memory.FALSE;
    }

    public static Memory fclose(Environment env, TraceInfo trace, Memory stream) {
        if (stream.instanceOf(Stream.CLASS_NAME)) {
            try {
                env.invokeMethod(trace, stream, "close");
                return Memory.TRUE;
            } catch (Throwable throwable) {
                return Memory.FALSE;
            }
        } else {
            env.warning("fclose(): unable to close a non-stream");
            return Memory.FALSE;
        }
    }

    public static String getcwd() {
        Path currentRelativePath = Paths.get("");
        return currentRelativePath.toAbsolutePath().toString();
    }

    public static String getenv(Environment env, String name) {
        Map<String, String> zendEnv = env.getUserValue("env", Map.class);
        if (zendEnv != null) {
            String s = zendEnv.get(name);
            if (s != null) {
                return s;
            }
        }
        return System.getenv(name);
    }

    synchronized public static void putenv(Environment env, String _value) {
        if (_value.isEmpty()) {
            return;
        }
        String[] strings = StringUtils.split(_value, "=", 2);
        String name = strings[0];
        String value = strings.length > 1 ? strings[1] : null;
        Map<String, String> zendEnv = env.getUserValue("env", Map.class);
        if (zendEnv == null) {
            env.setUserValue("env", zendEnv = new HashMap<String, String>());
        }
        if (value == null) {
            zendEnv.remove(name);
        } else {
            zendEnv.put(name, value);
        }
    }

    public static Memory scandir(String path, int order) {
        ArrayMemory r = new ArrayMemory();
        String[] list = new File(path).list();
        switch(order) {
            case FileConstants.SCANDIR_SORT_DESCENDING:
                Arrays.sort(list, Collections.reverseOrder());
                for (String s : list) {
                    r.add(s);
                }
                r.add("..");
                r.add(".");
                break;
            case FileConstants.SCANDIR_SORT_ASCENDING:
                r.add(".");
                r.add("..");
                Arrays.sort(list);
                for (String s : list) {
                    r.add(s);
                }
                break;
            default:
                r.add(".");
                r.add("..");
                for (String s : list) {
                    r.add(s);
                }
                break;
        }
        return r.toConstant();
    }

    public static Memory scandir(String path) {
        return scandir(path, FileConstants.SCANDIR_SORT_ASCENDING);
    }

    public static Memory glob(Environment env, String path) {
        return glob(env, path, 0);
    }

    private static boolean globMatches(PathMatcher matcher, String rule, File file) {
        if (matcher.matches(file.toPath())) {
            return true;
        }

        return false;
    }

    public static Memory glob(Environment env, String path, int flags) {
        boolean onlyDirs = (flags & FileConstants.GLOB_ONLYDIR) == FileConstants.GLOB_ONLYDIR;
        boolean err = (flags & FileConstants.GLOB_ERR) == FileConstants.GLOB_ERR;
        boolean noSort = (flags & FileConstants.GLOB_NOSORT) == FileConstants.GLOB_NOSORT;
        boolean noCheck = (flags & FileConstants.GLOB_NOCHECK) == FileConstants.GLOB_NOCHECK;
        boolean noEscape = (flags & FileConstants.GLOB_NOESCAPE) == FileConstants.GLOB_NOESCAPE;
        ArrayMemory result = new ArrayMemory();
        boolean inCurrentDir = false;
        File file = new File(path);
        if (!path.endsWith("/") && !path.endsWith(Constants.DIRECTORY_SEPARATOR)) {
            if (file.getParentFile() == null) {
                file = new File("./");
                path = "./" + path;
                inCurrentDir = true;
            } else {
                file = file.getParentFile();
            }
        }
        final PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:" + path);
        ArrayMemory finalResult = result;
        File[] files = file.listFiles();
        if (files != null) {
            for (File one : files) {
                if (globMatches(matcher, path, one)) {
                    if (onlyDirs && !one.isDirectory()) {
                        continue;
                    }
                    String onePath = one.getPath();
                    if (inCurrentDir) {
                        onePath = onePath.substring(file.getPath().length() + 1);
                    }
                    finalResult.add(StringMemory.valueOf(onePath));
                }
            }
        }
        if (result.size() == 0 && noCheck) {
            result.add(path);
        }
        if (!noSort) {
            result = ItemsUtils.sort(env, result, Memory.NULL, Memory.FALSE).toValue(ArrayMemory.class);
        }
        return result;
    }
}


switch(transactionInfo.state) {
            case PREPARING:
                completePreparingTransaction(transactionId);
                break;
            case COMMITTING:
                completeCommittingTransaction(transactionId);
                break;
            case ROLLING_BACK:
                completeRollingBackTransaction(transactionId);
                break;
            default:
                break;
        }

package io.atomix.core.impl;

import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableSet;
import io.atomix.cluster.ClusterMembershipEvent;
import io.atomix.cluster.ClusterMembershipEventListener;
import io.atomix.cluster.MemberId;
import io.atomix.core.iterator.AsyncIterator;
import io.atomix.core.map.AsyncAtomicMap;
import io.atomix.core.map.AtomicMapConfig;
import io.atomix.core.map.AtomicMapType;
import io.atomix.core.transaction.ManagedTransactionService;
import io.atomix.core.transaction.ParticipantInfo;
import io.atomix.core.transaction.TransactionException;
import io.atomix.core.transaction.TransactionId;
import io.atomix.core.transaction.TransactionService;
import io.atomix.core.transaction.TransactionState;
import io.atomix.core.transaction.Transactional;
import io.atomix.primitive.DistributedPrimitive;
import io.atomix.primitive.PrimitiveBuilder;
import io.atomix.primitive.PrimitiveManagementService;
import io.atomix.primitive.PrimitiveType;
import io.atomix.primitive.partition.PartitionGroup;
import io.atomix.primitive.protocol.PrimitiveProtocol;
import io.atomix.primitive.protocol.ProxyCompatibleBuilder;
import io.atomix.primitive.protocol.ProxyProtocol;
import io.atomix.utils.concurrent.Futures;
import io.atomix.utils.serializer.Namespace;
import io.atomix.utils.serializer.Namespaces;
import io.atomix.utils.serializer.Serializer;
import io.atomix.utils.time.Versioned;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;

/**
 * Core transaction service.
 */
public class CoreTransactionService implements ManagedTransactionService {

    private static final Logger LOGGER = LoggerFactory.getLogger(CoreTransactionService.class);

    private static final Serializer SERIALIZER = Serializer.using(Namespace.builder().register(Namespaces.BASIC).register(MemberId.class).register(TransactionId.class).register(TransactionState.class).register(ParticipantInfo.class).register(TransactionInfo.class).build());

    private final PrimitiveManagementService managementService;

    private final MemberId localMemberId;

    private final ClusterMembershipEventListener clusterEventListener = this::onMembershipChange;

    private volatile AsyncAtomicMap<TransactionId, TransactionInfo> transactions;

    private final AtomicBoolean started = new AtomicBoolean();

    public CoreTransactionService(PrimitiveManagementService managementService) {
        this.managementService = checkNotNull(managementService);
        this.localMemberId = managementService.getMembershipService().getLocalMember().id();
    }

    @Override
    public Set<TransactionId> getActiveTransactions() {
        checkState(isRunning());
        return transactions.sync().keySet();
    }

    @Override
    public TransactionState getTransactionState(TransactionId transactionId) {
        checkState(isRunning());
        TransactionInfo info = Versioned.valueOrNull(transactions.get(transactionId).join());
        return info != null ? info.state : null;
    }

    @Override
    public CompletableFuture<TransactionId> begin() {
        checkState(isRunning());
        TransactionId transactionId = TransactionId.from(UUID.randomUUID().toString());
        TransactionInfo info = new TransactionInfo(localMemberId, TransactionState.ACTIVE, ImmutableSet.of());
        return transactions.put(transactionId, info).thenApply(v -> transactionId);
    }

    @Override
    public CompletableFuture<Void> preparing(TransactionId transactionId, Set<ParticipantInfo> participants) {
        checkState(isRunning());
        return transactions.compute(transactionId, (id, info) -> {
            if (info == null) {
                return null;
            } else if (info.state == TransactionState.ACTIVE && info.coordinator.equals(localMemberId)) {
                return new TransactionInfo(info.coordinator, TransactionState.PREPARING, participants);
            } else {
                return info;
            }
        }).thenCompose(value -> {
            if (value == null || value.value() == null) {
                return Futures.exceptionalFuture(new TransactionException("Unknown transaction " + transactionId));
            } else if (value.value().state != TransactionState.PREPARING) {
                return Futures.exceptionalFuture(new TransactionException("Concurrent transaction modification " + transactionId));
            } else if (!value.value().coordinator.equals(localMemberId)) {
                return Futures.exceptionalFuture(new TransactionException("Transaction " + transactionId + " recovered by another member"));
            }
            return Futures.completedFuture(null);
        });
    }

    @Override
    public CompletableFuture<Void> committing(TransactionId transactionId) {
        checkState(isRunning());
        return transactions.compute(transactionId, (id, info) -> {
            if (info == null) {
                return null;
            } else if (info.state == TransactionState.PREPARING && info.coordinator.equals(localMemberId)) {
                return new TransactionInfo(info.coordinator, TransactionState.COMMITTING, info.participants);
            } else {
                return info;
            }
        }).thenCompose(value -> {
            if (value == null || value.value() == null) {
                return Futures.exceptionalFuture(new TransactionException("Unknown transaction " + transactionId));
            } else if (value.value().state != TransactionState.COMMITTING) {
                return Futures.exceptionalFuture(new TransactionException("Concurrent transaction modification " + transactionId));
            } else if (!value.value().coordinator.equals(localMemberId)) {
                return Futures.exceptionalFuture(new TransactionException("Transaction " + transactionId + " recovered by another member"));
            }
            return Futures.completedFuture(null);
        });
    }

    @Override
    public CompletableFuture<Void> aborting(TransactionId transactionId) {
        checkState(isRunning());
        return transactions.compute(transactionId, (id, info) -> {
            if (info == null) {
                return null;
            } else if (info.state == TransactionState.PREPARING && info.coordinator.equals(localMemberId)) {
                return new TransactionInfo(info.coordinator, TransactionState.ROLLING_BACK, info.participants);
            } else {
                return info;
            }
        }).thenCompose(value -> {
            if (value == null || value.value() == null) {
                return Futures.exceptionalFuture(new TransactionException("Unknown transaction " + transactionId));
            } else if (value.value().state != TransactionState.ROLLING_BACK) {
                return Futures.exceptionalFuture(new TransactionException("Concurrent transaction modification " + transactionId));
            } else if (!value.value().coordinator.equals(localMemberId)) {
                return Futures.exceptionalFuture(new TransactionException("Transaction " + transactionId + " recovered by another member"));
            }
            return Futures.completedFuture(null);
        });
    }

    @Override
    public CompletableFuture<Void> complete(TransactionId transactionId) {
        checkState(isRunning());
        return transactions.remove(transactionId).thenApply(v -> null);
    }

    /**
     * Handles a cluster membership change event.
     */
    private void onMembershipChange(ClusterMembershipEvent event) {
        if (event.type() == ClusterMembershipEvent.Type.MEMBER_REMOVED) {
            recoverTransactions(transactions.entrySet().iterator(), event.subject().id());
        }
    }

    /**
     * Recursively recovers transactions using the given iterator.
     *
     * @param iterator the asynchronous iterator from which to recover transactions
     * @param memberId the transaction member ID
     */
    private void recoverTransactions(AsyncIterator<Map.Entry<TransactionId, Versioned<TransactionInfo>>> iterator, MemberId memberId) {
        iterator.next().thenAccept(entry -> {
            if (entry.getValue().value().coordinator.equals(memberId)) {
                recoverTransaction(entry.getKey(), entry.getValue().value());
            }
            recoverTransactions(iterator, memberId);
        });
    }

    /**
     * Recovers and completes the given transaction.
     *
     * @param transactionId the transaction identifier
     * @param transactionInfo the transaction info
     */
    private void recoverTransaction(TransactionId transactionId, TransactionInfo transactionInfo) {

    }

    /**
     * Completes a transaction in the {@link TransactionState#PREPARING} state.
     *
     * @param transactionId the transaction identifier for the transaction to complete
     */
    private void completePreparingTransaction(TransactionId transactionId) {
        // Change ownership of the transaction to the local node and set its state to ROLLING_BACK and then roll it back.
        completeTransaction(transactionId, TransactionState.PREPARING, info -> new TransactionInfo(localMemberId, TransactionState.ROLLING_BACK, info.participants), info -> info.state == TransactionState.ROLLING_BACK, (id, transactional) -> transactional.rollback(id)).whenComplete((result, error) -> {
            if (error != null) {
                error = Throwables.getRootCause(error);
                if (error instanceof TransactionException) {
                    LOGGER.warn("Failed to complete transaction", error);
                } else {
                    LOGGER.warn("Failed to roll back transaction " + transactionId);
                }
            }
        });
    }

    /**
     * Completes a transaction in the {@link TransactionState#PREPARING} state.
     *
     * @param transactionId the transaction identifier for the transaction to complete
     */
    private void completeCommittingTransaction(TransactionId transactionId) {
        // Change ownership of the transaction to the local node and then commit it.
        completeTransaction(transactionId, TransactionState.COMMITTING, info -> new TransactionInfo(localMemberId, TransactionState.COMMITTING, info.participants), info -> info.state == TransactionState.COMMITTING && info.coordinator.equals(localMemberId), (id, transactional) -> transactional.commit(id)).whenComplete((result, error) -> {
            if (error != null) {
                error = Throwables.getRootCause(error);
                if (error instanceof TransactionException) {
                    LOGGER.warn("Failed to complete transaction", error);
                } else {
                    LOGGER.warn("Failed to commit transaction " + transactionId);
                }
            }
        });
    }

    /**
     * Completes a transaction in the {@link TransactionState#PREPARING} state.
     *
     * @param transactionId the transaction identifier for the transaction to complete
     */
    private void completeRollingBackTransaction(TransactionId transactionId) {
        // Change ownership of the transaction to the local node and then roll it back.
        completeTransaction(transactionId, TransactionState.ROLLING_BACK, info -> new TransactionInfo(localMemberId, TransactionState.ROLLING_BACK, info.participants), info -> info.state == TransactionState.ROLLING_BACK && info.coordinator.equals(localMemberId), (id, transactional) -> transactional.rollback(id)).whenComplete((result, error) -> {
            if (error != null) {
                error = Throwables.getRootCause(error);
                if (error instanceof TransactionException) {
                    LOGGER.warn("Failed to complete transaction", error);
                } else {
                    LOGGER.warn("Failed to roll back transaction " + transactionId);
                }
            }
        });
    }

    /**
     * Completes a transaction by modifying the transaction state to change ownership to this member and then completing
     * the transaction based on the existing transaction state.
     */
    @SuppressWarnings("unchecked")
    private CompletableFuture<Void> completeTransaction(TransactionId transactionId, TransactionState expectState, Function<TransactionInfo, TransactionInfo> updateFunction, Predicate<TransactionInfo> updatedPredicate, BiFunction<TransactionId, Transactional<?>, CompletableFuture<Void>> completionFunction) {
        return transactions.compute(transactionId, (id, info) -> {
            if (info == null) {
                return null;
            } else if (info.state == expectState) {
                return updateFunction.apply(info);
            } else {
                return info;
            }
        }).thenCompose(value -> {
            if (value != null && updatedPredicate.test(value.value())) {
                return Futures.allOf(value.value().participants.stream().map(participantInfo -> completeParticipant(participantInfo, info -> completionFunction.apply(transactionId, info)))).thenApply(v -> null);
            }
            return Futures.exceptionalFuture(new TransactionException("Failed to acquire transaction lock"));
        });
    }

    /**
     * Completes an individual participant in a transaction by loading the primitive by type/protocol/partition group and
     * applying the given completion function to it.
     */
    @SuppressWarnings("unchecked")
    private CompletableFuture<Void> completeParticipant(ParticipantInfo participantInfo, Function<Transactional<?>, CompletableFuture<Void>> completionFunction) {
        // Look up the primitive type for the participant. If the primitive type is not found, return an exception.
        PrimitiveType primitiveType = managementService.getPrimitiveTypeRegistry().getPrimitiveType(participantInfo.type());
        if (primitiveType == null) {
            return Futures.exceptionalFuture(new TransactionException("Failed to locate primitive type " + participantInfo.type() + " for participant " + participantInfo.name()));
        }
        // Look up the protocol type for the participant.
        PrimitiveProtocol.Type protocolType = managementService.getProtocolTypeRegistry().getProtocolType(participantInfo.protocol());
        if (protocolType == null) {
            return Futures.exceptionalFuture(new TransactionException("Failed to locate protocol type for participant " + participantInfo.name()));
        }
        // Look up the partition group in which the primitive is stored.
        PartitionGroup partitionGroup;
        if (participantInfo.group() == null) {
            partitionGroup = managementService.getPartitionService().getPartitionGroup(protocolType);
        } else {
            partitionGroup = managementService.getPartitionService().getPartitionGroup(participantInfo.group());
        }
        // If the partition group is not found, return an exception.
        if (partitionGroup == null) {
            return Futures.exceptionalFuture(new TransactionException("Failed to locate partition group for participant " + participantInfo.name()));
        }
        PrimitiveBuilder builder = primitiveType.newBuilder(participantInfo.name(), primitiveType.newConfig(), managementService);
        ((ProxyCompatibleBuilder) builder).withProtocol(partitionGroup.newProtocol());
        DistributedPrimitive primitive = builder.build();
        return completionFunction.apply((Transactional<?>) primitive);
    }

    @Override
    @SuppressWarnings("unchecked")
    public CompletableFuture<TransactionService> start() {
        PrimitiveProtocol protocol = managementService.getPartitionService().getSystemPartitionGroup().newProtocol();
        return AtomicMapType.<TransactionId, TransactionInfo>instance().newBuilder("atomix-transactions", new AtomicMapConfig(), managementService).withSerializer(SERIALIZER).withProtocol((ProxyProtocol) protocol).withCacheEnabled().buildAsync().thenApply(transactions -> {
            this.transactions = transactions.async();
            managementService.getMembershipService().addListener(clusterEventListener);
            LOGGER.info("Started");
            started.set(true);
            return this;
        });
    }

    @Override
    public boolean isRunning() {
        return started.get();
    }

    @Override
    public CompletableFuture<Void> stop() {
        if (started.compareAndSet(true, false)) {
            managementService.getMembershipService().removeListener(clusterEventListener);
            return transactions.close().exceptionally(e -> null);
        }
        return CompletableFuture.completedFuture(null);
    }

    private static class TransactionInfo {

        private final MemberId coordinator;

        private final TransactionState state;

        private final Set<ParticipantInfo> participants;

        TransactionInfo(MemberId coordinator, TransactionState state, Set<ParticipantInfo> participants) {
            this.coordinator = coordinator;
            this.state = state;
            this.participants = participants;
        }
    }
}


switch(msg.what) {
                case 0:
                    progressView.cuurDuration = progressView.cuurDuration + 500;
                    progressView.cuurDuration = progressView.cuurDuration % progressView.maxDuration;
                    sendEmptyMessageDelayed(0, 200);
                    progressView.invalidate();
                    break;
            }

package com.aserbao.aserbaosandroid.ui.canvas.canvas;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.Shader;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.View;
import com.aserbao.aserbaosandroid.R;
import com.example.base.utils.screen.DisplayUtil;
import java.lang.ref.WeakReference;

/*
 * 作用：
 * @author aserbao
 * @date: on 2020/7/14
 * @project: AserbaosAndroid
 * @package: com.aserbao.aserbaosandroid.ui.canvas.canvas
 */
public class ProgressView extends View {

    private Paint mProgressPaint;

    private Paint mActivePaint;

    private Paint mPathPaint;

    private int cuurDuration, maxDuration = 10 * 1000;

    int startColor = Color.BLACK;

    int endColor = Color.WHITE;

    private Path mPath;

    int dp4 = DisplayUtil.dip2px(4);

    private int leftTopRadius = dp4, leftBottomRadius = dp4, rightTopRadius = dp4, rightBottomRadius = dp4;

    private RectF tempRectF = new RectF();

    public ProgressView(Context paramContext) {
        super(paramContext);
        init();
    }

    public ProgressView(Context paramContext, AttributeSet paramAttributeSet) {
        super(paramContext, paramAttributeSet);
        init();
    }

    public ProgressView(Context paramContext, AttributeSet paramAttributeSet, int paramInt) {
        super(paramContext, paramAttributeSet, paramInt);
        init();
    }

    public int getMaxDuration() {
        return maxDuration;
    }

    private void init() {
        mProgressPaint = new Paint();
        mActivePaint = new Paint();
        mPathPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPath = new Path();
        mActivePaint.setColor(Color.RED);
        mActivePaint.setStyle(Paint.Style.FILL);
        mProgressPaint.setColor(Color.parseColor("#FAE000"));
        mProgressPaint.setStyle(Paint.Style.FILL);
        mPathPaint.setColor(Color.TRANSPARENT);
        mPathPaint.setAntiAlias(true);
        mPathPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        final int width = getMeasuredWidth(), height = getMeasuredHeight();
        int left = 0, right = 0, duration = 0;
        right = left + (int) (cuurDuration * 1.0F / maxDuration * width);
        final float halfWidth = getWidth() / 2f;
        final float halfHeight = getHeight() / 2f;
        final float minSize = halfWidth > halfHeight ? halfWidth : halfHeight;
        final float leftTop = width > 2 * leftTopRadius ? leftTopRadius : minSize / 2f;
        final float rightTop = width > 2 * rightTopRadius ? rightTopRadius : minSize / 2f;
        final float leftBottom = width > 2 * leftBottomRadius ? leftBottomRadius : minSize / 2f;
        final float rightBottom = width > 2 * rightBottomRadius ? rightBottomRadius : minSize / 2f;
        canvas.drawRect(left, 0.0F, right, height, mProgressPaint);
        tryTwo(canvas, leftTop, rightTop, leftBottom, rightBottom);
    }

    private void tryOne(Canvas canvas, float leftTop, float rightTop, float leftBottom, float rightBottom) {
        mPath.reset();
        mPath.moveTo(0, leftTop);
        tempRectF.set(0, 0, leftTop * 2, leftTop * 2);
        mPath.arcTo(tempRectF, 180, 90, false);
        mPath.lineTo(0, 0);
        mPath.lineTo(0, leftTop);
        canvas.drawPath(mPath, mPathPaint);
        mPath.reset();
        mPath.moveTo(getWidth() - rightTop, 0);
        tempRectF.set(getWidth() - rightTop * 2, 0, getWidth(), rightTop * 2);
        mPath.arcTo(tempRectF, 270, 90, false);
        mPath.lineTo(getWidth(), 0);
        mPath.lineTo(getWidth() - rightTop, 0);
        canvas.drawPath(mPath, mPathPaint);
        mPath.reset();
        mPath.moveTo(getWidth(), getHeight() - rightBottom);
        tempRectF.set(getWidth() - rightBottom * 2, getHeight() - rightBottom * 2, getWidth(), getHeight());
        mPath.arcTo(tempRectF, 0, 90, false);
        mPath.lineTo(getWidth(), getHeight());
        mPath.lineTo(getWidth(), getHeight() - rightBottom);
        canvas.drawPath(mPath, mPathPaint);
        mPath.reset();
        mPath.moveTo(leftBottom, getHeight());
        tempRectF.set(0, getHeight() - leftBottom * 2, leftBottom * 2, getHeight());
        mPath.arcTo(tempRectF, 90, 90, false);
        mPath.lineTo(0, getHeight());
        mPath.lineTo(leftBottom, getHeight());
        canvas.drawPath(mPath, mPathPaint);
    }

    private void tryTwo(Canvas canvas, float leftTop, float rightTop, float leftBottom, float rightBottom) {
        mPath.reset();
        mPath.moveTo(0, leftTop);
        tempRectF.set(0, 0, leftTop * 2, leftTop * 2);
        mPath.arcTo(tempRectF, 180, 90, false);
        mPath.lineTo(0, 0);
        mPath.lineTo(0, leftTop);
        canvas.drawPath(mPath, mPathPaint);
        mPath.reset();
        mPath.moveTo(getWidth() - rightTop, 0);
        tempRectF.set(getWidth() - rightTop * 2, 0, getWidth(), rightTop * 2);
        mPath.arcTo(tempRectF, 270, 90, false);
        mPath.lineTo(getWidth(), 0);
        mPath.lineTo(getWidth() - rightTop, 0);
        canvas.drawPath(mPath, mPathPaint);
        mPath.reset();
        mPath.moveTo(getWidth(), getHeight() - rightBottom);
        tempRectF.set(getWidth() - rightBottom * 2, getHeight() - rightBottom * 2, getWidth(), getHeight());
        mPath.arcTo(tempRectF, 0, 90, false);
        mPath.lineTo(getWidth(), getHeight());
        mPath.lineTo(getWidth(), getHeight() - rightBottom);
        canvas.drawPath(mPath, mPathPaint);
        mPath.reset();
        mPath.moveTo(leftBottom, getHeight());
        tempRectF.set(0, getHeight() - leftBottom * 2, leftBottom * 2, getHeight());
        mPath.arcTo(tempRectF, 90, 90, false);
        mPath.lineTo(0, getHeight());
        mPath.lineTo(leftBottom, getHeight());
        canvas.drawPath(mPath, mPathPaint);
    }

    public void changeDuration(int progress) {
        mHandler.sendEmptyMessage(0);
        cuurDuration = progress;
        invalidate();
    }

    private Handler mHandler = new MyHandler(new WeakReference<ProgressView>(this));

    private static class MyHandler extends Handler {

        WeakReference<ProgressView> mWeakReference;

        public MyHandler(WeakReference<ProgressView> weakReference) {
            mWeakReference = weakReference;
        }

        @Override
        public void dispatchMessage(Message msg) {
            ProgressView progressView = mWeakReference.get();


switch(variant) {
            case CLOUDERA_MANAGER:
                return cmTemplateValidator;
            default:
                throw new NotImplementedException("BlueprintValidator for " + variant);
        }

package com.sequenceiq.cloudbreak.service.blueprint;

import static com.sequenceiq.cloudbreak.cluster.api.ClusterApi.CLOUDERA_MANAGER;
import javax.inject.Inject;
import org.apache.commons.lang3.NotImplementedException;
import org.springframework.stereotype.Component;
import com.sequenceiq.cloudbreak.cmtemplate.CmTemplateValidator;
import com.sequenceiq.cloudbreak.cmtemplate.utils.BlueprintUtils;
import com.sequenceiq.cloudbreak.domain.Blueprint;
import com.sequenceiq.cloudbreak.template.validation.BlueprintValidator;

@Component
public class BlueprintValidatorFactory {

    @Inject
    private BlueprintUtils blueprintUtils;

    @Inject
    private CmTemplateValidator cmTemplateValidator;

    public BlueprintValidator createBlueprintValidator(Blueprint blueprint) {
        String variant = blueprintUtils.getBlueprintVariant(blueprint.getBlueprintText());


switch(lastChar) {
            case 's':
                return TimeUnit.SECONDS.toMillis(parsedValue);
            case 'm':
                return TimeUnit.MINUTES.toMillis(parsedValue);
            case 'h':
                return TimeUnit.HOURS.toMillis(parsedValue);
            case 'd':
                return TimeUnit.DAYS.toMillis(parsedValue);
        }

package io.cdap.cdap.etl.common.macro;

import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import java.util.concurrent.TimeUnit;

/**
 * Parses time expressions and does simple time math.
 */
public class TimeParser {

    private final long runtime;

    public TimeParser(long runtime) {
        this.runtime = runtime;
    }

    /**
     * Parses time strings into timestamps, with support for some basic math.
     * Math syntax includes addition and subtraction in seconds, minutes, hours, and days.
     * Periods of time are specified as some number followed by the time unit,
     * where 's' is seconds', 'm' is minutes, 'h' is hours, and 'd' is days.
     * The "runtime" keyword translates to some given runtime, and can be strung together with
     * various additions and subtractions.
     * For example, "runtime-5s" is 5 seconds before runtime, "runtime-1d" is one day before runtime,
     * and "runtime-1d+4h" is 20 hours before runtime.
     *
     * @param str the time string to parse
     * @return milliseconds equivalent of the time string
     */
    public long parseRuntime(String str) {
        MathNode mathNode = parseExpression(str);
        return mathNode.evaluate();
    }

    /**
     * Parses a duration String to its long value.
     * Frequency string consists of a number followed by an unit, with 's' for seconds, 'm' for minutes, 'h' for hours
     * and 'd' for days. For example, an input of '5m' means 5 minutes which will be parsed to 300000 milliseconds.
     *
     * @param durationStr the duration string (ex: 5m, 5h etc).
     * @return milliseconds equivalent of the duration string
     */
    public static long parseDuration(String durationStr) {
        Preconditions.checkArgument(!Strings.isNullOrEmpty(durationStr));
        durationStr = durationStr.trim().toLowerCase();
        String value = durationStr.substring(0, durationStr.length() - 1);
        long parsedValue = Long.parseLong(value);
        char lastChar = durationStr.charAt(durationStr.length() - 1);

        throw new IllegalArgumentException(String.format("Time unit not supported: %s", lastChar));
    }

    // parse an expression into an evaluation tree
    private MathNode parseExpression(String expression) {
        expression = expression.trim();
        // we only support + and - right now so just evaluate from left to right
        int idx = Math.max(expression.lastIndexOf('+'), expression.lastIndexOf('-'));
        // if no operator, this should be a duration string
        if (idx < 0) {
            return "runtime".equals(expression) ? new ValueNode(runtime) : new ValueNode(parseDuration(expression));
        }
        char operator = expression.charAt(idx);
        if (idx == expression.length()) {
            throw new IllegalArgumentException(String.format("Invalid expression '%s'. Cannot end with %s.", expression, operator));
        }
        // if this in an expression like '-5d'
        if (idx == 0) {
            return new ValueNode(parseDuration(expression));
        }
        MathNode left = parseExpression(expression.substring(0, idx));
        MathNode right = parseExpression(expression.substring(idx + 1));
        if (operator == '+') {
            return new AddNode(left, right);
        }
        return new SubtractNode(left, right);
    }

    private interface MathNode {

        long evaluate();
    }

    private static class ValueNode implements MathNode {

        private long value;

        ValueNode(long value) {
            this.value = value;
        }

        @Override
        public long evaluate() {
            return value;
        }
    }

    private static class AddNode implements MathNode {

        private final MathNode left;

        private final MathNode right;

        AddNode(MathNode left, MathNode right) {
            this.left = left;
            this.right = right;
        }

        @Override
        public long evaluate() {
            return left.evaluate() + right.evaluate();
        }
    }

    private static class SubtractNode implements MathNode {

        private final MathNode left;

        private final MathNode right;

        SubtractNode(MathNode left, MathNode right) {
            this.left = left;
            this.right = right;
        }

        @Override
        public long evaluate() {
            return left.evaluate() - right.evaluate();
        }
    }
}