public static final String PLUGIN_CONFIGS_COMMENTS = "Plugin.Configurations.Comments"; public static final String PLUGIN_CONFIGS_NOTE = "Plugin.Configurations.Note"; public static final String PLUGIN_CONFIGS_NOTEBOOK = "Plugin.Configurations.Notebook"; public static final int PLUGIN_CONFIGS_REFRESH_ID = -143259786; // random number public static final String PLUGIN_CONFIGS_TAGS = "Plugin.Configurations.Tags"; public static final String PLUGIN_SETTINGS_KEY_CHECKED = "isChecked"; public static final String PLUGIN_SETTINGS_KEY_GUID = "guid"; public static final String PLUGIN_SETTINGS_KEY_NAME = "name"; public static final String PLUGIN_SETTINGS_KEY_OBJECT = "object"; public static final String PLUGIN_SETTINGS_KEY_TOKEN = "token"; public static final String PLUGIN_SETTINGS_KEY_TYPE = "type"; public static final String PLUGIN_SETTINGS_KEY_UUID = "uuid"; public static final String PLUGIN_SETTINGS_SECTION_COMMENTS = "comments"; public static final String PLUGIN_SETTINGS_SECTION_NOTE = "note"; public static final String PLUGIN_SETTINGS_SECTION_NOTEBOOK = "notebook"; public static final String PLUGIN_SETTINGS_SECTION_TAGS = "tags"; public static final String TAGS_SEPARATOR = ConstantsUtil.COMMA; public class Messages extends NLS {

    private static final String BUNDLE_NAME = "messages"; //$NON-NLS-1$

    public static String Plugin_Configs_Shell_Title;
    public static String Plugin_Configs_QuickOrgnize_Shell_Title;
    public static String Plugin_Configs_Title;
    public static String Plugin_Configs_Message;
    public static String Plugin_Configs_Organize;
    public static String Plugin_Configs_Notebook;
    public static String Plugin_Configs_Note;
    public static String Plugin_Configs_Tags;
    public static String Plugin_Configs_Notebook_Hint;
    public static String Plugin_Configs_Note_Hint;
    public static String Plugin_Configs_Tags_Hint;
    public static String Plugin_Configs_Comments;
    public static String Plugin_Configs_Refresh;
    public static String Plugin_Configs_Authenticating;
    public static String Plugin_Configs_FetchingNotebooks;
    public static String Plugin_Configs_FetchingNotes;
    public static String Plugin_Configs_FetchingTags;
    public static String Plugin_Runtime_ClipFileToEvernote;
    public static String Plugin_Runtime_ClipSelectionToEvernote;
    public static String Plugin_Runtime_ClipScreenshotToEvernote_Hint;
    public static String Plugin_Runtime_CreateNewNote;
    public static String Plugin_Runtime_CreateNewNoteInNotebook;
    public static String Plugin_Runtime_ClipToDefault;
    public static String Plugin_Runtime_CreateNewNoteWithGivenName;
    public static String Plugin_Runtime_CreateNewNoteWithGivenNameInNotebook;
    public static String Plugin_Runtime_ClipToNotebook_Default;
    public static String Plugin_OutOfDate;
    public static String Plugin_Error_Occurred;
    public static String Plugin_Error_OutOfDate;
    public static String Plugin_Error_NoFile;
    public static String Plugin_Error_NoText;
    public static String Plugin_OAuth_Cancel;
    public static String Plugin_OAuth_Copy;

    public static String Plugin_OAuth_TokenNotConfigured;
    public static String Plugin_OAuth_Title;
    public static String Plugin_OAuth_Configure;
    public static String Plugin_OAuth_NotNow;
    public static String Plugin_OAuth_Waiting;
    public static String Plugin_OAuth_Confirm;
    public static String Plugin_OAuth_AuthExpired_Message;
    public static String Plugin_OAuth_AuthExpired_Title;
    public static String Plugin_OAuth_AuthExpired_ReAuth;
    public static String Plugin_OAuth_DoItManually;
    public static String Plugin_OAuth_SWITCH_YXBJ;
    public static String Plugin_OAuth_SWITCH_INTL;

    public static String DOM_Error0;
    public static String DOM_Error1;
    public static String DOM_Error2;
    public static String DOM_Error3;
    public static String DOM_Error4;
    public static String DOM_Error5;
    public static String DOM_Error6;
    public static String DOM_Error7;
    public static String DOM_Error8;
    public static String DOM_Error9;
    public static String Throwable_IllegalArgumentException_Message;
    public static String Throwable_NotSerializable_Message;

    // For Debug
    public static String Plugin_Debug_Default_Font_Style;
    public static String Plugin_Debug_StyleRange_Font_Style;
    public static String Plugin_Debug_FinalConcluded_Font_Style;
    public static String Plugin_Debug_NewClipper;
    public static String Plugin_Debug_IsFullScreenSupported;
    public static String Plugin_Debug_CapturedScreenshot;
    public static String Plugin_Debug_NoScreenCaptureProcessor;

    static {
        // initialize resource bundle
        NLS.initializeMessages(BUNDLE_NAME, Messages.class);
    }

    private Messages() {
    }

} public abstract class EeClipper {

    private boolean valid = true;

    public abstract void clipFile(ENNote args) throws Exception;

    public abstract void clipSelection(ENNote args) throws Exception;

    public abstract Map<String, ENObject> listNotebooks() throws Exception;

    public abstract Map<String, ENNote> listNotesWithinNotebook(ENNote args) throws Exception;

    public abstract List<String> listTags() throws Exception;

    public boolean isValid() {
        return valid;
    };

    public void setInvalid() {
        valid = false;
    };

} public class ENNoteImpl extends ENObjectImpl implements ENNote {

    private ENObject notebook;
    private List<List<StyleText>> content;
    private List<File> attachments;
    private List<String> tags;
    private String comments;
    private int tabWidth;

    public ENNoteImpl() {
        content = ListUtil.list();
        attachments = ListUtil.list();
        tags = ListUtil.list();
    }

    public ENNoteImpl(final String guid) {
        super(guid);
        content = ListUtil.list();
        attachments = ListUtil.list();
        tags = ListUtil.list();
    }

    public ENNoteImpl(final String name, final String guid) {
        super(name, guid);
        content = ListUtil.list();
        attachments = ListUtil.list();
        tags = ListUtil.list();
    }

    @Override
    public ENObject getNotebook() {
        if (notebook == null) {
            notebook = new ENObjectImpl();
        }
        return notebook;
    }

    @Override
    public void setNotebook(final ENObject notebook) {
        this.notebook = notebook;
    }

    @Override
    public List<List<StyleText>> getContent() {
        return content;
    }

    @Override
    public void setContent(final List<List<StyleText>> content) {
        this.content = content;
    }

    @Override
    public List<File> getAttachments() {
        return attachments;
    }

    @Override
    public void setAttachments(final List<File> attachments) {
        this.attachments = attachments;
    }

    @Override
    public List<String> getTags() {
        return tags;
    }

    @Override
    public void setTags(final List<String> tags) {
        this.tags = tags;
    }

    @Override
    public String getComments() {
        return comments;
    }

    @Override
    public void setComments(final String comments) {
        this.comments = comments;
    }

    @Override
    public int getTabWidth() {
        return tabWidth;
    }

    @Override
    public void setTabWidth(final int tabWidth) {
        this.tabWidth = tabWidth;
    }

    /**
     * Adopt if not null/empty/blank
     */
    @Override
    public void adopt(final ENObject obj) {

        super.adopt(obj);

        if (!(obj instanceof ENNote)) {
            return;
        }
        ENNote note = (ENNote) obj;

        if (note.getNotebook() != null) {
            getNotebook().adopt(note.getNotebook());
        }

        if (!ListUtil.isNullOrEmptyList(note.getContent())) {
            setContent(note.getContent());
        }

        if (!ListUtil.isNullOrEmptyList(note.getAttachments())) {
            setAttachments(note.getAttachments());
        }

        if (!ListUtil.isNullOrEmptyList(note.getTags())) {
            setTags(note.getTags());
        }

        if (StringUtils.isNotBlank(note.getComments())) {
            setComments(note.getComments());
        }
    }

    public static ENNote forGuid(final String guid) {
        return new ENNoteImpl(guid);
    }

    public static ENNote forNameAndGuid(final String name, final String guid) {
        return new ENNoteImpl(name, guid);
    }

    public static ENNote forNotebook(final ENObject notebook) {
        ENNote n = new ENNoteImpl();
        if (notebook != null) {
            n.getNotebook().setName(notebook.getName()); // maybe no use but just in case
            n.getNotebook().setGuid(notebook.getGuid());
            n.getNotebook().setType(notebook.getType());
            n.getNotebook().setLinkedObject(notebook.getLinkedObject());
        }
        return n;
    }

} public class ENObjectImpl implements ENObject {

    private String name;
    private String guid;

    private boolean reset = false;
    private boolean adopt = false;

    private ENObjectType type = ENObjectType.NORMAL;
    private Object linkedObject;

    public ENObjectImpl() {

    }

    public ENObjectImpl(final String guid) {
        this.guid = guid;
    }

    public ENObjectImpl(final String name, final String guid) {
        this(guid);
        this.name = name;
    }

    public ENObjectImpl(final String name, final String guid, final ENObjectType type) {
        this(name, guid);
        this.type = type;
    }

    public ENObjectImpl(final String name, final String guid, final ENObjectType type, final Object linkedObject) {
        this(name, guid, type);
        this.linkedObject = linkedObject;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(final String name) {
        this.name = name;
    }

    @Override
    public String getGuid() {
        return guid;
    }

    @Override
    public void setGuid(final String guid) {
        this.guid = guid;
    }

    @Override
    public boolean isArgsReset() {
        return reset;
    }

    @Override
    public void setArgsReset(final boolean reset) {
        this.reset = reset;
    }

    @Override
    public boolean isArgsAdopt() {
        return adopt;
    }

    @Override
    public void setArgsAdopt(final boolean adopt) {
        this.adopt = adopt;
    }

    @Override
    public ENObjectType getType() {
        return type;
    }

    @Override
    public void setType(final ENObjectType type) {
        this.type = type;
    }

    @Override
    public Object getLinkedObject() {
        return linkedObject;
    }

    @Override
    public void setLinkedObject(final Object linkedObject) {
        this.linkedObject = linkedObject;
    }

    /**
     * Adopt if not null/empty/blank
     */
    @Override
    public void adopt(final ENObject note) {
        // this/set-name should always be done
        if (StringUtils.isNotBlank(note.getName())) {
            setName(note.getName());
        }

        if (StringUtils.isNotBlank(note.getGuid())) { // user do re-select an existing note in Quick Organize
            setGuid(note.getGuid());

            if (note.getType() != null) {
                setType(note.getType());
            }
            if (note.getLinkedObject() != null) {
                setLinkedObject(note.getLinkedObject());
            }

            setArgsAdopt(true);
        }
    }

    /**
     * equals if Guid equals.
     */
    @Override
    public boolean equals(final Object other) {
        if(this == other) {
            return true;
        }
        if (!(other instanceof ENObject)) {
            return false;
        }
        ENObject o = (ENObject) other;
        return StringUtils.equals(guid, o.getGuid());
    }

    public static ENObject forGuid(final String guid) {
        return new ENObjectImpl(guid);
    }

    public static ENObject forNameAndGuid(final String name, final String guid) {
        return new ENObjectImpl(name, guid);
    }

    public static ENObject forValues(final String name, final String guid, final ENObjectType type, final Object linkedObject) {
        return new ENObjectImpl(name, guid, type, linkedObject);
    }

} public class EDAMLimits {

    public static final int EDAM_NOTE_TITLE_LEN_MAX = 255;
    public static final int EDAM_NOTEBOOK_NAME_LEN_MAX = 100;
    public static final int EDAM_TAG_NAME_LEN_MAX = 100;

} public enum ENObjectType {

    NORMAL {
        @Override
        public String toString() {
            return Constants.ENOBJECT_TYPE_NORMAL;
        }
    },
    LINKED {
        @Override
        public String toString() {
            return Constants.ENOBJECT_TYPE_LINKED;
        }
    },
    BUSINESS {
        @Override
        public String toString() {
            return Constants.ENOBJECT_TYPE_BUSINESS;
        }
    };

    public static ENObjectType forName(final String name) throws IllegalArgumentException {
        ENObjectType[] values = ENObjectType.values();
        for (ENObjectType value : values) {
            if (value.toString().equalsIgnoreCase(name)) {
                return value;
            }
        }
        LogUtil.debug(Messages.bind(Messages.Throwable_IllegalArgumentException_Message, name));
        throw new IllegalArgumentException(Messages.bind(Messages.Throwable_IllegalArgumentException_Message, name));
    }

} public interface ENNote extends ENObject {

    public ENObject getNotebook();

    public void setNotebook(ENObject notebook);

    public List<List<StyleText>> getContent();

    public void setContent(List<List<StyleText>> content);

    public List<File> getAttachments();

    public void setAttachments(List<File> files);

    public List<String> getTags();

    public abstract void setTags(List<String> tags);

    public abstract String getComments();

    public abstract void setComments(String comments);

    public abstract int getTabWidth();

    public abstract void setTabWidth(int tabWidth);

} public class EDAMNotFoundHandler {

    private final String token;

    public EDAMNotFoundHandler(final String token) {
        this.token = token;
    }

    public IStatus fixNotFoundException(final EDAMNotFoundException e, final ENNote args) {
        if (e.getIdentifier().equals(EDAMDataModel.Note_notebookGuid.toString())) {
            return fixNotFoundNotebookGuid(args) ? LogUtil.ok() : LogUtil.error(e);
        } else if (e.getIdentifier().equals(EDAMDataModel.Note_noteGuid.toString())) {
            return fixNotFoundNoteGuid(args) ? LogUtil.ok() : LogUtil.error(e);
        }
        return LogUtil.error(e);
    }

    public ENObject findNotebookByName(final String name) {
        try {
            EeClipper clipper = EeClipperFactory.getInstance().getEeClipper(token, false);
            Map<String, ENObject> map = clipper.listNotebooks();
            return map.get(name);
        } catch (Throwable e) {
            // ignore and give up failure recovery
            LogUtil.logCancel(e);
        }
        return null;
    }

    public ENNote findNoteByName(final ENObject notebook, final String name) {
        ENNote noteFound = null;
        try {
            EeClipper clipper = EeClipperFactory.getInstance().getEeClipper(token, false);
            ENNote args = new ENNoteImpl();
            args.setNotebook(notebook);
            args.setName(name);
            Map<String, ENNote> map = clipper.listNotesWithinNotebook(args);
            noteFound = findNote(map, name);
        } catch (EDAMNotFoundException e) {
            if (e.getIdentifier().equals(EDAMDataModel.Notebook_guid.toString())) {
                ENObject nb = findNotebookByName(notebook.getName());
                // notebook is found or null, so should not come here again, which will result in a dead loop.
                noteFound = findNoteByName(nb, name);
            } else {
                LogUtil.logCancel(e);
            }
        } catch (Throwable e) {
            // ignore and give up failure recovery
            LogUtil.logCancel(e);
        }
        return noteFound;
    }

    /**
     * find note by name
     *
     * @param noteMap
     *            uuid-ENNote map
     * @param name
     *            should be exact note name, not uuid.
     * @return
     */
    public static ENNote findNote(final Map<String, ENNote> noteMap, final String name) {
        List<String> titles = ListUtil.list();
        for (Entry<String, ENNote> e : noteMap.entrySet()) {
            if (e.getValue().getName().equals(name)) {
                if (titles.size() != 0) {
                    return null;
                }
                titles.add(e.getKey());
            }
        }
        if (titles.size() == 1) {
            return noteMap.get(titles.get(0));
        }
        return null;
    }

    private boolean fixNotFoundNotebookGuid(final ENNote args) {
        ENObject found = findNotebookByName(args.getNotebook().getName());
        if (found != null) {
            args.setNotebook(found);
            args.getNotebook().setArgsReset(true);
            return true;
        }
        return false;
    }

    private boolean fixNotFoundNoteGuid(final ENNote args) {
        ENNote found = findNoteByName(args.getNotebook(), args.getName());
        if (found != null && StringUtils.isNotBlank(found.getGuid())) {
            args.setGuid(found.getGuid());
            args.setArgsReset(true);
            return true;
        }
        return false;
    }

} public class ThrowableHandler {

    public static void openError(final Shell shell, final String message) {
        EclipseUtil.openErrorSyncly(shell, Messages.Plugin_Error_Occurred, message);
    }

    public static boolean handleDesignTimeErr(final Shell shell, final Throwable e) {
        return handleDesignTimeErr(shell, e, null);
    }

    private static boolean result;

    public static boolean handleDesignTimeErr(final Shell shell, final Throwable e, final EeClipper clipper) {
        if (e instanceof EDAMUserException) {
            Display.getDefault().syncExec(new Runnable() {
                @Override
                public void run() {
                    result = new EDAMUserExceptionHandler().handleDesingTime(shell, (EDAMUserException) e, clipper);
                }
            });
            return result;
        } else if (e instanceof OutOfDateException) {
            openError(shell, Messages.bind(Messages.Plugin_OutOfDate, EemoryPlugin.getVersion()));
        } else if (e instanceof TTransportException) {
            if (clipper != null) {
                clipper.setInvalid();
            }
            openError(shell, ExceptionUtils.getRootCauseMessage(e));
        } else {
            openError(shell, ExceptionUtils.getRootCauseMessage(e));
        }
        return false;
    }

    public static IStatus handleJobErr(final Throwable e) {
        return handleJobErr(e, null, null, null);
    }

    public static IStatus handleJobErr(final Throwable e, final ENNote args) {
        return handleJobErr(e, null, args, null);
    }

    public static IStatus handleJobErr(final Throwable e, final EeClipper clipper) {
        return handleJobErr(e, clipper, null, null);
    }

    public static IStatus handleJobErr(final Throwable e, final EeClipper clipper, final ENNote args, final Shell shell) {
        if (e instanceof EDAMNotFoundException) {
            if (args != null) {
                return new EDAMNotFoundHandler(EncryptionUtil.decrypt(IDialogSettingsUtil.get(Constants.PLUGIN_SETTINGS_KEY_TOKEN))).fixNotFoundException((EDAMNotFoundException) e, args);
            }
        } else if (e instanceof EDAMUserException) {
            return new EDAMUserExceptionHandler().handleRuntime(shell, (EDAMUserException) e, clipper);
        } else if (e instanceof OutOfDateException) {
            return LogUtil.error(Messages.bind(Messages.Plugin_OutOfDate, EemoryPlugin.getVersion()));
        } else if (e instanceof TTransportException) {
            if (clipper != null) {
                clipper.setInvalid();
            }
        } else if (e instanceof NoDataFoundException) {
            return LogUtil.warning(ExceptionUtils.getRootCauseMessage(e));
        }
        return LogUtil.error(e);
    }

    public static ExecutionException handleExecErr(final Throwable e) {
        return handleExecErr(e, null);
    }

    public static ExecutionException handleExecErr(final Throwable e, final EeClipper clipper) {
        if (e instanceof OutOfDateException) {
            return new ExecutionException(Messages.bind(Messages.Plugin_OutOfDate, EemoryPlugin.getVersion()));
        } else if (e instanceof TTransportException) {
            if (clipper != null) {
                clipper.setInvalid();
            }
        }
        return new ExecutionException(ExceptionUtils.getRootCauseMessage(e), e);
    }

} public class ColorUtil {

    public final static java.awt.Color AWT_EVERNOTE_GREEN = new java.awt.Color(32, 192, 92);
    public final static org.eclipse.swt.graphics.Color SWT_COLOR_DEFAULT = new org.eclipse.swt.graphics.Color(null, 0, 0, 0);
    public final static int SWT_COLOR_GRAY = SWT.COLOR_GRAY;

    public static String toHexCode(final int r, final int g, final int b) {
        return ConstantsUtil.POUND + toHexString(r) + toHexString(g) + toHexString(b);
    }

    private static String toHexString(final int number) {
        String hex = Integer.toHexString(number).toUpperCase();
        while (hex.length() < 2) {
            hex = 0 + hex;
        }
        return hex;
    }

} public class ConstantsUtil {

    public static final String COMMA = ",";
    public static final String COLON = ":";
    public static final String SEMICOLON = ";";
    public static final String DOT = ".";
    public static final String POUND = "#";
    public static final String STAR = "*";
    public static final String MD5 = "MD5";
    public static final String LEFT_PARENTHESIS = "(";
    public static final String RIGHT_PARENTHESIS = ")";
    public static final String LEFT_BRACE = "{";
    public static final String RIGHT_BRACE = "}";
    public static final String LESS_THAN = "<";
    public static final String GREATER_THAN = ">";
    public static final String RIGHT_ANGLE_BRACKET = GREATER_THAN;
    public static final String LEFT_ANGLE_BRACKET = LESS_THAN;
    public static final String IMG_PNG = "png";
    public static final String PLUS = "+";
    public static final String MINUS = "-";
    public static final String EQUAL = "=";
    public static final String EXCLAMATION = "!";
    public static final String DOUBLE_QUOTE = "\"";
    public static final String SINGLE_QUOTE = "'";
    public static String SLASH = "/";
    public static String BACKSLASH = "\\";
    public static String QUESTION_MARK = "?";
    public static final String TAB = "\t";

} public class EclipseUtil {

    public static List<File> getSelectedFiles(final ExecutionEvent event) throws NoDataFoundException {
        ISelection selection = HandlerUtil.getActiveWorkbenchWindow(event).getActivePage().getSelection();

        final List<File> files = ListUtil.list();

        if (selection instanceof ITextSelection) {
            IEditorPart editorPart = HandlerUtil.getActiveEditor(event);
            if (editorPart == null) {
                throw new NoDataFoundException(Messages.Plugin_Error_NoFile);
            }
            IFile iFile = (IFile) editorPart.getEditorInput().getAdapter(IFile.class);
            if (iFile != null) {
                File file = iFile.getLocation().makeAbsolute().toFile();
                files.add(file);
            } else {
                throw new NoDataFoundException(Messages.Plugin_Error_NoFile);
            }
        } else if (selection instanceof IStructuredSelection) {
            Iterator<?> iterator = ((StructuredSelection) selection).iterator();
            while (iterator.hasNext()) {
                IFile iFile = null;
                Object object = iterator.next();
                if (object instanceof IFile) {
                    iFile = (IFile) object;
                } else if (object instanceof IAdaptable) {
                    IAdaptable adapt = (IAdaptable) object;
                    Object resource = adapt.getAdapter(IResource.class);
                    if (resource instanceof IFile) {
                        iFile = (IFile) resource;
                    }
                }
                if (iFile != null) {
                    File file = iFile.getLocation().makeAbsolute().toFile();
                    files.add(file);
                } else {
                    throw new NoDataFoundException(Messages.Plugin_Error_NoFile);
                }
            }
        }

        return files;
    }

    public static List<List<StyleText>> getSelectedStyleText(final StyledText styledText) {
        Point selection = styledText.getSelection();
        String selectionText = styledText.getSelectionText();
        if (StringUtils.isEmpty(selectionText)) {
            return ListUtil.list();
        }

        int size = 10; // 10 as default value in eclipse, will be overwritten by custom
        String face = StringUtils.EMPTY;
        FontStyle foreStyle = FontStyle.NORMAL;
        FontData[] fontDatas = styledText.getFont().getFontData(); // On Windows, only one FontData will be returned per font. On X however, a Font object may be composed of multiple X fonts.
        if (ArrayUtils.isNotEmpty(fontDatas)) {
            size = fontDatas[0].getHeight();

            try {
                foreStyle = FontStyle.forNumber(fontDatas[0].getStyle());
            } catch (IllegalArgumentException e) {
                foreStyle = FontStyle.NORMAL;
            }
            LogUtil.debug(Messages.bind(Messages.Plugin_Debug_Default_Font_Style, foreStyle));

            List<String> fontFamily = ListUtil.list();
            for (FontData f : fontDatas) {
                if (StringUtils.isNotBlank(f.getName())) {
                    fontFamily.add(f.getName());
                }
            }
            face = StringUtils.join(fontFamily, ConstantsUtil.COMMA);
        }

        List<List<StyleText>> list = ListUtil.list();
        int start = 0;
        while (start >= 0) {
            int end = StringUtil.indexOfAny(selectionText, ArrayUtils.toArray(StringUtil.CRLF, StringUtils.CR, StringUtils.LF), start);

            String line = selectionText.substring(start, end < 0 ? selectionText.length() : end);

            int offset = selection.x + start;
            StyleRange[] ranges = styledText.getStyleRanges(offset, line.length());
            List<StyleText> textRanges = parseLine(line, ranges, offset, face, String.valueOf(size), ObjectUtils.defaultIfNull(styledText.getForeground(), ColorUtil.SWT_COLOR_DEFAULT), foreStyle);
            list.add(textRanges);

            start = end < 0 ? end : end + (selectionText.startsWith(StringUtil.CRLF, end) ? StringUtil.CRLF.length() : selectionText.startsWith(StringUtils.CR, end) ? StringUtils.CR.length() : StringUtils.LF.length());
        }

        return list;
    }

    // [PlainText][StyledText][PlainText]
    private static List<StyleText> parseLine(final String text, final StyleRange[] styleRanges, final int offset, final String face, final String size, final Color defaultForeColor, final FontStyle defaultForeStyle) {
        List<StyleText> textRanges = ListUtil.list();

        if (ArrayUtils.isEmpty(styleRanges)) {
            StyleText textRange = new StyleText(text, face, ColorUtil.toHexCode(defaultForeColor.getRed(), defaultForeColor.getGreen(), defaultForeColor.getBlue()), size, defaultForeStyle);
            textRanges.add(textRange);
            return textRanges;
        }

        int count = 0;
        for (StyleRange styleRange : styleRanges) {
            int start = styleRange.start - offset;

            // [PlainText] - Part1
            String part = text.substring(count, start);
            if (!StringUtils.isEmpty(part)) {
                StyleText textRange = new StyleText(part, face, ColorUtil.toHexCode(defaultForeColor.getRed(), defaultForeColor.getGreen(), defaultForeColor.getBlue()), size, defaultForeStyle);
                textRanges.add(textRange);
                count += part.length();
            }

            // // [StyledText]
            part = text.substring(start, start + styleRange.length);
            Color foreground = styleRange.foreground != null ? styleRange.foreground : defaultForeColor;
            FontStyle fontStyle;
            try {
                FontStyle rangeStyle = FontStyle.forNumber(styleRange.fontStyle);
                LogUtil.debug(Messages.bind(Messages.Plugin_Debug_StyleRange_Font_Style, rangeStyle));

                if (rangeStyle == FontStyle.NORMAL && defaultForeStyle != FontStyle.NORMAL) {
                    fontStyle = defaultForeStyle;
                } else {
                    fontStyle = rangeStyle;
                }
            } catch (IllegalArgumentException e) {
                fontStyle = defaultForeStyle;
            }
            LogUtil.debug(Messages.bind(Messages.Plugin_Debug_FinalConcluded_Font_Style, fontStyle));

            StyleText textRange = new StyleText(part, face, ColorUtil.toHexCode(foreground.getRed(), foreground.getGreen(), foreground.getBlue()), size, fontStyle);
            textRanges.add(textRange);
            count += part.length();
        }
        // [PlainText] - Part2
        String part = text.substring(count);
        if (!StringUtils.isEmpty(part)) {
            StyleText textRange = new StyleText(part, face, ColorUtil.toHexCode(defaultForeColor.getRed(), defaultForeColor.getGreen(), defaultForeColor.getBlue()), size, defaultForeStyle);
            textRanges.add(textRange);
        }

        return textRanges;
    }

    public static boolean isBundleInstalled(final String bundleId) {
        return Platform.getBundle(bundleId) != null;
    }

    public static SimpleContentProposalProvider enableFilteringContentAssist(final Control control, final String[] proposals, final String byOperator) {
        Arrays.sort(proposals);
        ConfigContentProposalProvider contentProposalProvider = new ConfigContentProposalProvider(proposals);
        contentProposalProvider.setFiltering(true);
        contentProposalProvider.setByOperator(byOperator);

        ConfigTextContentAdapter textContentAdapter = new ConfigTextContentAdapter();
        textContentAdapter.setByOperator(byOperator);

        new ContentProposalAdapter(control, textContentAdapter, contentProposalProvider, null, null);

        return contentProposalProvider;
    }

    public static SimpleContentProposalProvider enableFilteringContentAssist(final Control control, final String[] proposals) {
        Arrays.sort(proposals);
        SimpleContentProposalProvider contentProposalProvider = new SimpleContentProposalProvider(proposals);
        contentProposalProvider.setFiltering(true);

        TextContentAdapter textContentAdapter = new TextContentAdapter();

        ContentProposalAdapter contentProposalAdapter = new ContentProposalAdapter(control, textContentAdapter, contentProposalProvider, null, null);
        contentProposalAdapter.setProposalAcceptanceStyle(ContentProposalAdapter.PROPOSAL_REPLACE);

        return contentProposalProvider;
    }

    public static void openErrorSyncly(final Shell shell, final String title, final String message) {
        Display.getDefault().syncExec(new Runnable() {
            @Override
            public void run() {
                MessageDialog.openError(shell, title, message);
            }
        });
    }

    public static String openCustomImageTypeWithCustomButtons(final Shell shell, final String title, final String message, final Image dialogImage, final LinkedHashMap<String, String> buttons) {
        return String.valueOf(openCustomImageTypeWithCustomButtons(shell, title, message, dialogImage, buttons, null, false).get(Constants.returnCode));
    }

    public static Map<String, Object> openCustomImageTypeWithCustomButtons(final Shell shell, final String title, final String message, final Image dialogImage, final LinkedHashMap<String, String> buttons, final String toggleMessage, final boolean defaultToggleState) {
        MessageDialog dialog;

        if (StringUtils.isBlank(toggleMessage)) {
            dialog = new MessageDialog(shell, title, null, message, MessageDialog.NONE, buttons.values().toArray(new String[buttons.size()]), 0) {
                @Override
                public Image getImage() {
                    return dialogImage;
                }
            };
        } else {
            dialog = new MessageDialogWithToggle(shell, title, null, message, MessageDialog.NONE, buttons.values().toArray(new String[buttons.size()]), 0, toggleMessage, defaultToggleState) {
                @Override
                public Image getImage() {
                    return dialogImage;
                }

                @Override
                protected void createButtonsForButtonBar(final Composite parent) {
                    String[] buttonLabels = getButtonLabels();
                    Button[] buttons = new Button[buttonLabels.length];
                    int defaultButtonIndex = getDefaultButtonIndex();

                    for (int i = 0; i < buttonLabels.length; i++) {
                        String label = buttonLabels[i];
                        Button button = createButton(parent, i, label, defaultButtonIndex == i);
                        buttons[i] = button;
                    }

                    setButtons(buttons);
                }
            };
        }

        dialog.open();

        Map<String, Object> map = MapUtil.map();
        map.put(Constants.returnCode, buttons.keySet().toArray()[dialog.getReturnCode()]);
        if (dialog instanceof MessageDialogWithToggle) {
            map.put(Constants.toggleState, ((MessageDialogWithToggle) dialog).getToggleState());
        }

        return map;
    }

    public static String openInformationWithCustomButtons(final Shell shell, final String title, final String message, final LinkedHashMap<String, String> buttons) {
        MessageDialog dialog = new MessageDialog(shell, title, null, message, MessageDialog.INFORMATION, buttons.values().toArray(new String[buttons.size()]), 0);
        dialog.open();
        return (String) buttons.keySet().toArray()[dialog.getReturnCode()];
    }

    public static String openQuestionWithCustomButtons(final Shell shell, final String title, final String message, final LinkedHashMap<String, String> buttons) {
        MessageDialog dialog = new MessageDialog(shell, title, null, message, MessageDialog.QUESTION_WITH_CANCEL, buttons.values().toArray(new String[buttons.size()]), 0);
        dialog.open();
        return (String) buttons.keySet().toArray()[dialog.getReturnCode()];
    }

    public static String openWarningWithCustomButtons(final Shell shell, final String title, final String message, final LinkedHashMap<String, String> buttons) {
        MessageDialog dialog = new MessageDialog(shell, title, null, message, MessageDialog.WARNING, buttons.values().toArray(new String[buttons.size()]), 0);
        dialog.open();
        return (String) buttons.keySet().toArray()[dialog.getReturnCode()];
    }

    public static String openErrorWithCustomButtons(final Shell shell, final String title, final String message, final LinkedHashMap<String, String> buttons) {
        MessageDialog dialog = new MessageDialog(shell, title, null, message, MessageDialog.ERROR, buttons.values().toArray(new String[buttons.size()]), 0);
        dialog.open();
        return (String) buttons.keySet().toArray()[dialog.getReturnCode()];
    }

} public class EncryptionUtil {

    private static BasicTextEncryptor textEncryptor;

    static {
        textEncryptor = new BasicTextEncryptor();
        textEncryptor.setPassword(StringUtils.toEncodedString(Base64.decodeBase64("ZWVtb3J5"), Charset.forName(CharEncoding.UTF_8)));
    }

    public static String encrypt(final String plainText) {
        return textEncryptor.encrypt(plainText);
    }

    public static String decrypt(final String encryptedText) {
        return textEncryptor.decrypt(encryptedText);
    }

} public class IDialogSettingsUtil {

    private static IDialogSettings settings = EemoryPlugin.getDefault().getDialogSettings();

    public static String get(final String key) {
        return settings.get(key);
    }

    public static boolean getBoolean(final String key) {
        return settings.getBoolean(key);
    }

    public static void set(final String key, final String value) {
        settings.put(key, value);
    }

    public static void set(final String key, final boolean value) {
        settings.put(key, value);
    }

    public static String get(final String sectionName, final String key) {
        IDialogSettings section = settings.getSection(sectionName);
        return section != null ? section.get(key) : null;
    }

    public static boolean getBoolean(final String sectionName, final String key) {
        IDialogSettings section = settings.getSection(sectionName);
        return section != null && section.getBoolean(key);
    }

    public static void set(final String sectionName, final String key, final String value) {
        IDialogSettings section = getOrCreateSection(sectionName);
        section.put(key, value);
    }

    public static void set(final String sectionName, final String key, final boolean value) {
        IDialogSettings section = getOrCreateSection(sectionName);
        section.put(key, value);
    }

    public static IDialogSettings getOrCreateSection(final String sectionName) {
        IDialogSettings section = settings.getSection(sectionName);
        if (section == null) {
            section = settings.addNewSection(sectionName);
        }
        return section;
    }

} public final class ListUtil {

    public static String[] toStringArray(final Collection<String> list) {
        if (list == null) {
            return null;
        }
        String[] array = new String[list.size()];
        return list.toArray(array);
    }

    public static String[] toStringArray(final List<?> list, final ListStringizer stringizer) {
        if (list == null) {
            return null;
        }
        String[] array = new String[list.size()];
        for (int i = 0; i < list.size(); i++) {
            array[i] = stringizer.element(list.get(i));
        }
        return array;
    }

    public static List<String> toStringList(final List<?> list, final ListStringizer stringizer) {
        if (list == null) {
            return null;
        }
        List<String> strList = list();
        for (int i = 0; i < list.size(); i++) {
            strList.add(stringizer.element(list.get(i)));
        }
        return strList;
    }

    public static Map<String, String> toStringMap(final List<?> list, final MapStringizer stringizer) {
        if (list == null) {
            return null;
        }
        Map<String, String> map = MapUtil.map();
        for (int i = 0; i < list.size(); i++) {
            map.put(stringizer.key(list.get(i)), stringizer.value(list.get(i)));
        }
        return map;
    }

    public static <T> List<T> toList(final T[] array) {
        List<T> l = list(array.length);
        l.addAll(Arrays.asList(array));
        return l;
    }

    public static <T> List<T> list(final int initialCapacity) {
        return new ArrayList<T>(initialCapacity);
    }

    public static <T> List<T> list() {
        return new ArrayList<T>();
    }

    @SafeVarargs
    public static <T> List<T> list(final T... objects) {
        List<T> l = list();
        l.addAll(toList(objects));
        return l;
    }

    public static boolean isNullOrEmptyList(final List<?> list) {
        return list == null || list.size() == 0;
    }

    public static boolean isNullList(final Collection<?> list) {
        return list == null;
    }

    public static <T> boolean isIndexOutOfBounds(final List<T> list, final int index) {
        return isNullList(list) || index >= 0 && index < list.size();
    }

    public static <T> void replace(final List<T> list, final T newElement, final int index) {
        if (!isIndexOutOfBounds(list, index)) {
            list.remove(index);
            list.add(index, newElement);
        }
    }

    public static boolean isEqualList(final List<?> one, final List<?> other) {
        return isEqualList(one, other, false);
    }

    public static boolean isEqualList(final List<?> one, final List<?> other, final boolean compareOrder) {
        if (one == other) {
            return true;
        }
        if (isNullList(one) || isNullList(other)) {
            return false;
        }
        if (one.size() != other.size()) {
            return false;
        }
        if (compareOrder) {
            Iterator<?> iter1 = one.iterator();
            Iterator<?> iter2 = other.iterator();
            while (iter1.hasNext() && iter2.hasNext()) {
                if (!ObjectUtil.isEqualObject(iter1.next(), iter2.next())) {
                    return false;
                }
            }
        } else {
            List<Integer> matchedIndex = ListUtil.list();
            andContinue: for (Object o1 : one) {
                for (int i = 0; i < other.size(); i++) {
                    if (matchedIndex.contains(i)) {
                        continue;
                    }
                    if (ObjectUtil.isEqualObject(o1, other.get(i), compareOrder)) {
                        matchedIndex.add(i);
                        break andContinue;
                    }
                }
                return false;
            }
        }
        return true;
    }

    public static <E> List<E> cloneList(final ArrayList<E> source) {
        return cloneList(source, false);
    }

    public static <E> List<E> cloneList(final ArrayList<E> source, final boolean deep) {
        if (!deep) {
            return ObjectUtils.clone(source);
        }
        if (isNullList(source)) {
            return null;
        }
        List<E> list = list(source.size());
        for (E e : source) {
            list.add(ObjectUtil.cloneObject(e, deep));
        }
        return list;
    }

} public final class MapUtil {

    public static <K, V> Map<K, V> map() {
        return new HashMap<K, V>();
    }

    public static <K, V> LinkedHashMap<K, V> orderedMap() {
        return new LinkedHashMap<K, V>();
    }

    public static <K, V> Map<K, V> map(final int initialCapacity) {
        return new HashMap<K, V>(initialCapacity);
    }

    public static <K, V> Map<K, V> map(final Map<? extends K, ? extends V> source) {
        return new HashMap<K, V>(source);
    }

    public static <K, V> Map<K, V> map(final K k, final V v) {
        Map<K, V> map = MapUtil.map();
        map.put(k, v);
        return map;
    }

    public static <K, V> ConcurrentHashMap<K, V> concurrentMap() {
        return new ConcurrentHashMap<K, V>();
    }

    public static <K, V> SortedMap<K, V> sortedMap() {
        return new TreeMap<K, V>();
    }

    public static <K, V> SortedMap<K, V> sortedMap(final Comparator<K> comparator) {
        return new TreeMap<K, V>(comparator);
    }

    public static <K, V> SortedMap<K, V> sortedMap(final SortedMap<? extends K, ? extends V> source) {
        return new TreeMap<K, V>(source);
    }

    public static <K, V> boolean isEmpty(final Map<K, V> map) {
        return map == null || map.isEmpty();
    }

    public static <K, V> boolean isNull(final Map<K, V> map) {
        return map == null;
    }

    public static boolean isEqualList(final Map<?, ?> map1, final Map<?, ?> map2) {
        if (map1 == map2) {
            return true;
        }
        if (isNull(map1) || isNull(map2)) {
            return false;
        }
        if (map1.size() != map2.size()) {
            return false;
        }
        for (Entry<?, ?> e : map1.entrySet()) {
            if (!ObjectUtil.isEqualObject(map1.get(e.getKey()), map2.get(e.getKey()))) {
                return false;
            }
        }
        return true;
    }

    public static <K, V> Map<K, V> cloneMap(final HashMap<K, V> source) {
        return cloneMap(source, false);
    }

    public static <K, V> Map<K, V> cloneMap(final HashMap<K, V> source, final boolean deep) {
        if (!deep) {
            return ObjectUtils.clone(source);
        }
        if (isNull(source)) {
            return null;
        }
        Map<K, V> map = map(source.size());
        for (Entry<K, V> e : source.entrySet()) {
            map.put(ObjectUtil.cloneObject(e.getKey(), deep), ObjectUtil.cloneObject(e.getValue(), deep));
        }
        return map;
    }

    /**
     * The first key that value is matched, preferred to be used when value is
     * unique.
     */
    public static <K, V> K getKeyByValue(final Map<K, V> map, final V value) {
        for (K k : map.keySet()) {
            if (map.get(k).equals(value)) {
                return k;
            }
        }
        return null;
    }

} public class ObjectUtil {

    public static boolean isEqualObject(final Object one, final Object other) {
        return isEqualObject(one, other, false);
    }

    public static boolean isEqualObject(final Object one, final Object other, final boolean compareOrder) {
        if (one == other) {
            return true;
        }
        if (one == null || other == null) {
            return false;
        }
        if (one instanceof Map && other instanceof Map) {
            return MapUtil.isEqualList((Map<?, ?>) one, (Map<?, ?>) other);
        }
        if (one instanceof List && other instanceof List) {
            return ListUtil.isEqualList((List<?>) one, (List<?>) other, compareOrder);
        }
        if (one instanceof Object[] && other instanceof Object[]) {
            return ArrayUtil.isEqualArray((Object[]) one, (Object[]) other);
        }
        return one.equals(other);
    }

    public static <T> T cloneObject(final T obj) {
        return cloneObject(obj, false);
    }

    public static <T> T cloneObject(final T obj, final boolean deep) {
        if (!deep) {
            return ObjectUtils.clone(obj);
        }
        if (obj == null) {
            return null;
        }

        Object clone = null;
        if (obj instanceof ArrayList) {
            clone = ListUtil.cloneList((ArrayList<?>) obj, deep);
        } else if (obj instanceof HashMap) {
            clone = MapUtil.cloneMap((HashMap<?, ?>) obj, deep);
        } else if (obj.getClass().isArray()) {
            clone = ArrayUtil.cloneArray((Object[]) obj, deep);
        } else if (!(obj instanceof Cloneable)) {
            clone = cloneCloneNotSupportedObject(obj, deep);
        } else {
            // should not be an Array
            clone = ObjectUtils.clone(obj);
        }

        @SuppressWarnings("unchecked")
        // OK because input is of type T
        final T checkedClone = (T) clone;
        return checkedClone;
    }

    @SuppressWarnings("unchecked")
    protected static <T> T cloneCloneNotSupportedObject(final T obj, final boolean deep) {
        if (obj instanceof String) {
            return obj;
        } else if (obj instanceof Byte) {
            return (T) new Byte((Byte) obj);
        } else if (obj instanceof Short) {
            return (T) new Short((Short) obj);
        } else if (obj instanceof Integer) {
            return (T) new Integer((Integer) obj);
        } else if (obj instanceof Long) {
            return (T) new Long((Long) obj);
        } else if (obj instanceof Float) {
            return (T) new Float((Float) obj);
        } else if (obj instanceof Double) {
            return (T) new Double((Double) obj);
        } else if (obj instanceof Boolean) {
            return (T) new Boolean((Boolean) obj);
        } else if (obj instanceof Character) {
            return (T) new Character((Character) obj);
        }
        return null;
    }

    /*
     * Assume object is serializable.
     */
    public static String serialize(final Object object) {
        if (!(object instanceof Serializable)) {
            throw new SerializationException(Messages.bind(Messages.Throwable_NotSerializable_Message, object));
        }
        byte[] bytes = SerializationUtils.serialize((Serializable) object);
        return new String(Base64.encodeBase64(bytes));
    }

    public static Object deserialize(final String base64SerializedString) {
        byte[] bytes = Base64.decodeBase64(base64SerializedString);
        return SerializationUtils.deserialize(bytes);
    }

    public static String toString(final Object obj) {
        return obj == null ? StringUtils.EMPTY : obj.toString();
    }

} public final class StringUtil {

    public static final String CRLF = org.apache.commons.lang3.StringUtils.CR + org.apache.commons.lang3.StringUtils.LF;

    /**
     * <p>
     * Checks if a String is null. This has same effect to
     * {@code string == null}.
     * </p>
     *
     * <pre>
     * StringUtil.isNull(null)      = true
     * StringUtil.isNull("")        = true
     * StringUtil.isNull(" ")       = false
     * StringUtil.isNull("abc")     = false
     * </pre>
     *
     * @param string
     *            the String to check
     * @return {@code true} if the String is null
     */
    public static boolean isNull(final String string) {
        return string == null;
    }

    /**
     * <p>
     * Compares two strings given. The result is true if the two strings are
     * both null, empty or blank, or two strings that represent the same
     * sequence of characters, i.e. {@code str1.equals(str2)}.
     * </p>
     *
     * <pre>
     * StringUtil.equalsInLogic(null, null)   = true
     * StringUtil.equalsInLogic(null, "")  = true
     * StringUtil.equalsInLogic(null, " ")  = true
     * StringUtil.equalsInLogic("abc", "abc") = true
     * StringUtil.equalsIgnoreCase("abc", "ABC") = false
     * StringUtil.equalsIgnoreCase("abc", "abd") = false
     * </pre>
     *
     * @param str1
     *            the first CharSequence, may be null
     * @param str2
     *            the second CharSequence, may be null
     * @return true if the given two strings represent a logic equivalent, false
     *         otherwise
     */
    public static boolean equalsInLogic(final String str1, final String str2) {
        return StringUtils.isBlank(str1) ? StringUtils.isBlank(str2) : str1.equals(str2);
    }

    /**
     * <p>
     * Search the source string for the first substring that matched regular
     * expression.
     * </p>
     *
     * @param source
     *            The character sequence to be searched
     * @param regex
     *            The regular expression to be searched for
     *
     * @return the end offset of matched subsequence, -1 if no subsequence of
     *         the input sequence matches this matcher's pattern
     */
    public static int find(final String source, final String regex) {
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(source);

        boolean find = matcher.find();

        return find ? matcher.end() : -1;
    }

    public static String[] splitByMultipleSeparatorsPreserveAllTokens(final String str, final String[] separators) {
        String[] transition = ArrayUtils.EMPTY_STRING_ARRAY;
        String[] splits = { str };
        for (String sep : separators) {
            for (String part : splits) {
                String[] r = StringUtils.isEmpty(part) ? ArrayUtils.toArray(part) : StringUtils.splitByWholeSeparatorPreserveAllTokens(part, sep);
                transition = ArrayUtils.addAll(transition, r);
            }
            splits = transition;
            transition = ArrayUtils.EMPTY_STRING_ARRAY;
        }
        return splits;
    }

    public static int indexOfAny(final String selectionText, final String[] array, final int startPosition) {
        String subStrFromStart = selectionText.substring(startPosition);
        int i = StringUtils.indexOfAny(subStrFromStart, array);
        return i < 0 ? i : startPosition + i;
    }

    /**
     * <pre>
     * StringUtil.toQuotedString("abc") = ""abc"";
     * </pre>
     *
     * @param str
     *            string to quote, may be null.
     * @return quoted string
     */
    public static String toQuotedString(final String str) {
        return ConstantsUtil.DOUBLE_QUOTE + str + ConstantsUtil.DOUBLE_QUOTE;
    }

    public static String toSingleQuotedString(final String str) {
        return ConstantsUtil.SINGLE_QUOTE + str + ConstantsUtil.SINGLE_QUOTE;
    }
}
import static org.lttpp.eemory.Constants.PLUGIN_CONFIGS_COMMENTS;
import static org.lttpp.eemory.Constants.PLUGIN_CONFIGS_NOTE;
import static org.lttpp.eemory.Constants.PLUGIN_CONFIGS_NOTEBOOK;
import static org.lttpp.eemory.Constants.PLUGIN_CONFIGS_REFRESH_ID;
import static org.lttpp.eemory.Constants.PLUGIN_CONFIGS_TAGS;
import static org.lttpp.eemory.Constants.PLUGIN_SETTINGS_KEY_CHECKED;
import static org.lttpp.eemory.Constants.PLUGIN_SETTINGS_KEY_GUID;
import static org.lttpp.eemory.Constants.PLUGIN_SETTINGS_KEY_NAME;
import static org.lttpp.eemory.Constants.PLUGIN_SETTINGS_KEY_OBJECT;
import static org.lttpp.eemory.Constants.PLUGIN_SETTINGS_KEY_TOKEN;
import static org.lttpp.eemory.Constants.PLUGIN_SETTINGS_KEY_TYPE;
import static org.lttpp.eemory.Constants.PLUGIN_SETTINGS_KEY_UUID;
import static org.lttpp.eemory.Constants.PLUGIN_SETTINGS_SECTION_COMMENTS;
import static org.lttpp.eemory.Constants.PLUGIN_SETTINGS_SECTION_NOTE;
import static org.lttpp.eemory.Constants.PLUGIN_SETTINGS_SECTION_NOTEBOOK;
import static org.lttpp.eemory.Constants.PLUGIN_SETTINGS_SECTION_TAGS;
import static org.lttpp.eemory.Constants.TAGS_SEPARATOR;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.fieldassist.SimpleContentProposalProvider;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.lttpp.eemory.Messages;
import org.lttpp.eemory.client.EeClipper;
import org.lttpp.eemory.client.EeClipperFactory;
import org.lttpp.eemory.client.impl.model.ENNoteImpl;
import org.lttpp.eemory.client.impl.model.ENObjectImpl;
import org.lttpp.eemory.client.metadata.EDAMLimits;
import org.lttpp.eemory.client.metadata.ENObjectType;
import org.lttpp.eemory.client.model.ENNote;
import org.lttpp.eemory.client.model.ENObject;
import org.lttpp.eemory.exception.EDAMNotFoundHandler;
import org.lttpp.eemory.exception.ThrowableHandler;
import org.lttpp.eemory.util.ColorUtil;
import org.lttpp.eemory.util.ConstantsUtil;
import org.lttpp.eemory.util.EclipseUtil;
import org.lttpp.eemory.util.EncryptionUtil;
import org.lttpp.eemory.util.IDialogSettingsUtil;
import org.lttpp.eemory.util.ListUtil;
import org.lttpp.eemory.util.MapUtil;
import org.lttpp.eemory.util.ObjectUtil;
import org.lttpp.eemory.util.StringUtil;
        setSection(PLUGIN_SETTINGS_SECTION_NOTE, note != null ? note.getName() : null, isFieldEditable(PLUGIN_CONFIGS_NOTE), note != null ? note.getGuid() : null, noteValue);
    }

    @Override
    protected void okPressed() {
        try {
            saveSettings();
        } catch (IOException e) {
            ThrowableHandler.handleDesignTimeErr(shell, e);
        }
        super.okPressed();
    }

    @Override
    protected Point getInitialSize() {
        return new Point(550, 400);
    }

    public static int show(final Shell shell) {
        ConfigurationsDialog dialog = new ConfigurationsDialog(shell);
        return dialog.open();
    }

    private boolean shouldRefresh(final String uniqueKey, final String property) {
        return fieldValueChanged(uniqueKey, property);
    }

    private boolean fieldValueChanged(final String uniqueKey, final String property) {
        if (matrix == null) {
            matrix = MapUtil.map();
        }
        Map<String, String> map = matrix.get(uniqueKey);
        if (map == null) {
            map = MapUtil.map();
            matrix.put(uniqueKey, map);
        }
        String fieldValue = isHasInput(property) ? getFieldValue(property) : StringUtils.EMPTY;
        if (!StringUtil.equalsInLogic(fieldValue, map.get(property))) {
            map.put(property, getFieldValue(property));
            return true;
        }
        return false;
    }

    private void saveSettings() throws IOException {
        diagnoseNotebook();
        String notebookValue = getFieldInput(PLUGIN_CONFIGS_NOTEBOOK);
        ENObject notebook = notebooks.get(notebookValue);
        setSection(PLUGIN_SETTINGS_SECTION_NOTEBOOK, notebook != null ? notebook.getName() : null, isFieldEditable(PLUGIN_CONFIGS_NOTEBOOK), notebook != null ? notebook.getGuid() : null, notebookValue, notebook != null ? notebook.getType().toString() : null);
        if (notebook != null && notebook.getType() == ENObjectType.LINKED) {
            // notebook.getLinkedObject() should NOT be null
            IDialogSettingsUtil.set(PLUGIN_SETTINGS_SECTION_NOTEBOOK, PLUGIN_SETTINGS_KEY_OBJECT, ObjectUtil.serialize(notebook.getLinkedObject()));
        } else {
            IDialogSettingsUtil.set(PLUGIN_SETTINGS_SECTION_NOTEBOOK, PLUGIN_SETTINGS_KEY_OBJECT, null);
        }

        diagnoseNote();
        String noteValue = getFieldInput(PLUGIN_CONFIGS_NOTE);
        ENNote note = notes.get(noteValue);
        setSection(PLUGIN_SETTINGS_SECTION_NOTE, note != null ? note.getName() : null, isFieldEditable(PLUGIN_CONFIGS_NOTE), note != null ? note.getGuid() : null, noteValue);

        String tagsValue = getFieldInput(PLUGIN_CONFIGS_TAGS);
        setSection(PLUGIN_SETTINGS_SECTION_TAGS, tagsValue, isFieldEditable(PLUGIN_CONFIGS_TAGS));

        setSection(PLUGIN_SETTINGS_SECTION_COMMENTS, getFieldInput(PLUGIN_CONFIGS_COMMENTS), isFieldEditable(PLUGIN_CONFIGS_COMMENTS));
    }

    private void restoreSettings(final String label) {
        if (label.equals(PLUGIN_CONFIGS_NOTEBOOK)) {
            editableField(PLUGIN_CONFIGS_NOTEBOOK, IDialogSettingsUtil.getBoolean(PLUGIN_SETTINGS_SECTION_NOTEBOOK, PLUGIN_SETTINGS_KEY_CHECKED));
            String value = IDialogSettingsUtil.get(PLUGIN_SETTINGS_SECTION_NOTEBOOK, PLUGIN_SETTINGS_KEY_UUID);
            if (isFieldEditable(PLUGIN_CONFIGS_NOTEBOOK) && StringUtils.isNotBlank(value)) {
                setFieldValue(PLUGIN_CONFIGS_NOTEBOOK, value);
                setHasInput(PLUGIN_CONFIGS_NOTEBOOK, true);
            }
        } else if (label.equals(PLUGIN_CONFIGS_NOTE)) {
            editableField(PLUGIN_CONFIGS_NOTE, IDialogSettingsUtil.getBoolean(PLUGIN_SETTINGS_SECTION_NOTE, PLUGIN_SETTINGS_KEY_CHECKED));
            String value = IDialogSettingsUtil.get(PLUGIN_SETTINGS_SECTION_NOTE, PLUGIN_SETTINGS_KEY_UUID);
            if (isFieldEditable(PLUGIN_CONFIGS_NOTE) && StringUtils.isNotBlank(value)) {
                setFieldValue(PLUGIN_CONFIGS_NOTE, value);
                setHasInput(PLUGIN_CONFIGS_NOTE, true);
            }
        } else if (label.equals(PLUGIN_CONFIGS_TAGS)) {
            editableField(PLUGIN_CONFIGS_TAGS, IDialogSettingsUtil.getBoolean(PLUGIN_SETTINGS_SECTION_TAGS, PLUGIN_SETTINGS_KEY_CHECKED));
            String value = IDialogSettingsUtil.get(PLUGIN_SETTINGS_SECTION_TAGS, PLUGIN_SETTINGS_KEY_NAME);
            if (isFieldEditable(PLUGIN_CONFIGS_TAGS) && StringUtils.isNotBlank(value)) {
                setFieldValue(PLUGIN_CONFIGS_TAGS, value);
                setHasInput(PLUGIN_CONFIGS_TAGS, true);
            }
        } else if (label.equals(PLUGIN_CONFIGS_COMMENTS)) {
            editableField(PLUGIN_CONFIGS_COMMENTS, IDialogSettingsUtil.getBoolean(PLUGIN_SETTINGS_SECTION_COMMENTS, PLUGIN_SETTINGS_KEY_CHECKED));
            String value = IDialogSettingsUtil.get(PLUGIN_SETTINGS_SECTION_COMMENTS, PLUGIN_SETTINGS_KEY_NAME);
            if (isFieldEditable(PLUGIN_CONFIGS_COMMENTS) && StringUtils.isNotBlank(value)) {
                setFieldValue(PLUGIN_CONFIGS_COMMENTS, value);
            }
        }
    }

    private void setSection(final String sectionName, final String name, final boolean isChecked, final String guid, final String uuid, final String type) {
        setSection(sectionName, name, isChecked, guid, uuid);
        IDialogSettingsUtil.set(sectionName, PLUGIN_SETTINGS_KEY_TYPE, type);
    }

    private void setSection(final String sectionName, final String name, final boolean isChecked, final String guid, final String uuid) {
        setSection(sectionName, name, isChecked, guid);
        IDialogSettingsUtil.set(sectionName, PLUGIN_SETTINGS_KEY_UUID, uuid);
    }

    private void setSection(final String sectionName, final String name, final boolean isChecked, final String guid) {
        setSection(sectionName, name, isChecked);
        IDialogSettingsUtil.set(sectionName, PLUGIN_SETTINGS_KEY_GUID, guid);
    }

    private void setSection(final String sectionName, final String name, final boolean isChecked) {
        IDialogSettingsUtil.set(sectionName, PLUGIN_SETTINGS_KEY_NAME, name);
        IDialogSettingsUtil.set(sectionName, PLUGIN_SETTINGS_KEY_CHECKED, isChecked);
    }

    protected LabelCheckTextField createLabelCheckTextField(final Composite container, final String labelText) {
        final Button button = new Button(container, SWT.CHECK);
        button.setText(labelText + ConstantsUtil.COLON);