@Override
    public boolean supportsLimit()

package org.mybatis.pagination.dialect.db;

import org.mybatis.pagination.dialect.Dialect;

/**
 * Dialect for HSQLDB
 *
 * @author poplar.yfyang
 * @version 1.0 2010-10-10 下午12:31
 * @since JDK 1.5
 */
public class HSQLDialect implements Dialect {

{
        return true;
    }

    @Override
    public String getLimitString(String sql, int offset, int limit) {
        return getLimitString(sql, offset, Integer.toString(offset), Integer.toString(limit));
    }

    /**
     * 将sql变成分页sql语句,提供将offset及limit使用占位符号(placeholder)替换.
     * <pre>
     * 如mysql
     * dialect.getLimitString("select * from user", 12, ":offset",0,":limit") 将返回
     * select * from user limit :offset,:limit
     *
     * select limit :offset,:limit * from user
     * </pre>
     *
     * @param sql               实际SQL语句
     * @param offset            分页开始纪录条数
     * @param offsetPlaceholder 分页开始纪录条数－占位符号
     * @param limitPlaceholder  分页纪录条数占位符号
     * @return 包含占位符的分页sql
     */
    public String getLimitString(String sql, int offset, String offsetPlaceholder, String limitPlaceholder) {
        boolean hasOffset = offset > 0;
        return new StringBuffer(sql.length() + 10).append(sql).insert(sql.toLowerCase().indexOf("select") + 6, hasOffset ? " limit " + offsetPlaceholder + " " + limitPlaceholder : " top " + limitPlaceholder).toString();
    }

    @Override
    public String getCountString(String sql) {
        return "select count(*) from (" + sql + ") as tmp_count";
    }
}


{
        boolean hasOffset = offset > 0;
        return new StringBuffer(sql.length() + 10).append(sql).insert(sql.toLowerCase().indexOf("select") + 6, hasOffset ? " limit " + offsetPlaceholder + " " + limitPlaceholder : " top " + limitPlaceholder).toString();
    }

package org.mybatis.pagination.dialect.db;

import org.mybatis.pagination.dialect.Dialect;

/**
 * Dialect for HSQLDB
 *
 * @author poplar.yfyang
 * @version 1.0 2010-10-10 下午12:31
 * @since JDK 1.5
 */
public class HSQLDialect implements Dialect {

    @Override
    public boolean supportsLimit() {
        return true;
    }

    @Override
    public String getLimitString(String sql, int offset, int limit) {
        return getLimitString(sql, offset, Integer.toString(offset), Integer.toString(limit));
    }

    /**
     * 将sql变成分页sql语句,提供将offset及limit使用占位符号(placeholder)替换.
     * <pre>
     * 如mysql
     * dialect.getLimitString("select * from user", 12, ":offset",0,":limit") 将返回
     * select * from user limit :offset,:limit
     *
     * select limit :offset,:limit * from user
     * </pre>
     *
     * @param sql               实际SQL语句
     * @param offset            分页开始纪录条数
     * @param offsetPlaceholder 分页开始纪录条数－占位符号
     * @param limitPlaceholder  分页纪录条数占位符号
     * @return 包含占位符的分页sql
     */
    public String getLimitString(String sql, int offset, String offsetPlaceholder, String limitPlaceholder)

    @Override
    public String getCountString(String sql) {
        return "select count(*) from (" + sql + ") as tmp_count";
    }
}


return new StringBuffer(sql.length() + 10).append(sql).insert(sql.toLowerCase().indexOf("select") + 6, hasOffset ? " limit " + offsetPlaceholder + " " + limitPlaceholder : " top " + limitPlaceholder).toString();

package org.mybatis.pagination.dialect.db;

import org.mybatis.pagination.dialect.Dialect;

/**
 * Dialect for HSQLDB
 *
 * @author poplar.yfyang
 * @version 1.0 2010-10-10 下午12:31
 * @since JDK 1.5
 */
public class HSQLDialect implements Dialect {

    @Override
    public boolean supportsLimit() {
        return true;
    }

    @Override
    public String getLimitString(String sql, int offset, int limit) {
        return getLimitString(sql, offset, Integer.toString(offset), Integer.toString(limit));
    }

    /**
     * 将sql变成分页sql语句,提供将offset及limit使用占位符号(placeholder)替换.
     * <pre>
     * 如mysql
     * dialect.getLimitString("select * from user", 12, ":offset",0,":limit") 将返回
     * select * from user limit :offset,:limit
     *
     * select limit :offset,:limit * from user
     * </pre>
     *
     * @param sql               实际SQL语句
     * @param offset            分页开始纪录条数
     * @param offsetPlaceholder 分页开始纪录条数－占位符号
     * @param limitPlaceholder  分页纪录条数占位符号
     * @return 包含占位符的分页sql
     */
    public String getLimitString(String sql, int offset, String offsetPlaceholder, String limitPlaceholder) {
        boolean hasOffset = offset > 0;

    }

    @Override
    public String getCountString(String sql) {
        return "select count(*) from (" + sql + ") as tmp_count";
    }
}


public String getLimitString(String sql, int offset, String offsetPlaceholder, String limitPlaceholder) {
        boolean hasOffset = offset > 0;
        return new StringBuffer(sql.length() + 10).append(sql).insert(sql.toLowerCase().indexOf("select") + 6, hasOffset ? " limit " + offsetPlaceholder + " " + limitPlaceholder : " top " + limitPlaceholder).toString();
    }

package org.mybatis.pagination.dialect.db;

import org.mybatis.pagination.dialect.Dialect;

/**
 * Dialect for HSQLDB
 *
 * @author poplar.yfyang
 * @version 1.0 2010-10-10 下午12:31
 * @since JDK 1.5
 */
public class HSQLDialect implements Dialect {

    @Override
    public boolean supportsLimit() {
        return true;
    }

    @Override
    public String getLimitString(String sql, int offset, int limit) {
        return getLimitString(sql, offset, Integer.toString(offset), Integer.toString(limit));
    }

    /**
     * 将sql变成分页sql语句,提供将offset及limit使用占位符号(placeholder)替换.
     * <pre>
     * 如mysql
     * dialect.getLimitString("select * from user", 12, ":offset",0,":limit") 将返回
     * select * from user limit :offset,:limit
     *
     * select limit :offset,:limit * from user
     * </pre>
     *
     * @param sql               实际SQL语句
     * @param offset            分页开始纪录条数
     * @param offsetPlaceholder 分页开始纪录条数－占位符号
     * @param limitPlaceholder  分页纪录条数占位符号
     * @return 包含占位符的分页sql
     */


public static boolean viewOrEditFileWithWPS(@NonNull Fragment fragment, String filePath, boolean readOnly, int requestCode)

package com.hanlyjiang.library.fileviewer.wps;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.util.Log;
import com.hanlyjiang.library.utils.AndroidUtils;
import com.hanlyjiang.library.utils.FileViewerUtils;
import java.io.File;
import java.util.List;

/**
 * WPS打开文件工具
 *
 * @author hanlyjiang on 2018/3/22-13:49.
 * @version 1.0
 */
public class WPSOpenUtils {

    /**
     * 用WPS 打开应用,注意此种方法无法获取
     *
     * @param filePath 设置好数据了类型的intent
     * @param context  Context 对象
     * @return
     */
    public static boolean viewOrEditFileWithWPS(@NonNull Context context, String filePath, boolean readOnly) {
        return viewOrEditFileWithWPS(new ContextStartWrapper(context), filePath, readOnly, 0);
    }

    /**
     * 用WPS 打开文档
     *
     * @param fragment
     * @param filePath    设置好数据了类型的intent
     * @param readOnly    true - 以只读模式打开文档通过StartActivity启动）； false - 以编辑模式打开文档（通过StartActivityForResult启动）
     * @param requestCode 指定通过StartActivityForResult启动时的requestCode
     * @return
     */

{
        return viewOrEditFileWithWPS(new FragmentStartWrapper(fragment), filePath, readOnly, requestCode);
    }

    /**
     * 用WPS 打开应用
     *
     * @param filePath 设置好数据了类型的intent
     * @param wrapper  ActivityStarterWrapper 对象
     * @return
     */
    protected static boolean viewOrEditFileWithWPS(@NonNull ActivityStarterWrapper wrapper, String filePath, boolean readOnly, int requestCode) {
        Intent intent = new Intent();
        File file = new File(filePath);
        if (!file.isFile()) {
            AndroidUtils.showToast(wrapper.getContext(), "文件不存在");
            return false;
        }
        intent.setDataAndType(Uri.fromFile(file), FileViewerUtils.getMimeType(file));
        return doRealWPSViewOrEdit(wrapper, intent, readOnly, requestCode);
    }

