All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.moon.poi.excel.table.StyleUtil Maven / Gradle / Ivy

package com.moon.poi.excel.table;

import com.moon.core.lang.ClassUtil;
import com.moon.core.lang.IntUtil;
import com.moon.core.lang.StringUtil;
import com.moon.core.util.CollectUtil;
import com.moon.core.util.ListUtil;
import com.moon.core.util.MapUtil;
import com.moon.core.util.SetUtil;
import com.moon.core.util.function.ShortFunction;
import com.moon.poi.excel.annotation.TableRecord;
import com.moon.poi.excel.annotation.style.DefinitionStyle;
import com.moon.poi.excel.annotation.style.StyleBuilder;
import org.apache.poi.ss.usermodel.*;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Stream;

import static com.moon.core.lang.StringUtil.defaultIfEmpty;
import static com.moon.core.lang.StringUtil.isNotEmpty;

/**
 * @author moonsky
 */
final class StyleUtil {

    private final static String SEPARATOR = ":";

    final static Map defaultMap = Collections.emptyMap();

    final static String EMPTY = "";

    private static String scoped(Object type) {
        return IntUtil.toCompressionString(System.identityHashCode(type), Integer.MAX_VALUE);
    }

    private static String defaultClassnameForEmpty(
        String prefix, Attribute attribute, Function scopedPrefix
    ) {
        return classnameOf(prefix, classnameOf(scopedPrefix.apply(attribute), attribute.getName()));
    }

    private static String getClassnameIfAbsent(
        List styles, String prefix, Attribute attribute, Function scopedPrefix
    ) {
        if (ListUtil.isNotEmpty(styles)) {
            if (styles.size() == 1) {
                DefinitionStyle style = styles.get(0);
                String classname = style.classname();
                if (StringUtil.isEmpty(classname)) {
                    return defaultClassnameForEmpty(prefix, attribute, scopedPrefix);
                } else {
                    return classnameOf(prefix, classname);
                }
            }
            for (DefinitionStyle style : styles) {
                if (StringUtil.isEmpty(style.classname())) {
                    return defaultClassnameForEmpty(prefix, attribute, scopedPrefix);
                }
            }
        }
        return null;
    }

