diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..4f2ff52 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,138 @@ +site_name: uno文档 +repo_name: uno +repo_url: https://github.com/ClearXs/uno +site_author: J.x (jiangw1027@gmail.com) +copyright: Copyright © 2023 - 2024 ClearX + +nav: + - README.md + - core: core.md + - data: data.md + - rule: rule.md + - web: web.md + - test: test.md + - gis: gis.md + - auto: auto.md + - bom: bom.md + - plugins: plugins.md + - components: + - http: components/http.md + - kafka: components/kafka.md + - media: components/media.md + - netty: components/netty.md + - sequential: components/sequential.md + - websocket: components/websocket.md + - starter: starter.md + +theme: + # https://squidfunk.github.io/mkdocs-material/creating-your-site/#configuration + name: material + # https://squidfunk.github.io/mkdocs-material/setup/changing-the-colors/ + palette: + - scheme: default + toggle: + icon: material/brightness-7 + primary: indigo + accent: light blue + - scheme: slate + toggle: + icon: material/brightness-4 + primary: indigo + accent: grey + # https://squidfunk.github.io/mkdocs-material/setup/changing-the-fonts/ + font: + text: Nunito Sans + code: Roboto Mono + logo: favicon.png + favicon: favicon.png + features: + # Navigation + # https://squidfunk.github.io/mkdocs-material/setup/setting-up-navigation/ + - navigation.instant # some features may not work properly with XHR + - navigation.tracking # the URL changes with the active anchor + - navigation.instant.prefetch + - navigation.tabs # first top-level folders are shown as tabs + - navigation.tabs.sticky # tabs always show + - navigation.prune + - navigation.sections # second-level folders are expanded + - navigation.expand # all folders are expanded + - navigation.indexes # link an index page to its parent folder + - navigation.top # show the back-to-top button + - toc.integrate # show the table of contents in the navigation panel + # Search + # https://squidfunk.github.io/mkdocs-material/setup/setting-up-site-search + - search.suggest # display the likeliest completion for the last word + - search.highlight # highlight all occurrences + - search.share # show a share button of the current search + # Header + # https://squidfunk.github.io/mkdocs-material/setup/setting-up-the-header/ + - header.autohide # hide the post title when scroll down + # Content + # https://squidfunk.github.io/mkdocs-material/reference/code-blocks/#adding-annotations + - content.code.annotate # add comment to code blocks + - content.tabs.link # link tabs with the same label + - content.tooltips + +# +# Plugins +# +plugins: + - tags + - search: # must be included at the first place + lang: + - en + - zh + separator: '[\s\u200b\-]' + - offline +# +# Extensions +# +markdown_extensions: + - abbr # add a small tooltip to elements + - admonition # add call-out blocks + - attr_list # allow to add HTML attributes and CSS classes + - def_list # add description lists + - footnotes # define inline footnotes + - meta # attach arbitrary key-value pairs to a document + - md_in_html # allow to write Markdown inside HTML elements + - tables # create tables in Markdown + - toc: # generate a table of contents from documents + permalink: '#' # anchor + toc_depth: 4 # header 1 to header 4 + slugify: !!python/name:pymdownx.slugs.uslugify # convert title to html-compatible text + - sane_lists # make better lists + - smarty: # convert some special characters + smart_angled_quotes: true + - pymdownx.highlight: + anchor_linenums: true + # - pymdownx.betterem: # improve the detection of Markup to emphasize text in Markdown + # smart_enable: all + - pymdownx.caret # define superscript + - pymdownx.mark # highlight text + - pymdownx.tilde # define subscript + - pymdownx.critic # track changes + - pymdownx.details # add collapsible call-outs + - pymdownx.emoji: # add inlines bundled and custom icons and emojis + emoji_index: !!python/name:materialx.emoji.twemoji + emoji_generator: !!python/name:materialx.emoji.to_svg + - pymdownx.superfences # define code blocks and nesting of code + - pymdownx.highlight: # highlight of code blocks + linenums_style: pymdownx-inline + # anchor_linenums: true # create anchor link on each line of code + - pymdownx.inlinehilite # highlight inline code blocks + - pymdownx.smartsymbols # convert some sequences of characters into their corresponding symbols + - pymdownx.snippets: # embed content from arbitrary files into a document + check_paths: true + - pymdownx.tabbed: # group related content and code blocks under accessible tabs + alternate_style: true + - pymdownx.tasklist: # define list of task with checkbox + custom_checkbox: true + - pymdownx.escapeall: + hardbreak: true # use \ to create new line + nbsp: true # use \ to create   + # - pymdownx.keys # syntax ++ctrl+c++ does not look clear in MD document + - pymdownx.progressbar +extra: + version: + provider: mike + disqus: "vuquangtrong-github-io" \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..b7ac90b --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "name": "turbo-docs", + "version": "1.0.0", + "description": "turbo documentation", + "private": true, + "scripts": { + "dev": "mkdocs serve", + "build": "mkdocs build -d public" + } +} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e8d3d95 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +mkdocs +mkdocs-material +mkdocs-roamlinks-plugin \ No newline at end of file diff --git a/uno-core/src/main/java/cc/allio/uno/core/datastructure/tree/TreeSupport.java b/uno-core/src/main/java/cc/allio/uno/core/datastructure/tree/TreeSupport.java index d03f035..6c765da 100644 --- a/uno-core/src/main/java/cc/allio/uno/core/datastructure/tree/TreeSupport.java +++ b/uno-core/src/main/java/cc/allio/uno/core/datastructure/tree/TreeSupport.java @@ -1,5 +1,7 @@ package cc.allio.uno.core.datastructure.tree; +import cc.allio.uno.core.bean.BeanWrapper; +import cc.allio.uno.core.function.lambda.MethodFunction; import cc.allio.uno.core.util.CollectionUtils; import com.google.common.collect.Lists; @@ -7,6 +9,7 @@ import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * 提供树相关操作 @@ -17,9 +20,6 @@ */ public final class TreeSupport { - private TreeSupport() { - } - /** * @see #treeify(List, Function) */ @@ -127,7 +127,7 @@ public static > List expand(List forest) * @param 继承于{@link Element}的泛型 * @return expand */ - public static synchronized > List expand(List forest, Function expandFunc, Comparator comparator) { + public static > List expand(List forest, Function expandFunc, Comparator comparator) { List expands = Lists.newArrayList(); try { Element.ROOT_SENTINEL.setChildren(Lists.newArrayList(forest)); @@ -145,4 +145,34 @@ public static synchronized > List expa } return expands; } + + /** + * with any 'forest' type expand collection type R data + * + * @param forest the forest about description tree data structure + * @param childrenFunc the description children function + * @param transfer transfer type T to type R + * @return the collection of type R + * @param the forest type T + * @param the expand type R + * @see #doExpandFn(Collection, MethodFunction, Function) + */ + public static Collection withExpandFn(Collection forest, MethodFunction> childrenFunc, Function transfer) { + return doExpandFn(forest, childrenFunc, transfer).toList(); + } + + /** + * through use java stream feature, do expand + */ + static Stream doExpandFn(Collection forest, MethodFunction> childrenFunc, Function transfer) { + return forest.stream() + .flatMap(element -> { + String fieldName = childrenFunc.getFieldName(); + Collection children = BeanWrapper.getValue(element, fieldName, Collection.class); + if (CollectionUtils.isNotEmpty(children)) { + return doExpandFn(forest, childrenFunc, transfer); + } + return Stream.of(transfer.apply(element)); + }); + } } diff --git a/uno-core/src/main/java/cc/allio/uno/core/function/lambda/MethodFunction.java b/uno-core/src/main/java/cc/allio/uno/core/function/lambda/MethodFunction.java index e0d2c0f..d4c2851 100644 --- a/uno-core/src/main/java/cc/allio/uno/core/function/lambda/MethodFunction.java +++ b/uno-core/src/main/java/cc/allio/uno/core/function/lambda/MethodFunction.java @@ -12,9 +12,9 @@ * @since 1.1.7 */ @FunctionalInterface -public interface MethodFunction extends Serializable, LambdaMethod { +public interface MethodFunction extends Serializable, LambdaMethod { - K apply(T t); + R apply(T t); /** * 获取参数值的类型 @@ -26,7 +26,7 @@ default Class getParameterType() { /** * 获取返回值的类型 */ - default Class getReturnType() { - return (Class) ReflectTools.getGenericType(this, MethodFunction.class, 1); + default Class getReturnType() { + return (Class) ReflectTools.getGenericType(this, MethodFunction.class, 1); } } diff --git a/uno-core/src/main/java/cc/allio/uno/core/type/ShortTypeOperator.java b/uno-core/src/main/java/cc/allio/uno/core/type/ShortTypeOperator.java index 7ff6a6f..6bfbd58 100644 --- a/uno-core/src/main/java/cc/allio/uno/core/type/ShortTypeOperator.java +++ b/uno-core/src/main/java/cc/allio/uno/core/type/ShortTypeOperator.java @@ -1,7 +1,5 @@ package cc.allio.uno.core.type; -import cc.allio.uno.core.StringPool; - /** * Short类型的转换器。可能抛出NumberFormatException异常 * diff --git a/uno-core/src/main/java/cc/allio/uno/core/util/DateUtil.java b/uno-core/src/main/java/cc/allio/uno/core/util/DateUtil.java index dd61cc3..ca0da2e 100644 --- a/uno-core/src/main/java/cc/allio/uno/core/util/DateUtil.java +++ b/uno-core/src/main/java/cc/allio/uno/core/util/DateUtil.java @@ -5,7 +5,9 @@ import org.springframework.util.Assert; import reactor.core.publisher.Flux; +import java.text.DateFormat; import java.text.ParseException; +import java.text.SimpleDateFormat; import java.time.*; import java.time.format.DateTimeFormatter; import java.time.temporal.Temporal; @@ -55,6 +57,10 @@ public class DateUtil { public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.PATTERN_DATE); public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern(DateUtil.PATTERN_TIME); + static final ThreadLocal DATE_FORMAT_SHORT_DATE_LOCAL = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMMdd")); + static final ThreadLocal DATE_FORMAT_SHORT_MONTH_LOCAL = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyyMM")); + static final ThreadLocal DATE_FORMAT_YEAR_LOCAL = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy")); + /** * 获取当前日期 * @@ -888,4 +894,41 @@ public static boolean isBetween(Date nowTime, Date startTime, Date endTime) { public static Date getEpochTime() { return new Date(0); } + + /** + * get current datetime format to 'yyyyMMdd' + * + * @return the format 'yyyyMMdd' time + */ + public static String getNowYMD() { + return DATE_FORMAT_SHORT_DATE_LOCAL.get().format(new Date()); + } + + /** + * get current datetime format to 'yyyyMM' + * + * @return the format 'yyyyMM' time + */ + public static String getNowYM() { + return DATE_FORMAT_SHORT_MONTH_LOCAL.get().format(new Date()); + } + + /** + * get current datetime format to 'yyyy' + * + * @return the format 'yyyy' time + */ + public static String getNowY() { + return DATE_FORMAT_YEAR_LOCAL.get().format(new Date()); + } + + /** + * get current datetime specifies pattern format time + * + * @param pattern the format pattern + * @return format time + */ + public static String getNowPart(String pattern) { + return format(now(), pattern); + } } diff --git a/uno-core/src/main/java/cc/allio/uno/core/util/template/TemplateContext.java b/uno-core/src/main/java/cc/allio/uno/core/util/template/TemplateContext.java index 345d2e8..3249b86 100644 --- a/uno-core/src/main/java/cc/allio/uno/core/util/template/TemplateContext.java +++ b/uno-core/src/main/java/cc/allio/uno/core/util/template/TemplateContext.java @@ -2,10 +2,7 @@ import cc.allio.uno.core.api.OptionalContext; import cc.allio.uno.core.type.Types; -import cc.allio.uno.core.util.BeanUtils; -import cc.allio.uno.core.util.ClassUtils; -import cc.allio.uno.core.util.DateUtil; -import cc.allio.uno.core.util.JsonUtils; +import cc.allio.uno.core.util.*; import com.google.common.collect.Maps; import lombok.Getter; import org.springframework.context.ApplicationContext; @@ -41,6 +38,7 @@ public class TemplateContext implements OptionalContext { private static final String JSON_UTILITY_NAME = "json"; private static final String BEAN_UTILITY_NAME = "bean"; private static final String CLASS_UTILITY_NAME = "class"; + private static final String STRING_UTILITY_NAME = "string"; public TemplateContext() { this.vars = Maps.newConcurrentMap(); @@ -58,6 +56,7 @@ void initial() { addImport(JSON_UTILITY_NAME, JsonUtils.class); addImport(BEAN_UTILITY_NAME, BeanUtils.class); addImport(CLASS_UTILITY_NAME, ClassUtils.class); + addImport(STRING_UTILITY_NAME, StringUtils.class); } @Override diff --git a/uno-core/src/main/java/cc/allio/uno/core/util/template/mvel/CharsetOutputStream.java b/uno-core/src/main/java/cc/allio/uno/core/util/template/mvel/CharsetOutputStream.java new file mode 100644 index 0000000..e2a55d9 --- /dev/null +++ b/uno-core/src/main/java/cc/allio/uno/core/util/template/mvel/CharsetOutputStream.java @@ -0,0 +1,46 @@ +package cc.allio.uno.core.util.template.mvel; + +import org.mvel2.templates.util.TemplateOutputStream; + +import java.io.IOException; +import java.io.OutputStreamWriter; + +/** + * solution messy code + * + * @author j.x + * @date 2024/5/29 19:30 + * @since 1.1.9 + */ +public class CharsetOutputStream implements TemplateOutputStream { + + OutputStreamWriter writer; + + public CharsetOutputStream(OutputStreamWriter writer) { + this.writer = writer; + } + + @Override + public TemplateOutputStream append(char[] chars) { + try { + for (char c : chars) { + writer.write(c); + } + return this; + } catch (IOException ex) { + throw new RuntimeException("failed to write to stream", ex); + } + } + + @Override + public TemplateOutputStream append(CharSequence c) { + try { + for (int i = 0; i < c.length(); ++i) { + this.writer.write(c.charAt(i)); + } + return this; + } catch (IOException ex) { + throw new RuntimeException("failed to write to stream", ex); + } + } +} diff --git a/uno-core/src/main/java/cc/allio/uno/core/util/template/mvel/MVELExpressionTemplate.java b/uno-core/src/main/java/cc/allio/uno/core/util/template/mvel/MVELExpressionTemplate.java index ea6b989..735b953 100644 --- a/uno-core/src/main/java/cc/allio/uno/core/util/template/mvel/MVELExpressionTemplate.java +++ b/uno-core/src/main/java/cc/allio/uno/core/util/template/mvel/MVELExpressionTemplate.java @@ -1,14 +1,17 @@ package cc.allio.uno.core.util.template.mvel; +import cc.allio.uno.core.util.IoUtils; import cc.allio.uno.core.util.template.ExpressionTemplate; import cc.allio.uno.core.util.template.TemplateContext; +import io.protostuff.Input; import lombok.extern.slf4j.Slf4j; import org.mvel2.ParserContext; import org.mvel2.templates.CompiledTemplate; import org.mvel2.templates.TemplateCompiler; import org.mvel2.templates.TemplateRuntime; -import java.io.ByteArrayOutputStream; +import java.io.*; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Map; @@ -36,18 +39,26 @@ public String parseTemplate(String template, TemplateContext context) { for (Map.Entry importEntry : imports.entrySet()) { parserContext.addImport(importEntry.getKey(), importEntry.getValue()); } + CompiledTemplate compiledTemplate = TemplateCompiler.compileTemplate(template, parserContext); // 2. execute parse template ByteArrayOutputStream out = new ByteArrayOutputStream(); + OutputStreamWriter writer = new OutputStreamWriter(out, StandardCharsets.UTF_8); // use customize VariableResolverFactory TemplateContextVariableResolverFactory variableResolverFactory = new TemplateContextVariableResolverFactory(context); try { - TemplateRuntime.execute(compiledTemplate, null, variableResolverFactory, out); + TemplateRuntime.execute(compiledTemplate, null, variableResolverFactory, null, new CharsetOutputStream(writer)); } catch (Throwable ex) { log.error("Failed to mvel parse template {}", template, ex); } + try { + writer.flush(); + } catch (IOException ex) { + log.error("Failed to mvel parse template {}", template, ex); + } return out.toString(StandardCharsets.UTF_8); } + } diff --git a/uno-core/src/test/java/cc/allio/uno/core/util/template/mvel/MVELExpressionTemplateTest.java b/uno-core/src/test/java/cc/allio/uno/core/util/template/mvel/MVELExpressionTemplateTest.java index e8be486..a5734a7 100644 --- a/uno-core/src/test/java/cc/allio/uno/core/util/template/mvel/MVELExpressionTemplateTest.java +++ b/uno-core/src/test/java/cc/allio/uno/core/util/template/mvel/MVELExpressionTemplateTest.java @@ -1,7 +1,6 @@ package cc.allio.uno.core.util.template.mvel; import cc.allio.uno.core.BaseTestCase; -import cc.allio.uno.core.util.DateUtil; import cc.allio.uno.core.util.template.ExpressionTemplate; import cc.allio.uno.core.util.template.TemplateContext; import lombok.Data; @@ -26,15 +25,34 @@ void testOrbTemplate() { assertEquals("Hello, my name is 1 email 2", resolved); } + @Test + void testMapParams() { + String template = "Hello, @{name}"; + MVELExpressionTemplate mvelExpressionTemplate = ExpressionTemplate.createMVEL(); + TemplateContext templateContext = new TemplateContext(); + templateContext.putAttribute("name", "测试"); + String out = mvelExpressionTemplate.parseTemplate(template, templateContext); + assertEquals("Hello, 测试", out); + } + @Test void testUtilityMethod() { - String template = "time is @{date.formatNow()}"; + String template1 = "time is @{date.formatNow()}"; MVELExpressionTemplate mvelExpressionTemplate = ExpressionTemplate.createMVEL(); TemplateContext templateContext = new TemplateContext(); - String resolved = mvelExpressionTemplate.parseTemplate(template, templateContext); + String resolved = mvelExpressionTemplate.parseTemplate(template1, templateContext); System.out.println(resolved); + + String template2 = "@{string.camelToUnderline(AB)}"; + Person person = new Person(); + person.setName("ClickOver"); + templateContext.addImport(Person.class); + templateContext.putAttribute("person", person); + String r2 = mvelExpressionTemplate.parseTemplate(template2, templateContext); + System.out.println(r2); } + @Data public static class Person {