    /**
     * 用WPS 打开应用
     *
     * @param context Context 对象
     * @param intent  设置好数据了类型的intent
     * @return
     */
    protected static boolean doRealWPSViewOrEdit(@NonNull ActivityStarterWrapper context, @NonNull Intent intent, boolean readOnly, int requestCode) {
        Bundle bundle = new Bundle();
        // 关闭时是否发送广播
        bundle.putBoolean(WPSModel.SEND_CLOSE_BROAD, true);
        // 保存时是否发送广播
        bundle.putBoolean(WPSModel.SEND_SAVE_BROAD, true);
        // 第三方应用的包名，用于对改应用合法性的验证
        bundle.putString(WPSModel.THIRD_PACKAGE, context.getPackageName());
        if (readOnly) {
            // 打开模式
            bundle.putString(WPSModel.OPEN_MODE, WPSModel.OpenMode.READ_ONLY);
        } else {
            // 打开模式
            bundle.putString(WPSModel.OPEN_MODE, WPSModel.OpenMode.NORMAL);
            // 修订模式
            bundle.putBoolean(WPSModel.ENTER_REVISE_MODE, true);
        }
        // 清除打开记录
        bundle.putBoolean(WPSModel.CLEAR_TRACE, true);
        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        intent.setAction(Intent.ACTION_VIEW);
        intent.setClassName(WPSModel.PackageName.NORMAL, WPSModel.ClassName.NORMAL);
        intent.putExtras(bundle);
        try {
            if (readOnly) {
                context.startActivity(Intent.createChooser(intent, "请选择WPS打开文件"));
            } else {
                context.startActivityForResult(Intent.createChooser(intent, "请选择WPS打开应用"), requestCode);
            }
        } catch (ActivityNotFoundException exception) {
            Log.d("WPSOpenUtils", exception.getMessage());
            exception.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 检测是否有安装 WPS
     *
     * @param context Context
     * @return
     */
    public static boolean checkWPSInstallation(Context context) {
        PackageManager packageManager1 = context.getPackageManager();
        Intent intent = new Intent();
        intent.setClassName(WPSModel.PackageName.NORMAL, WPSModel.ClassName.NORMAL);
        List<ResolveInfo> resolveInfos = packageManager1.queryIntentActivities(intent, PackageManager.MATCH_ALL);
        if (resolveInfos == null || resolveInfos.size() == 0) {
            return false;
        }
        boolean found = false;
        for (ResolveInfo info : resolveInfos) {
            if (info.activityInfo.packageName.toLowerCase().equalsIgnoreCase(WPSModel.PackageName.NORMAL) || info.activityInfo.name.toLowerCase().equalsIgnoreCase(WPSModel.ClassName.NORMAL)) {
                found = true;
                break;
            }
        }
        return found;
    }

    abstract static class ActivityStarterWrapper {

        protected Context context;

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

        abstract String getPackageName();

        abstract void startActivityForResult(Intent intent, int requestCode);

        abstract void startActivity(Intent intent);

        public Context getContext() {
            return context;
        }
    }

    protected static class ContextStartWrapper extends ActivityStarterWrapper {

        public ContextStartWrapper(Context context) {
            super(context);
        }

        @Override
        String getPackageName() {
            return context.getPackageName();
        }

        @Override
        void startActivityForResult(Intent intent, int requestCode) {
            context.startActivity(intent);
        }

        @Override
        void startActivity(Intent intent) {
            context.startActivity(intent);
        }
    }

    protected static class FragmentStartWrapper extends ActivityStarterWrapper {

        Fragment fragment;

        public FragmentStartWrapper(Fragment fragment) {
            super(fragment.getContext());
            this.fragment = fragment;
        }

        @Override
        String getPackageName() {
            return (fragment.getContext() == null) ? "" : fragment.getContext().getPackageName();
        }

        @Override
        void startActivityForResult(Intent intent, int requestCode) {
            fragment.startActivityForResult(intent, requestCode);
        }

        @Override
        void startActivity(Intent intent) {
            fragment.startActivity(intent);
        }
    }
}


{
        return viewOrEditFileWithWPS(new FragmentStartWrapper(fragment), filePath, readOnly, requestCode);
    }

package com.hanlyjiang.library.fileviewer.wps;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.util.Log;
import com.hanlyjiang.library.utils.AndroidUtils;
import com.hanlyjiang.library.utils.FileViewerUtils;
import java.io.File;
import java.util.List;

/**
 * WPS打开文件工具
 *
 * @author hanlyjiang on 2018/3/22-13:49.
 * @version 1.0
 */
public class WPSOpenUtils {

    /**
     * 用WPS 打开应用,注意此种方法无法获取
     *
     * @param filePath 设置好数据了类型的intent
     * @param context  Context 对象
     * @return
     */
    public static boolean viewOrEditFileWithWPS(@NonNull Context context, String filePath, boolean readOnly) {
        return viewOrEditFileWithWPS(new ContextStartWrapper(context), filePath, readOnly, 0);
    }

    /**
     * 用WPS 打开文档
     *
     * @param fragment
     * @param filePath    设置好数据了类型的intent
     * @param readOnly    true - 以只读模式打开文档通过StartActivity启动）； false - 以编辑模式打开文档（通过StartActivityForResult启动）
     * @param requestCode 指定通过StartActivityForResult启动时的requestCode
     * @return
     */
    public static boolean viewOrEditFileWithWPS(@NonNull Fragment fragment, String filePath, boolean readOnly, int requestCode)

    /**
     * 用WPS 打开应用
     *
     * @param filePath 设置好数据了类型的intent
     * @param wrapper  ActivityStarterWrapper 对象
     * @return
     */
    protected static boolean viewOrEditFileWithWPS(@NonNull ActivityStarterWrapper wrapper, String filePath, boolean readOnly, int requestCode) {
        Intent intent = new Intent();
        File file = new File(filePath);
        if (!file.isFile()) {
            AndroidUtils.showToast(wrapper.getContext(), "文件不存在");
            return false;
        }
        intent.setDataAndType(Uri.fromFile(file), FileViewerUtils.getMimeType(file));
        return doRealWPSViewOrEdit(wrapper, intent, readOnly, requestCode);
    }

    /**
     * 用WPS 打开应用
     *
     * @param context Context 对象
     * @param intent  设置好数据了类型的intent
     * @return
     */
    protected static boolean doRealWPSViewOrEdit(@NonNull ActivityStarterWrapper context, @NonNull Intent intent, boolean readOnly, int requestCode) {
        Bundle bundle = new Bundle();
        // 关闭时是否发送广播
        bundle.putBoolean(WPSModel.SEND_CLOSE_BROAD, true);
        // 保存时是否发送广播
        bundle.putBoolean(WPSModel.SEND_SAVE_BROAD, true);
        // 第三方应用的包名，用于对改应用合法性的验证
        bundle.putString(WPSModel.THIRD_PACKAGE, context.getPackageName());
        if (readOnly) {
            // 打开模式
            bundle.putString(WPSModel.OPEN_MODE, WPSModel.OpenMode.READ_ONLY);
        } else {
            // 打开模式
            bundle.putString(WPSModel.OPEN_MODE, WPSModel.OpenMode.NORMAL);
            // 修订模式
            bundle.putBoolean(WPSModel.ENTER_REVISE_MODE, true);
        }
        // 清除打开记录
        bundle.putBoolean(WPSModel.CLEAR_TRACE, true);
        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        intent.setAction(Intent.ACTION_VIEW);
        intent.setClassName(WPSModel.PackageName.NORMAL, WPSModel.ClassName.NORMAL);
        intent.putExtras(bundle);
        try {
            if (readOnly) {
                context.startActivity(Intent.createChooser(intent, "请选择WPS打开文件"));
            } else {
                context.startActivityForResult(Intent.createChooser(intent, "请选择WPS打开应用"), requestCode);
            }
        } catch (ActivityNotFoundException exception) {
            Log.d("WPSOpenUtils", exception.getMessage());
            exception.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 检测是否有安装 WPS
     *
     * @param context Context
     * @return
     */
    public static boolean checkWPSInstallation(Context context) {
        PackageManager packageManager1 = context.getPackageManager();
        Intent intent = new Intent();
        intent.setClassName(WPSModel.PackageName.NORMAL, WPSModel.ClassName.NORMAL);
        List<ResolveInfo> resolveInfos = packageManager1.queryIntentActivities(intent, PackageManager.MATCH_ALL);
        if (resolveInfos == null || resolveInfos.size() == 0) {
            return false;
        }
        boolean found = false;
        for (ResolveInfo info : resolveInfos) {
            if (info.activityInfo.packageName.toLowerCase().equalsIgnoreCase(WPSModel.PackageName.NORMAL) || info.activityInfo.name.toLowerCase().equalsIgnoreCase(WPSModel.ClassName.NORMAL)) {
                found = true;
                break;
            }
        }
        return found;
    }

    abstract static class ActivityStarterWrapper {

        protected Context context;

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

        abstract String getPackageName();

        abstract void startActivityForResult(Intent intent, int requestCode);

        abstract void startActivity(Intent intent);

        public Context getContext() {
            return context;
        }
    }

    protected static class ContextStartWrapper extends ActivityStarterWrapper {

        public ContextStartWrapper(Context context) {
            super(context);
        }

        @Override
        String getPackageName() {
            return context.getPackageName();
        }

        @Override
        void startActivityForResult(Intent intent, int requestCode) {
            context.startActivity(intent);
        }

        @Override
        void startActivity(Intent intent) {
            context.startActivity(intent);
        }
    }

    protected static class FragmentStartWrapper extends ActivityStarterWrapper {

        Fragment fragment;

        public FragmentStartWrapper(Fragment fragment) {
            super(fragment.getContext());
            this.fragment = fragment;
        }

        @Override
        String getPackageName() {
            return (fragment.getContext() == null) ? "" : fragment.getContext().getPackageName();
        }

        @Override
        void startActivityForResult(Intent intent, int requestCode) {
            fragment.startActivityForResult(intent, requestCode);
        }

        @Override
        void startActivity(Intent intent) {
            fragment.startActivity(intent);
        }
    }
}


package com.hanlyjiang.library.fileviewer.wps;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.util.Log;
import com.hanlyjiang.library.utils.AndroidUtils;
import com.hanlyjiang.library.utils.FileViewerUtils;
import java.io.File;
import java.util.List;

/**
 * WPS打开文件工具
 *
 * @author hanlyjiang on 2018/3/22-13:49.
 * @version 1.0
 */
public class WPSOpenUtils {

    /**
     * 用WPS 打开应用,注意此种方法无法获取
     *
     * @param filePath 设置好数据了类型的intent
     * @param context  Context 对象
     * @return
     */
    public static boolean viewOrEditFileWithWPS(@NonNull Context context, String filePath, boolean readOnly) {
        return viewOrEditFileWithWPS(new ContextStartWrapper(context), filePath, readOnly, 0);
    }

    /**
     * 用WPS 打开文档
     *
     * @param fragment
     * @param filePath    设置好数据了类型的intent
     * @param readOnly    true - 以只读模式打开文档通过StartActivity启动）； false - 以编辑模式打开文档（通过StartActivityForResult启动）
     * @param requestCode 指定通过StartActivityForResult启动时的requestCode
     * @return
     */
    public static boolean viewOrEditFileWithWPS(@NonNull Fragment fragment, String filePath, boolean readOnly, int requestCode) {
        return viewOrEditFileWithWPS(new FragmentStartWrapper(fragment), filePath, readOnly, requestCode);
    }

    /**
     * 用WPS 打开应用
     *
     * @param filePath 设置好数据了类型的intent
     * @param wrapper  ActivityStarterWrapper 对象
     * @return
     */
    protected static boolean viewOrEditFileWithWPS(@NonNull ActivityStarterWrapper wrapper, String filePath, boolean readOnly, int requestCode) {
        Intent intent

        File file = new File(filePath);
        if (!file.isFile()) {
            AndroidUtils.showToast(wrapper.getContext(), "文件不存在");
            return false;
        }
        intent.setDataAndType(Uri.fromFile(file), FileViewerUtils.getMimeType(file));
        return doRealWPSViewOrEdit(wrapper, intent, readOnly, requestCode);
    }

    /**
     * 用WPS 打开应用
     *
     * @param context Context 对象
     * @param intent  设置好数据了类型的intent
     * @return
     */
    protected static boolean doRealWPSViewOrEdit(@NonNull ActivityStarterWrapper context, @NonNull Intent intent, boolean readOnly, int requestCode) {
        Bundle bundle = new Bundle();
        // 关闭时是否发送广播
        bundle.putBoolean(WPSModel.SEND_CLOSE_BROAD, true);
        // 保存时是否发送广播
        bundle.putBoolean(WPSModel.SEND_SAVE_BROAD, true);
        // 第三方应用的包名，用于对改应用合法性的验证
        bundle.putString(WPSModel.THIRD_PACKAGE, context.getPackageName());
        if (readOnly) {
            // 打开模式
            bundle.putString(WPSModel.OPEN_MODE, WPSModel.OpenMode.READ_ONLY);
        } else {
            // 打开模式
            bundle.putString(WPSModel.OPEN_MODE, WPSModel.OpenMode.NORMAL);
            // 修订模式
            bundle.putBoolean(WPSModel.ENTER_REVISE_MODE, true);
        }
        // 清除打开记录
        bundle.putBoolean(WPSModel.CLEAR_TRACE, true);
        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        intent.setAction(Intent.ACTION_VIEW);
        intent.setClassName(WPSModel.PackageName.NORMAL, WPSModel.ClassName.NORMAL);
        intent.putExtras(bundle);
        try {
            if (readOnly) {
                context.startActivity(Intent.createChooser(intent, "请选择WPS打开文件"));
            } else {
                context.startActivityForResult(Intent.createChooser(intent, "请选择WPS打开应用"), requestCode);
            }
        } catch (ActivityNotFoundException exception) {
            Log.d("WPSOpenUtils", exception.getMessage());
            exception.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 检测是否有安装 WPS
     *
     * @param context Context
     * @return
     */
    public static boolean checkWPSInstallation(Context context) {
        PackageManager packageManager1 = context.getPackageManager();
        Intent intent = new Intent();
        intent.setClassName(WPSModel.PackageName.NORMAL, WPSModel.ClassName.NORMAL);
        List<ResolveInfo> resolveInfos = packageManager1.queryIntentActivities(intent, PackageManager.MATCH_ALL);
        if (resolveInfos == null || resolveInfos.size() == 0) {
            return false;
        }
        boolean found = false;
        for (ResolveInfo info : resolveInfos) {
            if (info.activityInfo.packageName.toLowerCase().equalsIgnoreCase(WPSModel.PackageName.NORMAL) || info.activityInfo.name.toLowerCase().equalsIgnoreCase(WPSModel.ClassName.NORMAL)) {
                found = true;
                break;
            }
        }
        return found;
    }

    abstract static class ActivityStarterWrapper {

        protected Context context;

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

        abstract String getPackageName();

        abstract void startActivityForResult(Intent intent, int requestCode);

        abstract void startActivity(Intent intent);

        public Context getContext() {
            return context;
        }
    }

    protected static class ContextStartWrapper extends ActivityStarterWrapper {

        public ContextStartWrapper(Context context) {
            super(context);
        }

        @Override
        String getPackageName() {
            return context.getPackageName();
        }

        @Override
        void startActivityForResult(Intent intent, int requestCode) {
            context.startActivity(intent);
        }

        @Override
        void startActivity(Intent intent) {
            context.startActivity(intent);
        }
    }

    protected static class FragmentStartWrapper extends ActivityStarterWrapper {

        Fragment fragment;

        public FragmentStartWrapper(Fragment fragment) {
            super(fragment.getContext());
            this.fragment = fragment;
        }

        @Override
        String getPackageName() {
            return (fragment.getContext() == null) ? "" : fragment.getContext().getPackageName();
        }

        @Override
        void startActivityForResult(Intent intent, int requestCode) {
            fragment.startActivityForResult(intent, requestCode);
        }

        @Override
        void startActivity(Intent intent) {
            fragment.startActivity(intent);
        }
    }
}


public static boolean viewOrEditFileWithWPS(@NonNull Context context, String filePath, boolean readOnly) {
        return viewOrEditFileWithWPS(new ContextStartWrapper(context), filePath, readOnly, 0);
    }

package com.hanlyjiang.library.fileviewer.wps;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.util.Log;
import com.hanlyjiang.library.utils.AndroidUtils;
import com.hanlyjiang.library.utils.FileViewerUtils;
import java.io.File;
import java.util.List;

/**
 * WPS打开文件工具
 *
 * @author hanlyjiang on 2018/3/22-13:49.
 * @version 1.0
 */
public class WPSOpenUtils {

    /**
     * 用WPS 打开应用,注意此种方法无法获取
     *
     * @param filePath 设置好数据了类型的intent
     * @param context  Context 对象
     * @return
     */

    /**
     * 用WPS 打开文档
     *
     * @param fragment
     * @param filePath    设置好数据了类型的intent
     * @param readOnly    true - 以只读模式打开文档通过StartActivity启动）； false - 以编辑模式打开文档（通过StartActivityForResult启动）
     * @param requestCode 指定通过StartActivityForResult启动时的requestCode
     * @return
     */
    public static boolean viewOrEditFileWithWPS(@NonNull Fragment fragment, String filePath, boolean readOnly, int requestCode) {
        return viewOrEditFileWithWPS(new FragmentStartWrapper(fragment), filePath, readOnly, requestCode);
    }

    /**
     * 用WPS 打开应用
     *
     * @param filePath 设置好数据了类型的intent
     * @param wrapper  ActivityStarterWrapper 对象
     * @return
     */
    protected static boolean viewOrEditFileWithWPS(@NonNull ActivityStarterWrapper wrapper, String filePath, boolean readOnly, int requestCode) {
        Intent intent = new Intent();
        File file = new File(filePath);
        if (!file.isFile()) {
            AndroidUtils.showToast(wrapper.getContext(), "文件不存在");
            return false;
        }
        intent.setDataAndType(Uri.fromFile(file), FileViewerUtils.getMimeType(file));
        return doRealWPSViewOrEdit(wrapper, intent, readOnly, requestCode);
    }

    /**
     * 用WPS 打开应用
     *
     * @param context Context 对象
     * @param intent  设置好数据了类型的intent
     * @return
     */
    protected static boolean doRealWPSViewOrEdit(@NonNull ActivityStarterWrapper context, @NonNull Intent intent, boolean readOnly, int requestCode) {
        Bundle bundle = new Bundle();
        // 关闭时是否发送广播
        bundle.putBoolean(WPSModel.SEND_CLOSE_BROAD, true);
        // 保存时是否发送广播
        bundle.putBoolean(WPSModel.SEND_SAVE_BROAD, true);
        // 第三方应用的包名，用于对改应用合法性的验证
        bundle.putString(WPSModel.THIRD_PACKAGE, context.getPackageName());
        if (readOnly) {
            // 打开模式
            bundle.putString(WPSModel.OPEN_MODE, WPSModel.OpenMode.READ_ONLY);
        } else {
            // 打开模式
            bundle.putString(WPSModel.OPEN_MODE, WPSModel.OpenMode.NORMAL);
            // 修订模式
            bundle.putBoolean(WPSModel.ENTER_REVISE_MODE, true);
        }
        // 清除打开记录
        bundle.putBoolean(WPSModel.CLEAR_TRACE, true);
        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        intent.setAction(Intent.ACTION_VIEW);
        intent.setClassName(WPSModel.PackageName.NORMAL, WPSModel.ClassName.NORMAL);
        intent.putExtras(bundle);
        try {
            if (readOnly) {
                context.startActivity(Intent.createChooser(intent, "请选择WPS打开文件"));
            } else {
                context.startActivityForResult(Intent.createChooser(intent, "请选择WPS打开应用"), requestCode);
            }
        } catch (ActivityNotFoundException exception) {
            Log.d("WPSOpenUtils", exception.getMessage());
            exception.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 检测是否有安装 WPS
     *
     * @param context Context
     * @return
     */
    public static boolean checkWPSInstallation(Context context) {
        PackageManager packageManager1 = context.getPackageManager();
        Intent intent = new Intent();
        intent.setClassName(WPSModel.PackageName.NORMAL, WPSModel.ClassName.NORMAL);
        List<ResolveInfo> resolveInfos = packageManager1.queryIntentActivities(intent, PackageManager.MATCH_ALL);
        if (resolveInfos == null || resolveInfos.size() == 0) {
            return false;
        }
        boolean found = false;
        for (ResolveInfo info : resolveInfos) {
            if (info.activityInfo.packageName.toLowerCase().equalsIgnoreCase(WPSModel.PackageName.NORMAL) || info.activityInfo.name.toLowerCase().equalsIgnoreCase(WPSModel.ClassName.NORMAL)) {
                found = true;
                break;
            }
        }
        return found;
    }

    abstract static class ActivityStarterWrapper {

        protected Context context;

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

        abstract String getPackageName();

        abstract void startActivityForResult(Intent intent, int requestCode);

        abstract void startActivity(Intent intent);

        public Context getContext() {
            return context;
        }
    }

    protected static class ContextStartWrapper extends ActivityStarterWrapper {

        public ContextStartWrapper(Context context) {
            super(context);
        }

        @Override
        String getPackageName() {
            return context.getPackageName();
        }

        @Override
        void startActivityForResult(Intent intent, int requestCode) {
            context.startActivity(intent);
        }

        @Override
        void startActivity(Intent intent) {
            context.startActivity(intent);
        }
    }

    protected static class FragmentStartWrapper extends ActivityStarterWrapper {

        Fragment fragment;

        public FragmentStartWrapper(Fragment fragment) {
            super(fragment.getContext());
            this.fragment = fragment;
        }

        @Override
        String getPackageName() {
            return (fragment.getContext() == null) ? "" : fragment.getContext().getPackageName();
        }

        @Override
        void startActivityForResult(Intent intent, int requestCode) {
            fragment.startActivityForResult(intent, requestCode);
        }

        @Override
        void startActivity(Intent intent) {
            fragment.startActivity(intent);
        }
    }
}


try {
            if (readOnly) {
                context.startActivity(Intent.createChooser(intent, "请选择WPS打开文件"));
            } else {
                context.startActivityForResult(Intent.createChooser(intent, "请选择WPS打开应用"), requestCode);
            }
        } catch (ActivityNotFoundException exception) {
            Log.d("WPSOpenUtils", exception.getMessage());
            exception.printStackTrace();
            return false;
        }

package com.hanlyjiang.library.fileviewer.wps;

import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.Fragment;
import android.util.Log;
import com.hanlyjiang.library.utils.AndroidUtils;
import com.hanlyjiang.library.utils.FileViewerUtils;
import java.io.File;
import java.util.List;

/**
 * WPS打开文件工具
 *
 * @author hanlyjiang on 2018/3/22-13:49.
 * @version 1.0
 */
public class WPSOpenUtils {

    /**
     * 用WPS 打开应用,注意此种方法无法获取
     *
     * @param filePath 设置好数据了类型的intent
     * @param context  Context 对象
     * @return
     */
    public static boolean viewOrEditFileWithWPS(@NonNull Context context, String filePath, boolean readOnly) {
        return viewOrEditFileWithWPS(new ContextStartWrapper(context), filePath, readOnly, 0);
    }

    /**
     * 用WPS 打开文档
     *
     * @param fragment
     * @param filePath    设置好数据了类型的intent
     * @param readOnly    true - 以只读模式打开文档通过StartActivity启动）； false - 以编辑模式打开文档（通过StartActivityForResult启动）
     * @param requestCode 指定通过StartActivityForResult启动时的requestCode
     * @return
     */
    public static boolean viewOrEditFileWithWPS(@NonNull Fragment fragment, String filePath, boolean readOnly, int requestCode) {
        return viewOrEditFileWithWPS(new FragmentStartWrapper(fragment), filePath, readOnly, requestCode);
    }

    /**
     * 用WPS 打开应用
     *
     * @param filePath 设置好数据了类型的intent
     * @param wrapper  ActivityStarterWrapper 对象
     * @return
     */
    protected static boolean viewOrEditFileWithWPS(@NonNull ActivityStarterWrapper wrapper, String filePath, boolean readOnly, int requestCode) {
        Intent intent = new Intent();
        File file = new File(filePath);
        if (!file.isFile()) {
            AndroidUtils.showToast(wrapper.getContext(), "文件不存在");
            return false;
        }
        intent.setDataAndType(Uri.fromFile(file), FileViewerUtils.getMimeType(file));
        return doRealWPSViewOrEdit(wrapper, intent, readOnly, requestCode);
    }

    /**
     * 用WPS 打开应用
     *
     * @param context Context 对象
     * @param intent  设置好数据了类型的intent
     * @return
     */
    protected static boolean doRealWPSViewOrEdit(@NonNull ActivityStarterWrapper context, @NonNull Intent intent, boolean readOnly, int requestCode) {
        Bundle bundle = new Bundle();
        // 关闭时是否发送广播
        bundle.putBoolean(WPSModel.SEND_CLOSE_BROAD, true);
        // 保存时是否发送广播
        bundle.putBoolean(WPSModel.SEND_SAVE_BROAD, true);
        // 第三方应用的包名，用于对改应用合法性的验证
        bundle.putString(WPSModel.THIRD_PACKAGE, context.getPackageName());
        if (readOnly) {
            // 打开模式
            bundle.putString(WPSModel.OPEN_MODE, WPSModel.OpenMode.READ_ONLY);
        } else {
            // 打开模式
            bundle.putString(WPSModel.OPEN_MODE, WPSModel.OpenMode.NORMAL);
            // 修订模式
            bundle.putBoolean(WPSModel.ENTER_REVISE_MODE, true);
        }
        // 清除打开记录
        bundle.putBoolean(WPSModel.CLEAR_TRACE, true);
        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        intent.setAction(Intent.ACTION_VIEW);
        intent.setClassName(WPSModel.PackageName.NORMAL, WPSModel.ClassName.NORMAL);
        intent.putExtras(bundle);

        return true;
    }

    /**
     * 检测是否有安装 WPS
     *
     * @param context Context
     * @return
     */
    public static boolean checkWPSInstallation(Context context) {
        PackageManager packageManager1 = context.getPackageManager();
        Intent intent = new Intent();
        intent.setClassName(WPSModel.PackageName.NORMAL, WPSModel.ClassName.NORMAL);
        List<ResolveInfo> resolveInfos = packageManager1.queryIntentActivities(intent, PackageManager.MATCH_ALL);
        if (resolveInfos == null || resolveInfos.size() == 0) {
            return false;
        }
        boolean found = false;
        for (ResolveInfo info : resolveInfos) {
            if (info.activityInfo.packageName.toLowerCase().equalsIgnoreCase(WPSModel.PackageName.NORMAL) || info.activityInfo.name.toLowerCase().equalsIgnoreCase(WPSModel.ClassName.NORMAL)) {
                found = true;
                break;
            }
        }
        return found;
    }

    abstract static class ActivityStarterWrapper {

        protected Context context;

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

        abstract String getPackageName();

        abstract void startActivityForResult(Intent intent, int requestCode);

        abstract void startActivity(Intent intent);

        public Context getContext() {
            return context;
        }
    }

    protected static class ContextStartWrapper extends ActivityStarterWrapper {

        public ContextStartWrapper(Context context) {
            super(context);
        }

        @Override
        String getPackageName() {
            return context.getPackageName();
        }

        @Override
        void startActivityForResult(Intent intent, int requestCode) {
            context.startActivity(intent);
        }

        @Override
        void startActivity(Intent intent) {
            context.startActivity(intent);
        }
    }

    protected static class FragmentStartWrapper extends ActivityStarterWrapper {

        Fragment fragment;

        public FragmentStartWrapper(Fragment fragment) {
            super(fragment.getContext());
            this.fragment = fragment;
        }

        @Override
        String getPackageName() {
            return (fragment.getContext() == null) ? "" : fragment.getContext().getPackageName();
        }

        @Override
        void startActivityForResult(Intent intent, int requestCode) {
            fragment.startActivityForResult(intent, requestCode);
        }

        @Override
        void startActivity(Intent intent) {
            fragment.startActivity(intent);
        }
    }
}


@Override
    public void load() throws IOException

// This source code is available under agreement available at
// %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
// You should have received a copy of the agreement
// along with this program; if not, write to Talend SA
// 9 rue Pages 92150 Suresnes, France
package org.talend.designer.maven.job.setting.repository.standalone.page;

import java.io.IOException;
import org.eclipse.core.resources.IFile;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceStore;
import org.eclipse.swt.widgets.Composite;
import org.talend.designer.maven.job.setting.project.page.standalone.StandaloneJobPomProjectSettingPage;
import org.talend.designer.maven.ui.setting.repository.RepositoryMavenSettingStore;

/**
 * DOC ggu class global comment. Detailled comment
 */
public class StandaloneJobPomRepositorySettingPage extends StandaloneJobPomProjectSettingPage {

    private IFile scriptFile;

    public StandaloneJobPomRepositorySettingPage(IFile scriptFile) {
        super();
        this.scriptFile = scriptFile;
    }

    public void createControl(Composite parent) {
        noDefaultAndApplyButton();
        this.setTitle(this.getHeadTitle());
        super.createControl(parent);
    }

    @Override
    protected IPreferenceStore doGetPreferenceStore() {
        return new RepositoryMavenSettingStore(this.getPreferenceKey(), this.scriptFile);
    }

{
        if (getPreferenceStore() instanceof PreferenceStore) {
            ((PreferenceStore) getPreferenceStore()).load();
        }
        super.load();
    }

    @Override
    public void save() throws IOException {
        if (getPreferenceStore() instanceof PreferenceStore) {
            ((PreferenceStore) getPreferenceStore()).save();
        }
        super.save();
    }
}


{
        if (getPreferenceStore() instanceof PreferenceStore) {
            ((PreferenceStore) getPreferenceStore()).save();
        }
        super.save();
    }

// This source code is available under agreement available at
// %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
// You should have received a copy of the agreement
// along with this program; if not, write to Talend SA
// 9 rue Pages 92150 Suresnes, France
package org.talend.designer.maven.job.setting.repository.standalone.page;

import java.io.IOException;
import org.eclipse.core.resources.IFile;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceStore;
import org.eclipse.swt.widgets.Composite;
import org.talend.designer.maven.job.setting.project.page.standalone.StandaloneJobPomProjectSettingPage;
import org.talend.designer.maven.ui.setting.repository.RepositoryMavenSettingStore;

/**
 * DOC ggu class global comment. Detailled comment
 */
public class StandaloneJobPomRepositorySettingPage extends StandaloneJobPomProjectSettingPage {

    private IFile scriptFile;

    public StandaloneJobPomRepositorySettingPage(IFile scriptFile) {
        super();
        this.scriptFile = scriptFile;
    }

    public void createControl(Composite parent) {
        noDefaultAndApplyButton();
        this.setTitle(this.getHeadTitle());
        super.createControl(parent);
    }

    @Override
    protected IPreferenceStore doGetPreferenceStore() {
        return new RepositoryMavenSettingStore(this.getPreferenceKey(), this.scriptFile);
    }

    @Override
    public void load() throws IOException {
        if (getPreferenceStore() instanceof PreferenceStore) {
            ((PreferenceStore) getPreferenceStore()).load();
        }
        super.load();
    }

    @Override
    public void save() throws IOException


// This source code is available under agreement available at
// %InstallDIR%\features\org.talend.rcp.branding.%PRODUCTNAME%\%PRODUCTNAME%license.txt
// You should have received a copy of the agreement
// along with this program; if not, write to Talend SA
// 9 rue Pages 92150 Suresnes, France
package org.talend.designer.maven.job.setting.repository.standalone.page;

import java.io.IOException;
import org.eclipse.core.resources.IFile;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceStore;
import org.eclipse.swt.widgets.Composite;
import org.talend.designer.maven.job.setting.project.page.standalone.StandaloneJobPomProjectSettingPage;
import org.talend.designer.maven.ui.setting.repository.RepositoryMavenSettingStore;

/**
 * DOC ggu class global comment. Detailled comment
 */
public class StandaloneJobPomRepositorySettingPage extends StandaloneJobPomProjectSettingPage {

    private IFile scriptFile;

    public StandaloneJobPomRepositorySettingPage(IFile scriptFile) {
        super();
        this.scriptFile = scriptFile;
    }

    public void createControl(Composite parent) {
        noDefaultAndApplyButton();
        this.setTitle(this.getHeadTitle());
        super.createControl(parent);
    }

    @Override
    protected IPreferenceStore doGetPreferenceStore() {
        return new RepositoryMavenSettingStore(this.getPreferenceKey(), this.scriptFile);
    }

    @Override
    public void load() throws IOException {
        if (getPreferenceStore() instanceof PreferenceStore) {
            ((PreferenceStore) getPreferenceStore()).load();
        }
        sup

    }

    @Override
    public void save() throws IOException {
        if (getPreferenceStore() instanceof PreferenceStore) {
            ((PreferenceStore) getPreferenceStore()).save();
        }
        super.save();
    }
}


@OptionsByType.Default
    public static RetryFrequency standard()

/*
 * File: RetryFrequency.java
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 */
package com.oracle.bedrock.deferred.options;

import com.oracle.bedrock.Option;
import com.oracle.bedrock.OptionsByType;
import com.oracle.bedrock.deferred.Ensured;
import com.oracle.bedrock.util.Duration;
import com.oracle.bedrock.util.ExponentialIterator;
import com.oracle.bedrock.util.FibonacciIterator;
import com.oracle.bedrock.util.MappingIterator;
import com.oracle.bedrock.util.PerpetualIterator;
import com.oracle.bedrock.util.RandomIterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * An {@link Option} to define frequency (durations to wait) when attempting to resolve an
 * {@link Ensured} when calling {@link Ensured#get()}
 * <p>
 * Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
 *
 * @author Brian Oliver
 *
 * @see Ensured
 */
public class RetryFrequency implements Option {

    /**
     * The {@link Duration}s for the {@link RetryFrequency}.
     */
    private Iterable<Duration> durations;

    /**
     * Privately constructs a {@link RetryFrequency} {@link Option}.
     *
     * @param durations  the {@link Duration}s for the {@link RetryFrequency}
     */
    private RetryFrequency(Iterable<Duration> durations) {
        this.durations = durations;
    }

    /**
     * Obtains the {@link Duration}s for the {@link RetryFrequency}.
     *
     * @return  the {@link Duration}s for the {@link RetryFrequency}
     */
    public Iterable<Duration> get() {
        return durations;
    }

    /**
     * Obtains a randomized version of this {@link RetryFrequency}, where by
     * values returned from the {@link #get()} {@link Iterable} will be
     * randomized values between 0 and the values provided by this
     * {@link RetryFrequency}.
     *
     * @return  a new randomized {@link RetryFrequency}
     */
    public RetryFrequency randomized() {
        return new RetryFrequency(() -> new MappingIterator<>(new RandomIterator(new MappingIterator<>(this.get().iterator(), duration -> duration.to(TimeUnit.MILLISECONDS))), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
    }

    /**
     * Obtains the standard {@link RetryFrequency} for retrying every 250 milliseconds.
     *
     * @return  a {@link RetryFrequency}
     */

{
        return every(250, TimeUnit.MILLISECONDS);
    }

    /**
     * Obtains a {@link RetryFrequency} based on the specified {@link Duration}s.
     *
     * @param durations  the {@link Duration}s
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency of(Iterable<Duration> durations) {
        return new RetryFrequency(durations);
    }

    /**
     * Obtains a {@link RetryFrequency} based on the specified times.
     *

     * For example, to retry at 0, 5, 10 and 20 seconds, use the following
     *
     * RetryFrequency.of(TimeUnit.SECONDS, 0, 5, 10, 20);
     *
     *
     * @param unit       the {@link TimeUnit} for the durations
     * @param instants   the instants in time  (will be sorted)
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency of(TimeUnit unit, long... instants) {
        ArrayList<Long> frequencies = new ArrayList<>();
        if (instants.length > 0) {
            // ensure we have the instants in a sorted order
            Arrays.sort(instants);
            // add the lowest
            frequencies.add(instants[0]);
            long sum = instants[0];
            for (int i = 1; i < instants.length; i++) {
                long delta = instants[i] - sum;
                sum = sum + delta;
                frequencies.add(delta);
            }
            return new RetryFrequency(() -> new MappingIterator<>(frequencies.iterator(), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
        } else {
            throw new IllegalArgumentException("Failed to specify any times for the RetryFrequency");
        }
    }

    /**
     * Obtains a {@link RetryFrequency} representing fixed retry {@link Duration}s.
     *
     * @param duration  the duration
     * @param unit      the {@link TimeUnit} for the {@link Duration}
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency every(long duration, TimeUnit unit) {
        return new RetryFrequency(() -> new PerpetualIterator<>(Duration.of(duration, unit)));
    }

    /**
     * Obtains a {@link RetryFrequency} based on the Fibonacci sequence.
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency fibonacci() {
        return new RetryFrequency(() -> new MappingIterator<>(new FibonacciIterator(), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
    }

    /**
     * Obtains a {@link RetryFrequency} based on an exponential sequence, starting at 0
     * with a 50% growth rate.
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency exponential() {
        return exponential(0, 50);
    }

    /**
     * Obtains a {@link RetryFrequency} based on an exponential sequence.
     *
     * @param initial     the initial (starting) value
     * @param percentage  the growth rate percentage (typically between 0.0 and 100.0)
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency exponential(double initial, double percentage) {
        return new RetryFrequency(() -> new MappingIterator<>(new ExponentialIterator(initial, percentage), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
    }
}


{
        return new RetryFrequency(() -> new PerpetualIterator<>(Duration.of(duration, unit)));
    }

/*
 * File: RetryFrequency.java
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 */
package com.oracle.bedrock.deferred.options;

import com.oracle.bedrock.Option;
import com.oracle.bedrock.OptionsByType;
import com.oracle.bedrock.deferred.Ensured;
import com.oracle.bedrock.util.Duration;
import com.oracle.bedrock.util.ExponentialIterator;
import com.oracle.bedrock.util.FibonacciIterator;
import com.oracle.bedrock.util.MappingIterator;
import com.oracle.bedrock.util.PerpetualIterator;
import com.oracle.bedrock.util.RandomIterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * An {@link Option} to define frequency (durations to wait) when attempting to resolve an
 * {@link Ensured} when calling {@link Ensured#get()}
 *

 * Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
 *
 * @author Brian Oliver
 *
 * @see Ensured
 */
public class RetryFrequency implements Option {

    /**
     * The {@link Duration}s for the {@link RetryFrequency}.
     */
    private Iterable<Duration> durations;

    /**
     * Privately constructs a {@link RetryFrequency} {@link Option}.
     *
     * @param durations  the {@link Duration}s for the {@link RetryFrequency}
     */
    private RetryFrequency(Iterable<Duration> durations) {
        this.durations = durations;
    }

    /**
     * Obtains the {@link Duration}s for the {@link RetryFrequency}.
     *
     * @return  the {@link Duration}s for the {@link RetryFrequency}
     */
    public Iterable<Duration> get() {
        return durations;
    }

    /**
     * Obtains a randomized version of this {@link RetryFrequency}, where by
     * values returned from the {@link #get()} {@link Iterable} will be
     * randomized values between 0 and the values provided by this
     * {@link RetryFrequency}.
     *
     * @return  a new randomized {@link RetryFrequency}
     */
    public RetryFrequency randomized() {
        return new RetryFrequency(() -> new MappingIterator<>(new RandomIterator(new MappingIterator<>(this.get().iterator(), duration -> duration.to(TimeUnit.MILLISECONDS))), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
    }

    /**
     * Obtains the standard {@link RetryFrequency} for retrying every 250 milliseconds.
     *
     * @return  a {@link RetryFrequency}
     */
    @OptionsByType.Default
    public static RetryFrequency standard() {
        return every(250, TimeUnit.MILLISECONDS);
    }

    /**
     * Obtains a {@link RetryFrequency} based on the specified {@link Duration}s.
     *
     * @param durations  the {@link Duration}s
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency of(Iterable<Duration> durations) {
        return new RetryFrequency(durations);
    }

    /**
     * Obtains a {@link RetryFrequency} based on the specified times.
     *

     * For example, to retry at 0, 5, 10 and 20 seconds, use the following
     *
     * RetryFrequency.of(TimeUnit.SECONDS, 0, 5, 10, 20);
     *
     *
     * @param unit       the {@link TimeUnit} for the durations
     * @param instants   the instants in time  (will be sorted)
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency of(TimeUnit unit, long... instants) {
        ArrayList<Long> frequencies = new ArrayList<>();
        if (instants.length > 0) {
            // ensure we have the instants in a sorted order
            Arrays.sort(instants);
            // add the lowest
            frequencies.add(instants[0]);
            long sum = instants[0];
            for (int i = 1; i < instants.length; i++) {
                long delta = instants[i] - sum;
                sum = sum + delta;
                frequencies.add(delta);
            }
            return new RetryFrequency(() -> new MappingIterator<>(frequencies.iterator(), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
        } else {
            throw new IllegalArgumentException("Failed to specify any times for the RetryFrequency");
        }
    }

    /**
     * Obtains a {@link RetryFrequency} representing fixed retry {@link Duration}s.
     *
     * @param duration  the duration
     * @param unit      the {@link TimeUnit} for the {@link Duration}
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency every(long duration, TimeUnit unit)

    /**
     * Obtains a {@link RetryFrequency} based on the Fibonacci sequence.
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency fibonacci() {
        return new RetryFrequency(() -> new MappingIterator<>(new FibonacciIterator(), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
    }

    /**
     * Obtains a {@link RetryFrequency} based on an exponential sequence, starting at 0
     * with a 50% growth rate.
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency exponential() {
        return exponential(0, 50);
    }

    /**
     * Obtains a {@link RetryFrequency} based on an exponential sequence.
     *
     * @param initial     the initial (starting) value
     * @param percentage  the growth rate percentage (typically between 0.0 and 100.0)
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency exponential(double initial, double percentage) {
        return new RetryFrequency(() -> new MappingIterator<>(new ExponentialIterator(initial, percentage), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
    }
}


FibonacciIterator(), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));

/*
 * File: RetryFrequency.java
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 */
package com.oracle.bedrock.deferred.options;

import com.oracle.bedrock.Option;
import com.oracle.bedrock.OptionsByType;
import com.oracle.bedrock.deferred.Ensured;
import com.oracle.bedrock.util.Duration;
import com.oracle.bedrock.util.ExponentialIterator;
import com.oracle.bedrock.util.FibonacciIterator;
import com.oracle.bedrock.util.MappingIterator;
import com.oracle.bedrock.util.PerpetualIterator;
import com.oracle.bedrock.util.RandomIterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * An {@link Option} to define frequency (durations to wait) when attempting to resolve an
 * {@link Ensured} when calling {@link Ensured#get()}
 *

 * Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
 *
 * @author Brian Oliver
 *
 * @see Ensured
 */
public class RetryFrequency implements Option {

    /**
     * The {@link Duration}s for the {@link RetryFrequency}.
     */
    private Iterable<Duration> durations;

    /**
     * Privately constructs a {@link RetryFrequency} {@link Option}.
     *
     * @param durations  the {@link Duration}s for the {@link RetryFrequency}
     */
    private RetryFrequency(Iterable<Duration> durations) {
        this.durations = durations;
    }

    /**
     * Obtains the {@link Duration}s for the {@link RetryFrequency}.
     *
     * @return  the {@link Duration}s for the {@link RetryFrequency}
     */
    public Iterable<Duration> get() {
        return durations;
    }

    /**
     * Obtains a randomized version of this {@link RetryFrequency}, where by
     * values returned from the {@link #get()} {@link Iterable} will be
     * randomized values between 0 and the values provided by this
     * {@link RetryFrequency}.
     *
     * @return  a new randomized {@link RetryFrequency}
     */
    public RetryFrequency randomized() {
        return new RetryFrequency(() -> new MappingIterator<>(new RandomIterator(new MappingIterator<>(this.get().iterator(), duration -> duration.to(TimeUnit.MILLISECONDS))), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
    }

    /**
     * Obtains the standard {@link RetryFrequency} for retrying every 250 milliseconds.
     *
     * @return  a {@link RetryFrequency}
     */
    @OptionsByType.Default
    public static RetryFrequency standard() {
        return every(250, TimeUnit.MILLISECONDS);
    }

    /**
     * Obtains a {@link RetryFrequency} based on the specified {@link Duration}s.
     *
     * @param durations  the {@link Duration}s
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency of(Iterable<Duration> durations) {
        return new RetryFrequency(durations);
    }

    /**
     * Obtains a {@link RetryFrequency} based on the specified times.
     *

     * For example, to retry at 0, 5, 10 and 20 seconds, use the following
     *
     * RetryFrequency.of(TimeUnit.SECONDS, 0, 5, 10, 20);
     *
     *
     * @param unit       the {@link TimeUnit} for the durations
     * @param instants   the instants in time  (will be sorted)
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency of(TimeUnit unit, long... instants) {
        ArrayList<Long> frequencies = new ArrayList<>();
        if (instants.length > 0) {
            // ensure we have the instants in a sorted order
            Arrays.sort(instants);
            // add the lowest
            frequencies.add(instants[0]);
            long sum = instants[0];
            for (int i = 1; i < instants.length; i++) {
                long delta = instants[i] - sum;
                sum = sum + delta;
                frequencies.add(delta);
            }
            return new RetryFrequency(() -> new MappingIterator<>(frequencies.iterator(), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
        } else {
            throw new IllegalArgumentException("Failed to specify any times for the RetryFrequency");
        }
    }

    /**
     * Obtains a {@link RetryFrequency} representing fixed retry {@link Duration}s.
     *
     * @param duration  the duration
     * @param unit      the {@link TimeUnit} for the {@link Duration}
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency every(long duration, TimeUnit unit) {
        return new RetryFrequency(() -> new PerpetualIterator<>(Duration.of(duration, unit)));
    }

    /**
     * Obtains a {@link RetryFrequency} based on the Fibonacci sequence.
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency fibonacci() {
        return new RetryFrequency(() -> new MappingIterator<>(new

    }

    /**
     * Obtains a {@link RetryFrequency} based on an exponential sequence, starting at 0
     * with a 50% growth rate.
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency exponential() {
        return exponential(0, 50);
    }

    /**
     * Obtains a {@link RetryFrequency} based on an exponential sequence.
     *
     * @param initial     the initial (starting) value
     * @param percentage  the growth rate percentage (typically between 0.0 and 100.0)
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency exponential(double initial, double percentage) {
        return new RetryFrequency(() -> new MappingIterator<>(new ExponentialIterator(initial, percentage), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
    }
}


public static RetryFrequency exponential(double initial, double percentage) {
        return new RetryFrequency(() -> new MappingIterator<>(new ExponentialIterator(initial, percentage), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
    }

/*
 * File: RetryFrequency.java
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 */
package com.oracle.bedrock.deferred.options;

import com.oracle.bedrock.Option;
import com.oracle.bedrock.OptionsByType;
import com.oracle.bedrock.deferred.Ensured;
import com.oracle.bedrock.util.Duration;
import com.oracle.bedrock.util.ExponentialIterator;
import com.oracle.bedrock.util.FibonacciIterator;
import com.oracle.bedrock.util.MappingIterator;
import com.oracle.bedrock.util.PerpetualIterator;
import com.oracle.bedrock.util.RandomIterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * An {@link Option} to define frequency (durations to wait) when attempting to resolve an
 * {@link Ensured} when calling {@link Ensured#get()}
 *

 * Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
 *
 * @author Brian Oliver
 *
 * @see Ensured
 */
public class RetryFrequency implements Option {

    /**
     * The {@link Duration}s for the {@link RetryFrequency}.
     */
    private Iterable<Duration> durations;

    /**
     * Privately constructs a {@link RetryFrequency} {@link Option}.
     *
     * @param durations  the {@link Duration}s for the {@link RetryFrequency}
     */
    private RetryFrequency(Iterable<Duration> durations) {
        this.durations = durations;
    }

    /**
     * Obtains the {@link Duration}s for the {@link RetryFrequency}.
     *
     * @return  the {@link Duration}s for the {@link RetryFrequency}
     */
    public Iterable<Duration> get() {
        return durations;
    }

    /**
     * Obtains a randomized version of this {@link RetryFrequency}, where by
     * values returned from the {@link #get()} {@link Iterable} will be
     * randomized values between 0 and the values provided by this
     * {@link RetryFrequency}.
     *
     * @return  a new randomized {@link RetryFrequency}
     */
    public RetryFrequency randomized() {
        return new RetryFrequency(() -> new MappingIterator<>(new RandomIterator(new MappingIterator<>(this.get().iterator(), duration -> duration.to(TimeUnit.MILLISECONDS))), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
    }

    /**
     * Obtains the standard {@link RetryFrequency} for retrying every 250 milliseconds.
     *
     * @return  a {@link RetryFrequency}
     */
    @OptionsByType.Default
    public static RetryFrequency standard() {
        return every(250, TimeUnit.MILLISECONDS);
    }

    /**
     * Obtains a {@link RetryFrequency} based on the specified {@link Duration}s.
     *
     * @param durations  the {@link Duration}s
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency of(Iterable<Duration> durations) {
        return new RetryFrequency(durations);
    }

    /**
     * Obtains a {@link RetryFrequency} based on the specified times.
     *

     * For example, to retry at 0, 5, 10 and 20 seconds, use the following
     *
     * RetryFrequency.of(TimeUnit.SECONDS, 0, 5, 10, 20);
     *
     *
     * @param unit       the {@link TimeUnit} for the durations
     * @param instants   the instants in time  (will be sorted)
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency of(TimeUnit unit, long... instants) {
        ArrayList<Long> frequencies = new ArrayList<>();
        if (instants.length > 0) {
            // ensure we have the instants in a sorted order
            Arrays.sort(instants);
            // add the lowest
            frequencies.add(instants[0]);
            long sum = instants[0];
            for (int i = 1; i < instants.length; i++) {
                long delta = instants[i] - sum;
                sum = sum + delta;
                frequencies.add(delta);
            }
            return new RetryFrequency(() -> new MappingIterator<>(frequencies.iterator(), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
        } else {
            throw new IllegalArgumentException("Failed to specify any times for the RetryFrequency");
        }
    }

    /**
     * Obtains a {@link RetryFrequency} representing fixed retry {@link Duration}s.
     *
     * @param duration  the duration
     * @param unit      the {@link TimeUnit} for the {@link Duration}
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency every(long duration, TimeUnit unit) {
        return new RetryFrequency(() -> new PerpetualIterator<>(Duration.of(duration, unit)));
    }

    /**
     * Obtains a {@link RetryFrequency} based on the Fibonacci sequence.
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency fibonacci() {
        return new RetryFrequency(() -> new MappingIterator<>(new FibonacciIterator(), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
    }

    /**
     * Obtains a {@link RetryFrequency} based on an exponential sequence, starting at 0
     * with a 50% growth rate.
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency exponential() {
        return exponential(0, 50);
    }

    /**
     * Obtains a {@link RetryFrequency} based on an exponential sequence.
     *
     * @param initial     the initial (starting) value
     * @param percentage  the growth rate percentage (typically between 0.0 and 100.0)
     *
     * @return a {@link RetryFrequency}
     */


for (int i = 1; i < instants.length; i++) {
                long delta = instants[i] - sum;
                sum = sum + delta;
                frequencies.add(delta);
            }

/*
 * File: RetryFrequency.java
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 */
package com.oracle.bedrock.deferred.options;

import com.oracle.bedrock.Option;
import com.oracle.bedrock.OptionsByType;
import com.oracle.bedrock.deferred.Ensured;
import com.oracle.bedrock.util.Duration;
import com.oracle.bedrock.util.ExponentialIterator;
import com.oracle.bedrock.util.FibonacciIterator;
import com.oracle.bedrock.util.MappingIterator;
import com.oracle.bedrock.util.PerpetualIterator;
import com.oracle.bedrock.util.RandomIterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;

/**
 * An {@link Option} to define frequency (durations to wait) when attempting to resolve an
 * {@link Ensured} when calling {@link Ensured#get()}
 *

 * Oracle is a registered trademark of Oracle Corporation and/or its affiliates.
 *
 * @author Brian Oliver
 *
 * @see Ensured
 */
public class RetryFrequency implements Option {

    /**
     * The {@link Duration}s for the {@link RetryFrequency}.
     */
    private Iterable<Duration> durations;

    /**
     * Privately constructs a {@link RetryFrequency} {@link Option}.
     *
     * @param durations  the {@link Duration}s for the {@link RetryFrequency}
     */
    private RetryFrequency(Iterable<Duration> durations) {
        this.durations = durations;
    }

    /**
     * Obtains the {@link Duration}s for the {@link RetryFrequency}.
     *
     * @return  the {@link Duration}s for the {@link RetryFrequency}
     */
    public Iterable<Duration> get() {
        return durations;
    }

    /**
     * Obtains a randomized version of this {@link RetryFrequency}, where by
     * values returned from the {@link #get()} {@link Iterable} will be
     * randomized values between 0 and the values provided by this
     * {@link RetryFrequency}.
     *
     * @return  a new randomized {@link RetryFrequency}
     */
    public RetryFrequency randomized() {
        return new RetryFrequency(() -> new MappingIterator<>(new RandomIterator(new MappingIterator<>(this.get().iterator(), duration -> duration.to(TimeUnit.MILLISECONDS))), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
    }

    /**
     * Obtains the standard {@link RetryFrequency} for retrying every 250 milliseconds.
     *
     * @return  a {@link RetryFrequency}
     */
    @OptionsByType.Default
    public static RetryFrequency standard() {
        return every(250, TimeUnit.MILLISECONDS);
    }

    /**
     * Obtains a {@link RetryFrequency} based on the specified {@link Duration}s.
     *
     * @param durations  the {@link Duration}s
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency of(Iterable<Duration> durations) {
        return new RetryFrequency(durations);
    }

    /**
     * Obtains a {@link RetryFrequency} based on the specified times.
     *

     * For example, to retry at 0, 5, 10 and 20 seconds, use the following
     *
     * RetryFrequency.of(TimeUnit.SECONDS, 0, 5, 10, 20);
     *
     *
     * @param unit       the {@link TimeUnit} for the durations
     * @param instants   the instants in time  (will be sorted)
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency of(TimeUnit unit, long... instants) {
        ArrayList<Long> frequencies = new ArrayList<>();
        if (instants.length > 0) {
            // ensure we have the instants in a sorted order
            Arrays.sort(instants);
            // add the lowest
            frequencies.add(instants[0]);
            long sum = instants[0];

            return new RetryFrequency(() -> new MappingIterator<>(frequencies.iterator(), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
        } else {
            throw new IllegalArgumentException("Failed to specify any times for the RetryFrequency");
        }
    }

    /**
     * Obtains a {@link RetryFrequency} representing fixed retry {@link Duration}s.
     *
     * @param duration  the duration
     * @param unit      the {@link TimeUnit} for the {@link Duration}
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency every(long duration, TimeUnit unit) {
        return new RetryFrequency(() -> new PerpetualIterator<>(Duration.of(duration, unit)));
    }

    /**
     * Obtains a {@link RetryFrequency} based on the Fibonacci sequence.
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency fibonacci() {
        return new RetryFrequency(() -> new MappingIterator<>(new FibonacciIterator(), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
    }

    /**
     * Obtains a {@link RetryFrequency} based on an exponential sequence, starting at 0
     * with a 50% growth rate.
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency exponential() {
        return exponential(0, 50);
    }

    /**
     * Obtains a {@link RetryFrequency} based on an exponential sequence.
     *
     * @param initial     the initial (starting) value
     * @param percentage  the growth rate percentage (typically between 0.0 and 100.0)
     *
     * @return a {@link RetryFrequency}
     */
    public static RetryFrequency exponential(double initial, double percentage) {
        return new RetryFrequency(() -> new MappingIterator<>(new ExponentialIterator(initial, percentage), duration -> Duration.of(duration, TimeUnit.MILLISECONDS)));
    }
}


package org.gradle.foundation;

import org.gradle.util.GUtil;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * Analog to gradle's Project but more light-weight and is better suited for using the gradle API from an IDE plugin. It is also easily serializable for passing across a socket. A project is a
 * collection of source files that have tasks associated with them. The tasks build the project. Projects can contain other projects. This is immutable and ultimately comes from gradle files.
 */
public class ProjectView implements Comparable<ProjectView>, Serializable {

    private final String name;

    private final ProjectView parentProject;

    // It is null for the root project.
    private final List<ProjectView> subProjects = new ArrayList<ProjectView>();

    private final List<TaskView> tasks = new ArrayList<TaskView>();

    private final List<ProjectView> dependsOnProjects = new ArrayList<ProjectView>();

    private final File buildFile;

    private final String description;

    /**
     * Instantiates an immutable view of a project. This is only meant to be called internally whenever generating a hierarchy of projects and tasks.
     */
    ProjectView(ProjectView parentProject, String name, File buildFile, String description) {
        this.parentProject = parentProject;
        this.name = name;
        this.buildFile = buildFile;
        this.description = GUtil.elvis(description, "");
        if (parentProject != null) {
            parentProject.addSubProject(this);
        }
    }

    public String getName() {
        return name;
    }

    public File getBuildFile() {
        return buildFile;
    }

    public String getDescription() {
        return description;
    }

    public String toString() {
        return name;
    }

    public ProjectView getParentProject() {
        return parentProject;
    }

    public int compareTo(ProjectView otherProject) {
        return name.compareTo(otherProject.name);
    }

    /**
     * creates a task for this project. This is only meant to be called internally whenever generating a hierachy of projects and tasks.
     */
    void createTask(String name, String description, boolean isDefault) {
        TaskView taskView = new TaskView(this, name, description, isDefault);
        tasks.add(taskView);
    }

    /**
     * Adds the specified project as a sub project of this project. This is only meant to be called internally whenever generating a hierachy of projects and tasks.
     */
    void addSubProject(ProjectView subProject) {
        subProjects.add(subProject);
    }

    /**
     * Sets the project that this project depends on. This is only meant to be called internally whenever generating a hierachy of projects and tasks.
     */
    void setDependsOnProjects(List<ProjectView> newDependsOnProjects) {
        if (newDependsOnProjects == null) {
            return;
        }
        this.dependsOnProjects.clear();
        this.dependsOnProjects.addAll(newDependsOnProjects);
    }

    public List<ProjectView> getDependsOnProjects() {
        return dependsOnProjects;
    }

{
        return Collections.unmodifiableList(tasks);
    }

    public List<ProjectView> getSubProjects() {
        return Collections.unmodifiableList(subProjects);
    }

    public void sortSubProjectsAndTasks() {
        Collections.sort(tasks);
        Collections.sort(subProjects);
    }

    public ProjectView getSubProject(String name) {
        Iterator<ProjectView> iterator = subProjects.iterator();
        while (iterator.hasNext()) {
            ProjectView subProject = iterator.next();
            if (name.equals(subProject.getName())) {
                return subProject;
            }
        }
        return null;
    }

    public TaskView getTask(String name) {
        Iterator<TaskView> iterator = tasks.iterator();
        while (iterator.hasNext()) {
            TaskView task = iterator.next();
            if (name.equals(task.getName())) {
                return task;
            }
        }
        return null;
    }

    public ProjectView getSubProjectFromFullPath(String fullProjectName) {
        if (fullProjectName == null) {
            return null;
        }
        PathParserPortion portion = new PathParserPortion(fullProjectName);
        ProjectView subProject = getSubProject(portion.getFirstPart());
        if (// if we have no remainder, then the path is just a sub project's name. We're done (even if subProject is null).
        !portion.hasRemainder()) {
            return subProject;
        }
        if (subProject == null) {
            return null;
        }
        // the path may be invalid
        return subProject.getSubProjectFromFullPath(portion.getRemainder());
    }

    /**
     * This gets the task based on the given full path. This recursively calls this same function with sub projects until it finds the task or no matches are found.
     *
     * @param fullTaskName the full task name (root_project:sub_project:sub_sub_project:task.).
     * @return the task or null if not found.
     */
    public TaskView getTaskFromFullPath(String fullTaskName) {
        if (fullTaskName == null) {
            return null;
        }
        PathParserPortion portion = new PathParserPortion(fullTaskName);
        if (// if we have no remainder, then this is for a task.
        !portion.hasRemainder()) {
            return getTask(portion.getFirstPart());
        }
        ProjectView subProject = getSubProject(portion.getFirstPart());
        if (subProject == null) {
            return null;
        }
        // let the sub project figure it out.
        return subProject.getTaskFromFullPath(portion.getRemainder());
    }

    /**
     * This generates this project's full name. This is a colon-separated string of this project and its parent projects.
     *
     * Example: root_project:sub_project:sub_sub_project.
     */
    public String getFullProjectName() {
        ProjectView ancestorProject = getParentProject();
        if (ancestorProject == null) {
            return "";
        }
        // if we're the root, our full project name is nothing.
        StringBuilder builder = new StringBuilder(name);
        while (// we don't want to include the 'root' project
        ancestorProject != null && ancestorProject.getParentProject() != null) {
            builder.insert(0, ancestorProject.getName() + ':');
            ancestorProject = ancestorProject.getParentProject();
        }
        return builder.toString();
    }

    /**
     * Builds a list of default tasks. These are defined by specifying
     *
     * defaultTasks 'task name'
     *
     * in the gradle file. There can be multiple default tasks. This only returns default tasks directly for this project and does not return them for subprojects.
     *
     * @return a list of default tasks or an empty list if none exist
     */
    public List<TaskView> getDefaultTasks() {
        List<TaskView> defaultTasks = new ArrayList<TaskView>();
        Iterator<TaskView> taskIterator = tasks.iterator();
        while (taskIterator.hasNext()) {
            TaskView taskView = taskIterator.next();
            if (taskView.isDefault()) {
                defaultTasks.add(taskView);
            }
        }
        return defaultTasks;
    }
}


{
        TaskView taskView = new TaskView(this, name, description, isDefault);
        tasks.add(taskView);
    }

package org.gradle.foundation;

import org.gradle.util.GUtil;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * Analog to gradle's Project but more light-weight and is better suited for using the gradle API from an IDE plugin. It is also easily serializable for passing across a socket. A project is a
 * collection of source files that have tasks associated with them. The tasks build the project. Projects can contain other projects. This is immutable and ultimately comes from gradle files.
 */
public class ProjectView implements Comparable<ProjectView>, Serializable {

    private final String name;

    private final ProjectView parentProject;

    // It is null for the root project.
    private final List<ProjectView> subProjects = new ArrayList<ProjectView>();

    private final List<TaskView> tasks = new ArrayList<TaskView>();

    private final List<ProjectView> dependsOnProjects = new ArrayList<ProjectView>();

    private final File buildFile;

    private final String description;

    /**
     * Instantiates an immutable view of a project. This is only meant to be called internally whenever generating a hierarchy of projects and tasks.
     */
    ProjectView(ProjectView parentProject, String name, File buildFile, String description) {
        this.parentProject = parentProject;
        this.name = name;
        this.buildFile = buildFile;
        this.description = GUtil.elvis(description, "");
        if (parentProject != null) {
            parentProject.addSubProject(this);
        }
    }

    public String getName() {
        return name;
    }

    public File getBuildFile() {
        return buildFile;
    }

    public String getDescription() {
        return description;
    }

    public String toString() {
        return name;
    }

    public ProjectView getParentProject() {
        return parentProject;
    }

    public int compareTo(ProjectView otherProject) {
        return name.compareTo(otherProject.name);
    }

    /**
     * creates a task for this project. This is only meant to be called internally whenever generating a hierachy of projects and tasks.
     */
    void createTask(String name, String description, boolean isDefault)

    /**
     * Adds the specified project as a sub project of this project. This is only meant to be called internally whenever generating a hierachy of projects and tasks.
     */
    void addSubProject(ProjectView subProject) {
        subProjects.add(subProject);
    }

    /**
     * Sets the project that this project depends on. This is only meant to be called internally whenever generating a hierachy of projects and tasks.
     */
    void setDependsOnProjects(List<ProjectView> newDependsOnProjects) {
        if (newDependsOnProjects == null) {
            return;
        }
        this.dependsOnProjects.clear();
        this.dependsOnProjects.addAll(newDependsOnProjects);
    }

    public List<ProjectView> getDependsOnProjects() {
        return dependsOnProjects;
    }

    public List<TaskView> getTasks() {
        return Collections.unmodifiableList(tasks);
    }

    public List<ProjectView> getSubProjects() {
        return Collections.unmodifiableList(subProjects);
    }

    public void sortSubProjectsAndTasks() {
        Collections.sort(tasks);
        Collections.sort(subProjects);
    }

    public ProjectView getSubProject(String name) {
        Iterator<ProjectView> iterator = subProjects.iterator();
        while (iterator.hasNext()) {
            ProjectView subProject = iterator.next();
            if (name.equals(subProject.getName())) {
                return subProject;
            }
        }
        return null;
    }

    public TaskView getTask(String name) {
        Iterator<TaskView> iterator = tasks.iterator();
        while (iterator.hasNext()) {
            TaskView task = iterator.next();
            if (name.equals(task.getName())) {
                return task;
            }
        }
        return null;
    }

    public ProjectView getSubProjectFromFullPath(String fullProjectName) {
        if (fullProjectName == null) {
            return null;
        }
        PathParserPortion portion = new PathParserPortion(fullProjectName);
        ProjectView subProject = getSubProject(portion.getFirstPart());
        if (// if we have no remainder, then the path is just a sub project's name. We're done (even if subProject is null).
        !portion.hasRemainder()) {
            return subProject;
        }
        if (subProject == null) {
            return null;
        }
        // the path may be invalid
        return subProject.getSubProjectFromFullPath(portion.getRemainder());
    }

    /**
     * This gets the task based on the given full path. This recursively calls this same function with sub projects until it finds the task or no matches are found.
     *
     * @param fullTaskName the full task name (root_project:sub_project:sub_sub_project:task.).
     * @return the task or null if not found.
     */
    public TaskView getTaskFromFullPath(String fullTaskName) {
        if (fullTaskName == null) {
            return null;
        }
        PathParserPortion portion = new PathParserPortion(fullTaskName);
        if (// if we have no remainder, then this is for a task.
        !portion.hasRemainder()) {
            return getTask(portion.getFirstPart());
        }
        ProjectView subProject = getSubProject(portion.getFirstPart());
        if (subProject == null) {
            return null;
        }
        // let the sub project figure it out.
        return subProject.getTaskFromFullPath(portion.getRemainder());
    }

    /**
     * This generates this project's full name. This is a colon-separated string of this project and its parent projects.
     *
     * Example: root_project:sub_project:sub_sub_project.
     */
    public String getFullProjectName() {
        ProjectView ancestorProject = getParentProject();
        if (ancestorProject == null) {
            return "";
        }
        // if we're the root, our full project name is nothing.
        StringBuilder builder = new StringBuilder(name);
        while (// we don't want to include the 'root' project
        ancestorProject != null && ancestorProject.getParentProject() != null) {
            builder.insert(0, ancestorProject.getName() + ':');
            ancestorProject = ancestorProject.getParentProject();
        }
        return builder.toString();
    }

    /**
     * Builds a list of default tasks. These are defined by specifying
     *
     * defaultTasks 'task name'
     *
     * in the gradle file. There can be multiple default tasks. This only returns default tasks directly for this project and does not return them for subprojects.
     *
     * @return a list of default tasks or an empty list if none exist
     */
    public List<TaskView> getDefaultTasks() {
        List<TaskView> defaultTasks = new ArrayList<TaskView>();
        Iterator<TaskView> taskIterator = tasks.iterator();
        while (taskIterator.hasNext()) {
            TaskView taskView = taskIterator.next();
            if (taskView.isDefault()) {
                defaultTasks.add(taskView);
            }
        }
        return defaultTasks;
    }
}


package org.gradle.foundation;

import org.gradle.util.GUtil;
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * Analog to gradle's Project but more light-weight and is better suited for using the gradle API from an IDE plugin. It is also easily serializable for passing across a socket. A project is a
 * collection of source files that have tasks associated with them. The tasks build the project. Projects can contain other projects. This is immutable and ultimately comes from gradle files.
 */
public class ProjectView implements Comparable<ProjectView>, Serializable {

    private final String name;

    private final ProjectView parentProject;

    // It is null for the root project.
    private final List<ProjectView> subProjects = new ArrayList<ProjectView>();

    private final List<TaskView> tasks = new ArrayList<TaskView>();

    private final List<ProjectView> dependsOnProjects = new ArrayList<ProjectView>();

    private final File buildFile;

    private final String description;

    /**
     * Instantiates an immutable view of a project. This is only meant to be called internally whenever generating a hierarchy of projects and tasks.
     */
    ProjectView(ProjectView parentProject, String name, File buildFile, String description) {
        this.parentProject = parentProject;
        this.name = name;
        this.buildFile = buildFile;
        this.description = GUtil.elvis(description, "");
        if (parentProject != null) {
            parentProject.addSubProject(this);
        }
    }

    public String getName() {
        return name;
    }

    public File getBuildFile() {
        return buildFile;
    }

    public String getDescription() {
        return description;
    }

    public String toString() {
        return name;
    }

    public ProjectView getParentProject() {
        return parentProject;
    }

    public int compareTo(ProjectView otherProject) {
        return name.compareTo(otherProject.name);
    }

    /**
     * creates a task for this project. This is only meant to be called internally whenever generating a hierachy of projects and tasks.
     */
    void createTask(String name, String description, boolean isDefault) {
        TaskView taskView = new TaskView(this, name, description, isDefault);
        tasks.add(taskView);
    }

    /**
     * Adds the specified project as a sub project of this project. This is only meant to be called internally whenever generating a hierachy of projects and tasks.
     */
    void addSubProject(ProjectView subProject) {
        subProjects.add(subProje

    }

    /**
     * Sets the project that this project depends on. This is only meant to be called internally whenever generating a hierachy of projects and tasks.
     */
    void setDependsOnProjects(List<ProjectView> newDependsOnProjects) {
        if (newDependsOnProjects == null) {
            return;
        }
        this.dependsOnProjects.clear();
        this.dependsOnProjects.addAll(newDependsOnProjects);
    }

    public List<ProjectView> getDependsOnProjects() {
        return dependsOnProjects;
    }

    public List<TaskView> getTasks() {
        return Collections.unmodifiableList(tasks);
    }

    public List<ProjectView> getSubProjects() {
        return Collections.unmodifiableList(subProjects);
    }

    public void sortSubProjectsAndTasks() {
        Collections.sort(tasks);
        Collections.sort(subProjects);
    }

    public ProjectView getSubProject(String name) {
        Iterator<ProjectView> iterator = subProjects.iterator();
        while (iterator.hasNext()) {
            ProjectView subProject = iterator.next();
            if (name.equals(subProject.getName())) {
                return subProject;
            }
        }
        return null;
    }

    public TaskView getTask(String name) {
        Iterator<TaskView> iterator = tasks.iterator();
        while (iterator.hasNext()) {
            TaskView task = iterator.next();
            if (name.equals(task.getName())) {
                return task;
            }
        }
        return null;
    }

    public ProjectView getSubProjectFromFullPath(String fullProjectName) {
        if (fullProjectName == null) {
            return null;
        }
        PathParserPortion portion = new PathParserPortion(fullProjectName);
        ProjectView subProject = getSubProject(portion.getFirstPart());
        if (// if we have no remainder, then the path is just a sub project's name. We're done (even if subProject is null).
        !portion.hasRemainder()) {
            return subProject;
        }
        if (subProject == null) {
            return null;
        }
        // the path may be invalid
        return subProject.getSubProjectFromFullPath(portion.getRemainder());
    }

    /**
     * This gets the task based on the given full path. This recursively calls this same function with sub projects until it finds the task or no matches are found.
     *
     * @param fullTaskName the full task name (root_project:sub_project:sub_sub_project:task.).
     * @return the task or null if not found.
     */
    public TaskView getTaskFromFullPath(String fullTaskName) {
        if (fullTaskName == null) {
            return null;
        }
        PathParserPortion portion = new PathParserPortion(fullTaskName);
        if (// if we have no remainder, then this is for a task.
        !portion.hasRemainder()) {
            return getTask(portion.getFirstPart());
        }
        ProjectView subProject = getSubProject(portion.getFirstPart());
        if (subProject == null) {
            return null;
        }
        // let the sub project figure it out.
        return subProject.getTaskFromFullPath(portion.getRemainder());
    }

    /**
     * This generates this project's full name. This is a colon-separated string of this project and its parent projects.
     *
     * Example: root_project:sub_project:sub_sub_project.
     */
    public String getFullProjectName() {
        ProjectView ancestorProject = getParentProject();
        if (ancestorProject == null) {
            return "";
        }
        // if we're the root, our full project name is nothing.
        StringBuilder builder = new StringBuilder(name);
        while (// we don't want to include the 'root' project
        ancestorProject != null && ancestorProject.getParentProject() != null) {
            builder.insert(0, ancestorProject.getName() + ':');
            ancestorProject = ancestorProject.getParentProject();
        }
        return builder.toString();
    }

    /**
     * Builds a list of default tasks. These are defined by specifying
     *
     * defaultTasks 'task name'
     *
     * in the gradle file. There can be multiple default tasks. This only returns default tasks directly for this project and does not return them for subprojects.
     *
     * @return a list of default tasks or an empty list if none exist
     */
    public List<TaskView> getDefaultTasks() {
        List<TaskView> defaultTasks = new ArrayList<TaskView>();
        Iterator<TaskView> taskIterator = tasks.iterator();
        while (taskIterator.hasNext()) {
            TaskView taskView = taskIterator.next();
            if (taskView.isDefault()) {
                defaultTasks.add(taskView);
            }
        }
        return defaultTasks;
    }
}