    /**
     * 如果字段上存在主动命名的 style,就直接使用主动命名
     * 

* 如果不存在主动命名的 style,首先检查当前字段或 getter 方法是否有注解{@link DefinitionStyle} * 如果有且仅有一个,无论该{@link DefinitionStyle}是否定义了{@code classname},都直接使用该样式, * 如果有多个,则使用其中没有设置{@code classname}的{@link DefinitionStyle} * 如果一个也没有,则检查是否存在全局默认定义,即在类上是否有定义没有设置{@code classname}的{@link DefinitionStyle}有就使用 * 否则返回 null * * @param config * * @return */ static String getTableColClassname(AttrConfig config) { Attribute attribute = config.getAttribute(); if (!attribute.isAnnotated()) { return null; } String prefix = scoped(config.getTargetClass()); String style = attribute.getTableColumn().style(); // TableHeadClassname styleForCell = attribute.getAnnotation(TableHeadClassname.class); if (StringUtil.isEmpty(style)) { String classname = getClassnameIfAbsent(attribute.getDefinitionStylesOnMethod(), prefix, attribute, attr -> scoped(attr.getMemberMethod())); classname = classname == null ? getClassnameIfAbsent(attribute.getDefinitionStylesOnField(), prefix, attribute, attr -> scoped(attr.getMemberField())) : classname; if (classname == null) { return config.isDefinedDefaultStyle() ? classnameOfEmpty(config.getTargetClass()) : null; } return classname; } else { return classnameOf(prefix, style); } } static String classnameOfEmpty(Class prefix) { return classnameOf(prefix, EMPTY); } private static String classnameOf(Class prefix, String classname) { return classnameOf(scoped(prefix), classname); } private static String classnameOf(String prefix, String classname) { return StringUtil.concat(prefix, SEPARATOR, classname); } private static void collectImports( Set> rang, final Map builderMap, final String prefix, final TableRecord record ) { if (record == null) { return; } Class[] imports = record.importStyles(); for (final Class importClass : imports) { TableRecord importRecord = null; // 递归深层引入样式 if (rang.contains(importClass)) { continue; } else { rang.add(importClass); importRecord = importClass.getAnnotation(TableRecord.class); collectImports(rang, builderMap, prefix, importRecord); } // 定义在类上的所有样式 parseStyleOnClass(builderMap, importClass, prefix, importRecord); // 定义在 getter 方法上的命名样式 try { BeanInfo info = Introspector.getBeanInfo(importClass); PropertyDescriptor[] descriptors = info.getPropertyDescriptors(); for (PropertyDescriptor descriptor : descriptors) { Method getter = descriptor.getReadMethod(); if (getter != null) { DefinitionStyle[] styles = getter.getAnnotationsByType(DefinitionStyle.class); // 只保留命名样式 Stream stream = Arrays.stream(styles) .filter(style -> isNotEmpty(style.classname())); parseStyles(builderMap, prefix, null, ListUtil.newList(stream)); } } } catch (IntrospectionException e) { // ignore } // 定义在字段上的命名样式 Class thisClass = importClass; while (thisClass != Object.class) { Field[] fields = thisClass.getDeclaredFields(); for (Field field : fields) { DefinitionStyle[] styles = field.getAnnotationsByType(DefinitionStyle.class); // 只保留命名样式 Stream stream = Arrays.stream(styles) .filter(style -> isNotEmpty(style.classname())); parseStyles(builderMap, prefix, null, ListUtil.newList(stream)); } thisClass = thisClass.getSuperclass(); } } } private static void parseStyleOnClass( Map builderMap, Class type, String prefix, TableRecord record ) { List classStyles = ListUtil.newList(type.getAnnotationsByType(DefinitionStyle.class)); parseStyles(builderMap, prefix, EMPTY, classStyles); } static Map collect(Class type, List attrs) { Map builderMap = MapUtil.newMap(); final String prefix = scoped(type); // 首先引入外部定义样式 final TableRecord tableRecord = type.getAnnotation(TableRecord.class); collectImports(SetUtil.newSet(type), builderMap, prefix, tableRecord); // 解析类定义样式 parseStyleOnClass(builderMap, type, prefix, tableRecord); for (Attribute attr : attrs) { // getter 方法上的默认样式覆盖字段上的默认样式 List stylesOnMethod = attr.getDefinitionStylesOnMethod(); if (CollectUtil.isNotEmpty(stylesOnMethod)) { String name = classnameOf(scoped(attr.getMemberMethod()), attr.getName()); parseStyles(builderMap, prefix, name, stylesOnMethod); } // 解析字段上的样式 List stylesOnField = attr.getDefinitionStylesOnField(); if (CollectUtil.isNotEmpty(stylesOnField)) { String name = classnameOf(scoped(attr.getMemberField()), attr.getName()); parseStyles(builderMap, prefix, name, stylesOnField); } } return MapUtil.isEmpty(builderMap) ? defaultMap : builderMap; } private static void parseStyles( Map builderMap, String scope, String defaultClassnameForEmpty, Collection styles ) { parseStyles(builderMap, scope, defaultClassnameForEmpty, ListUtil.toArray(styles, DefinitionStyle[]::new)); } private static void parseStyles( Map builderMap, String scope, String defaultClassnameForEmpty, DefinitionStyle... styles ) { int length = styles == null ? 0 : styles.length; for (int i = 0; i < length; i++) { DefinitionStyle style = styles[i]; String classname = classnameOf(scope, defaultIfEmpty(style.classname(), defaultClassnameForEmpty)); builderMap.put(classname, toBuilder(style)); } } private static StyleBuilder toBuilder(DefinitionStyle style) { Class type = style.createBy(); if (type != StyleBuilder.class) { ParserUtil.checkValidImplClass(type, StyleBuilder.class); return ClassUtil.newInstance(type); } List consumers = new ArrayList<>(); consumers.add(new Align(style.align())); consumers.add(new VerticalAlign(style.verticalAlign())); consumers.add(new FillPattern(style.fillPattern())); consumers.add(style.wrapText() ? WrapText.WRAP : WrapText.UNWRAP); addIfValidColor(consumers, style.backgroundColor(), FillBackgroundColor::new); addIfValidColor(consumers, style.foregroundColor(), FillForegroundColor::new); short[] colors = style.borderColor(); switch (colors.length) { default: case 4: addIfValidColor(consumers, colors[3], BorderColorLeft::new); case 3: addIfValidColor(consumers, colors[2], BorderColorBottom::new); case 2: addIfValidColor(consumers, colors[1], BorderColorRight::new); case 1: addIfValidColor(consumers, colors[0], BorderColorTop::new); case 0: break; } BorderStyle[] styles = style.border(); switch (styles.length) { default: case 4: consumers.add(new BorderStyleLeft(styles[3])); case 3: consumers.add(new BorderStyleBottom(styles[2])); case 2: consumers.add(new BorderStyleRight(styles[1])); case 1: consumers.add(new BorderStyleTop(styles[0])); case 0: break; } return new TableStyleBuilder(ListUtil.toArray(consumers, Consumer[]::new)); } private static class TableStyleBuilder implements StyleBuilder { private final Consumer[] setters; private TableStyleBuilder(Consumer[] setters) { this.setters = setters; } @Override public void accept(CellStyle style) { Consumer[] setters = this.setters; for (Consumer setter : setters) { setter.accept(style); } } } private abstract static class BorderStyleBase implements Consumer { protected final BorderStyle style; protected BorderStyleBase(BorderStyle style) {this.style = style;} } private final static class BorderStyleTop extends BorderStyleBase { protected BorderStyleTop(BorderStyle style) { super(style); } @Override public void accept(CellStyle cellStyle) { cellStyle.setBorderTop(style); } } private final static class BorderStyleRight extends BorderStyleBase { protected BorderStyleRight(BorderStyle style) { super(style); } @Override public void accept(CellStyle cellStyle) { cellStyle.setBorderRight(style); } } private final static class BorderStyleBottom extends BorderStyleBase { protected BorderStyleBottom(BorderStyle style) { super(style); } @Override public void accept(CellStyle cellStyle) { cellStyle.setBorderBottom(style); } } private final static class BorderStyleLeft extends BorderStyleBase { protected BorderStyleLeft(BorderStyle style) { super(style); } @Override public void accept(CellStyle cellStyle) { cellStyle.setBorderLeft(style); } } private abstract static class BorderColorBase implements Consumer { protected final short color; protected BorderColorBase(short color) {this.color = color;} } private final static class BorderColorTop extends BorderColorBase { protected BorderColorTop(short width) { super(width); } @Override public void accept(CellStyle cellStyle) { cellStyle.setTopBorderColor(color); } } private final static class BorderColorRight extends BorderColorBase { protected BorderColorRight(short width) { super(width); } @Override public void accept(CellStyle cellStyle) { cellStyle.setRightBorderColor(color); } } private final static class BorderColorBottom extends BorderColorBase { protected BorderColorBottom(short width) { super(width); } @Override public void accept(CellStyle cellStyle) { cellStyle.setBottomBorderColor(color); } } private final static class BorderColorLeft extends BorderColorBase { protected BorderColorLeft(short width) { super(width); } @Override public void accept(CellStyle cellStyle) { cellStyle.setLeftBorderColor(color); } } private enum WrapText implements Consumer { WRAP(true), UNWRAP(false); private final boolean wrap; WrapText(boolean wrap) { this.wrap = wrap; } @Override public void accept(CellStyle style) { style.setWrapText(wrap); } } private static class Align implements Consumer { private final HorizontalAlignment align; private Align(HorizontalAlignment align) {this.align = align;} @Override public void accept(CellStyle style) { style.setAlignment(align); } } private static class VerticalAlign implements Consumer { private final VerticalAlignment alignment; private VerticalAlign(VerticalAlignment alignment) {this.alignment = alignment;} @Override public void accept(CellStyle style) { style.setVerticalAlignment(alignment); } } private static class FillPattern implements Consumer { private final FillPatternType type; private FillPattern(FillPatternType pattern) {this.type = pattern;} @Override public void accept(CellStyle style) { style.setFillPattern(type); } } private static class FillBackgroundColor implements Consumer { private final short color; private FillBackgroundColor(short color) {this.color = color;} @Override public void accept(CellStyle style) { style.setFillBackgroundColor(color); } } private static class FillForegroundColor implements Consumer { private final short color; private FillForegroundColor(short color) {this.color = color;} @Override public void accept(CellStyle style) { style.setFillForegroundColor(color); } } private static void addIfValidColor(List consumers, short color, ShortFunction builder) { if (color > -1) { consumers.add(builder.apply(color)); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy