package com.sunqian.utils; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.util.TextRange; import com.sunqian.constvalue.ConstValue; import com.sunqian.constvalue.ShortCutType; import com.sunqian.model.ActionPerformer; import lombok.NoArgsConstructor; import lombok.Setter; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import java.math.BigDecimal; import java.text.MessageFormat; import java.util.Objects; import java.util.Optional; import java.util.regex.Pattern; import static com.sunqian.constvalue.MagicValue.*; /** * 转换工具类 * * @author sunqian * @date 2018/12/8 15:39 */ @NoArgsConstructor public class FormatTools { @Setter private volatile static FormatTools formatTools; private ConstValue constValue; private static Pattern NUMBER_PATTERN = Pattern.compile(NUMBER_PATTERN_FORMULA); private FormatTools(ConstValue constValue) { this.constValue = constValue; } public static FormatTools getFormatTools(ConstValue constValue) { return Optional.ofNullable(formatTools).orElseGet(() -> { synchronized (FormatTools.class) { return Optional.ofNullable(formatTools).orElseGet(() -> new FormatTools(constValue)); } }); } private String getFormatText(String ele, ShortCutType shortCutType) { return Optional.ofNullable(ele).filter(text -> text.contains(PX_STYLE_TAG) && !Objects.equals(NULL_STRING, text)).map(text -> Optional.of(NumberUtils.toDouble(text.substring(0, text.indexOf(PX_STYLE_TAG)).trim())).map(px -> valueFormat(px, shortCutType)).get() ).orElse(ele); } private String valueFormat(Double px, ShortCutType shortCutType) { return Optional.of(this.constValue.baseValueType().get(shortCutType)).map(value -> Optional.of(px / value).map(rem -> Optional.of(check(Double.toString(px), Double.toString(value))).filter(ifDivide -> ifDivide).map(ifDivide -> (rem.toString()).replaceAll("0*$", "").replaceAll("\\.$", "") + STYLE_TAG_TYPE.get(shortCutType) + showComment(px, value) ).orElseGet(() -> String.format(getAccuracy(value + ""), rem).replaceAll("0*$", "").replaceAll("\\.$", "").trim() + STYLE_TAG_TYPE.get(shortCutType) + showComment(px, value) ) ).orElse(px.toString()) ).orElse(px.toString()); } /** * 计算回退 * * @param result 计算结果 * @param shortCutType 类型 * @return 返回回退的结果 */ private String valueRollback(String result, ShortCutType shortCutType) { return Optional.ofNullable(result).filter(text -> StringUtils.containsAny(result, REM_STYLE_TAG, VW_STYLE_TAG, VH_STYLE_TAG) && !Objects.equals(NULL_STRING, text)).map(text -> Optional.of(NumberUtils.toDouble(text.substring(0, text.indexOf(STYLE_TAG_TYPE.get(shortCutType).toString())).trim())).map(res -> Optional.ofNullable(this.constValue.baseValueType().get(shortCutType)).map(value -> Optional.of(Math.round(res * value) + PX_STYLE_TAG).orElse(result)).orElse(result) ).get() ).orElse(result); } private String getFormatLine(String content, ShortCutType shortCutType, String styleTag, FormatStyleValue<String> formatStyleValue) { int index; while ((index = StringUtils.indexOf(content.toLowerCase(), styleTag)) > -1) { int startIndex = index; while (isNumeric(content.substring(startIndex - 1, index)) && startIndex > 0) { startIndex--; } if (startIndex != index) { String value = content.substring(startIndex, index) + styleTag; content = content.substring(0, startIndex) + formatStyleValue.apply(value, shortCutType) + content.substring(index + styleTag.length()); } else { break; } } return content; } @FunctionalInterface private interface FormatStyleValue<R> { R apply(String value, ShortCutType shortCutType); } /** * 回退转换过程 * * @param actionPerformer 动作参数 * @param lineNum 行号 */ public void rollbackStyle(ActionPerformer actionPerformer, int lineNum) { Optional.of(actionPerformer.getDocument().getLineStartOffset(lineNum)).ifPresent(lineStartOffset -> Optional.of(actionPerformer.getDocument().getLineEndOffset(lineNum)).ifPresent(lineEndOffset -> Optional.of(actionPerformer.getDocument().getText(new TextRange(lineStartOffset, lineEndOffset))) .map(lineContent -> { WriteCommandAction.runWriteCommandAction(actionPerformer.getProject(), () -> actionPerformer.getDocument().replaceString( lineStartOffset, lineEndOffset, getFormatLine(getFormatLine(getFormatLine(lineContent, ShortCutType.REM, REM_STYLE_TAG, this::valueRollback), ShortCutType.VW, VW_STYLE_TAG, this::valueRollback), ShortCutType.VH, VH_STYLE_TAG, this::valueRollback)) ); return lineContent; }) ) ); } /** * 判断字符串是否为数字 * * @param str 被判断的字符串 * @return 返回是否是数字 */ private static boolean isNumeric(String str) { return NUMBER_PATTERN.matcher(str).matches(); } /** * 检查是否可以被除尽 * * @param amount 被除数 * @param count 除数 * @return 是否可以被除尽 */ @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") private boolean check(String amount, String count) { return Optional.of(new BigDecimal(amount.replaceAll("\\.", "")).divideAndRemainder(new BigDecimal(count.replaceAll("\\.", "")))[1]).filter(times -> Objects.equals(times, new BigDecimal("0"))).map(times -> true).orElseGet(() -> Objects.equals(new BigDecimal(amount.replaceAll("\\.", "")).divideAndRemainder(LogicUtils.getLogic().funWithWhile(LogicUtils.getLogic().funWithWhile(new BigDecimal(count.replaceAll("\\.", "")), m -> Objects.equals(m.divideAndRemainder(new BigDecimal("2"))[1], new BigDecimal("0")), m -> m.divide(new BigDecimal("2"))), n -> Objects.equals(n.divideAndRemainder(new BigDecimal("5"))[1], new BigDecimal("0")), n -> n.divide(new BigDecimal("5"))))[1], new BigDecimal("0")) ); } /** * 通过注释的方式生成计算过程 * * @param obj1 被除数 * @param obj2 除数 * @return 返回计算公式 */ private String showComment(Object obj1, Object obj2) { return constValue.getShowCalculationProcess() ? " /* " + obj1.toString().replaceAll("0*$", "").replaceAll("\\.$", "") + "/" + obj2.toString().replaceAll("0*$", "").replaceAll("\\.$", "") + " */" : ""; } private String getAccuracy(String remValue) { return MessageFormat.format("%.{0}f", remValue.substring(0, !StringUtils.contains(remValue, ".") ? remValue.length() : StringUtils.indexOf(remValue, ".")).length() + 1); } /** * 转换一行的代码 * <p> * 这里行号取的是当前光标所在的行 * * @param actionPerformer 获取的动作参数 */ public void formatLineCode(ActionPerformer actionPerformer, ShortCutType shortCutType) { formatLineCode(actionPerformer, actionPerformer.getDocument().getLineNumber(actionPerformer.getCaretModel().getOffset()), shortCutType); } /** * 转换一行代码中的样式单位 * * @param actionPerformer 获取的动作参数 * @param lineNum 行号 */ public void formatLineCode(ActionPerformer actionPerformer, int lineNum, ShortCutType shortCutType) { // 行起始offset Optional.of(actionPerformer.getDocument().getLineStartOffset(lineNum)).ifPresent(lineStartOffset -> { // 行结束offset Optional.of(actionPerformer.getDocument().getLineEndOffset(lineNum)).ifPresent(lineEndOffset -> Optional.of(actionPerformer.getDocument().getText(new TextRange(lineStartOffset, lineEndOffset))) .filter(lineContent -> lineContent.toLowerCase().contains(PX_STYLE_TAG)) .map(lineContent -> { WriteCommandAction.runWriteCommandAction(actionPerformer.getProject(), () -> actionPerformer.getDocument().replaceString( lineStartOffset, lineEndOffset, getFormatLine(lineContent, shortCutType, PX_STYLE_TAG, (value, type) -> getFormatText(value, shortCutType))) ); return lineContent; }) ); }); } /** * 格式化光标处往前最近的一个可格化的长度 * * @param actionPerformer 获取的动作参数 */ public void formatNearCode(ActionPerformer actionPerformer, ShortCutType shortCutType, ShortCutType unit) { Optional.of(actionPerformer.getDocument()).ifPresent(document -> Optional.of(actionPerformer.getCaretModel()).ifPresent(caretModel -> Optional.of(document.getText(new TextRange(document.getLineStartOffset(document.getLineNumber(caretModel.getOffset())), actionPerformer.getCaretModel().getOffset()))).ifPresent(lineContent -> Optional.of(lineContent.substring(getNearCode(lineContent) + 1, lineContent.length() - STYLE_TAG_TYPE.get(unit).toString().length()).trim()).ifPresent(content -> formatText(content, caretModel.getOffset() - content.length() - STYLE_TAG_TYPE.get(unit).toString().length(), caretModel.getOffset(), actionPerformer, shortCutType) ) ) ) ); } public void formatNearCode(ActionPerformer actionPerformer, ShortCutType shortCutType) { formatNearCode(actionPerformer, shortCutType, ShortCutType.PX); } /** * 取一个长度单位的起始index * * @param content 待处理文本 * @return 返回文本中包含的一个长度单位的起始index */ private int getNearCode(String content) { return NumberUtils.max(StringUtils.lastIndexOf(content, COLON_STRING), StringUtils.lastIndexOf(content, BLANK_STRING)); } /** * 转换选择的代码 * * @param actionPerformer 获取的动作参数 */ public void formatSelectCode(ActionPerformer actionPerformer, ShortCutType shortCutType) { // 光标选择的文字 Optional.ofNullable(actionPerformer.getSelectionModel().getSelectedText()).ifPresent(selectText -> Optional.of(selectText.indexOf(PX_STYLE_TAG)).filter(index -> index > -1).map(index -> { // 选择区域开始 Optional.of(actionPerformer.getSelectionModel().getSelectionStart()).ifPresent(start -> { // 选择区域结束 Optional.of(actionPerformer.getSelectionModel().getSelectionEnd()).ifPresent(end -> formatText(selectText.substring(0, index), start, end, actionPerformer, shortCutType) ); }); return index; }) ); } /** * 格式化指定的style文本 * * @param style style文本 * @param start 起始index * @param end 结束index * @param actionPerformer 动作参数 */ private void formatText(String style, int start, int end, ActionPerformer actionPerformer, ShortCutType shortCutType) { Optional.of(style).filter(FormatTools::isNumeric).ifPresent(text -> WriteCommandAction.runWriteCommandAction(actionPerformer.getProject(), () -> actionPerformer.getDocument().replaceString( start, end, getFormatText(style + PX_STYLE_TAG, shortCutType)) ) ); } }