Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add word docx export #686

Merged
merged 5 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 71 additions & 18 deletions src/main/java/com/ly/doc/builder/DocBuilderTemplate.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,16 +84,28 @@ public ApiAllData getApiData(ApiConfig config, JavaProjectBuilder javaProjectBui
public void buildApiDoc(List<ApiDoc> apiDocList, ApiConfig config, String template, String fileExtension) {
FileUtil.mkdirs(config.getOutPath());
for (ApiDoc doc : apiDocList) {
Template mapper = BeetlTemplateUtil.getByName(template);
mapper.binding(TemplateVariable.DESC.getVariable(), doc.getDesc());
mapper.binding(TemplateVariable.NAME.getVariable(), doc.getName());
mapper.binding(TemplateVariable.LIST.getVariable(), doc.getList());
mapper.binding(TemplateVariable.REQUEST_EXAMPLE.getVariable(), config.isRequestExample());
mapper.binding(TemplateVariable.RESPONSE_EXAMPLE.getVariable(), config.isResponseExample());
Template mapper = buildApiDocTemplate(doc, config, template);
FileUtil.nioWriteFile(mapper.render(), config.getOutPath() + DocGlobalConstants.FILE_SEPARATOR + doc.getName() + fileExtension);
}
}

/**
* build api doc template
*
* @param doc api doc
* @param config api config
* @param template template
*/
public Template buildApiDocTemplate(ApiDoc doc, ApiConfig config, String template) {
Template mapper = BeetlTemplateUtil.getByName(template);
mapper.binding(TemplateVariable.DESC.getVariable(), doc.getDesc());
mapper.binding(TemplateVariable.NAME.getVariable(), doc.getName());
mapper.binding(TemplateVariable.LIST.getVariable(), doc.getList());
mapper.binding(TemplateVariable.REQUEST_EXAMPLE.getVariable(), config.isRequestExample());
mapper.binding(TemplateVariable.RESPONSE_EXAMPLE.getVariable(), config.isResponseExample());
return mapper;
}

/**
* Merge all api doc into one document
*
Expand All @@ -104,26 +116,24 @@ public void buildApiDoc(List<ApiDoc> apiDocList, ApiConfig config, String templa
* @param outPutFileName output file
*/
public void buildAllInOne(List<ApiDoc> apiDocList, ApiConfig config, JavaProjectBuilder javaProjectBuilder,
String template, String outPutFileName) {
String template, String outPutFileName) {
buildDoc(apiDocList, config, javaProjectBuilder, template, outPutFileName, null, null);
}


/**
* Merge all api doc into one document
* get render doc template
*
* @param apiDocList list data of Api doc
* @param config api config
* @param javaProjectBuilder JavaProjectBuilder
* @param template template
* @param outPutFileName output file
* @param apiDoc apiDoc
* @param index index html
*/
public void buildDoc(List<ApiDoc> apiDocList, ApiConfig config, JavaProjectBuilder javaProjectBuilder,
String template, String outPutFileName, ApiDoc apiDoc, String index) {
String outPath = config.getOutPath();
public Template buildAllRenderDocTemplate(List<ApiDoc> apiDocList, ApiConfig config, JavaProjectBuilder javaProjectBuilder,
String template, ApiDoc apiDoc, String index) {
String strTime = DateTimeUtil.long2Str(now, DateTimeUtil.DATE_FORMAT_SECOND);
FileUtil.mkdirs(outPath);
List<ApiErrorCode> errorCodeList = DocUtil.errorCodeDictToList(config, javaProjectBuilder);
Template tpl = BeetlTemplateUtil.getByName(template);
String style = config.getStyle();
Expand Down Expand Up @@ -151,7 +161,7 @@ public void buildDoc(List<ApiDoc> apiDocList, ApiConfig config, JavaProjectBuild
boolean onlyHasDefaultGroup = apiDocList.stream().allMatch(doc -> Objects.equals(TornaConstants.DEFAULT_GROUP_CODE, doc.getGroup()));
int codeIndex = 0;
if (onlyHasDefaultGroup) {
if (apiDocList.size() > 0) {
if (!apiDocList.isEmpty()) {
codeIndex = apiDocList.get(0).getChildrenApiDocs().size();
}
} else {
Expand All @@ -172,6 +182,25 @@ public void buildDoc(List<ApiDoc> apiDocList, ApiConfig config, JavaProjectBuild
tpl.binding(TemplateVariable.ORDER.getVariable(), apiDoc.order);
tpl.binding(TemplateVariable.LIST.getVariable(), apiDoc.getList());//类名
}
return tpl;
}

/**
* Merge all api doc into one document
*
* @param apiDocList list data of Api doc
* @param config api config
* @param javaProjectBuilder JavaProjectBuilder
* @param template template
* @param outPutFileName output file
* @param apiDoc apiDoc
* @param index index html
*/
public void buildDoc(List<ApiDoc> apiDocList, ApiConfig config, JavaProjectBuilder javaProjectBuilder,
String template, String outPutFileName, ApiDoc apiDoc, String index) {
String outPath = config.getOutPath();
FileUtil.mkdirs(outPath);
Template tpl = buildAllRenderDocTemplate(apiDocList, config, javaProjectBuilder, template, apiDoc, index);
FileUtil.nioWriteFile(tpl.render(), outPath + DocGlobalConstants.FILE_SEPARATOR + outPutFileName);
}

Expand Down Expand Up @@ -266,13 +295,25 @@ public void buildSearchJs(ApiConfig config, JavaProjectBuilder javaProjectBuilde
* @param javaProjectBuilder javaProjectBuilder
*/
public void buildErrorCodeDoc(ApiConfig config, String template, String outPutFileName, JavaProjectBuilder javaProjectBuilder) {
Template tpl = buildErrorCodeDocTemplate(config, template, javaProjectBuilder);
FileUtil.nioWriteFile(tpl.render(), config.getOutPath() + DocGlobalConstants.FILE_SEPARATOR + outPutFileName);
}

/**
* build errorCode adoc template
*
* @param config api config
* @param template template
* @param javaProjectBuilder javaProjectBuilder
*/
public Template buildErrorCodeDocTemplate(ApiConfig config, String template, JavaProjectBuilder javaProjectBuilder) {
List<ApiErrorCode> errorCodeList = DocUtil.errorCodeDictToList(config, javaProjectBuilder);
String strTime = DateTimeUtil.long2Str(now, DateTimeUtil.DATE_FORMAT_SECOND);
Template tpl = BeetlTemplateUtil.getByName(template);
setCssCDN(config, tpl);
tpl.binding(TemplateVariable.CREATE_TIME.getVariable(), strTime);
tpl.binding(TemplateVariable.LIST.getVariable(), errorCodeList);
FileUtil.nioWriteFile(tpl.render(), config.getOutPath() + DocGlobalConstants.FILE_SEPARATOR + outPutFileName);
return tpl;
}

/**
Expand All @@ -286,7 +327,7 @@ public void buildErrorCodeDoc(ApiConfig config, String template, String outPutFi
* @param indexAlias index alias
*/
public void buildErrorCodeDoc(ApiConfig config, JavaProjectBuilder javaProjectBuilder,
List<ApiDoc> apiDocList, String template, String outPutFileName, String indexAlias) {
List<ApiDoc> apiDocList, String template, String outPutFileName, String indexAlias) {
List<ApiErrorCode> errorCodeList = DocUtil.errorCodeDictToList(config, javaProjectBuilder);
String strTime = DateTimeUtil.long2Str(now, DateTimeUtil.DATE_FORMAT_SECOND);
Template errorTemplate = BeetlTemplateUtil.getByName(template);
Expand Down Expand Up @@ -325,7 +366,7 @@ public void buildErrorCodeDoc(ApiConfig config, JavaProjectBuilder javaProjectBu
* @param indexAlias index alias
*/
public void buildDirectoryDataDoc(ApiConfig config, JavaProjectBuilder javaProjectBuilder, List<ApiDoc> apiDocList,
String template, String outPutFileName, String indexAlias) {
String template, String outPutFileName, String indexAlias) {
List<ApiDocDict> directoryList = DocUtil.buildDictionary(config, javaProjectBuilder);
Template mapper = BeetlTemplateUtil.getByName(template);
String strTime = DateTimeUtil.long2Str(now, DateTimeUtil.DATE_FORMAT_SECOND);
Expand Down Expand Up @@ -367,6 +408,18 @@ public void buildDirectoryDataDoc(ApiConfig config, JavaProjectBuilder javaProje
* @param outPutFileName output file
*/
public void buildDirectoryDataDoc(ApiConfig config, JavaProjectBuilder javaProjectBuilder, String template, String outPutFileName) {
Template mapper = buildDirectoryDataDocTemplate(config, javaProjectBuilder, template);
FileUtil.nioWriteFile(mapper.render(), config.getOutPath() + DocGlobalConstants.FILE_SEPARATOR + outPutFileName);
}

/**
* build common_data doc Template
*
* @param config api config
* @param javaProjectBuilder JavaProjectBuilder
* @param template template
*/
public Template buildDirectoryDataDocTemplate(ApiConfig config, JavaProjectBuilder javaProjectBuilder, String template) {
List<ApiDocDict> directoryList = DocUtil.buildDictionary(config, javaProjectBuilder);
Template mapper = BeetlTemplateUtil.getByName(template);
setDirectoryLanguageVariable(config, mapper);
Expand All @@ -375,7 +428,7 @@ public void buildDirectoryDataDoc(ApiConfig config, JavaProjectBuilder javaProje
String strTime = DateTimeUtil.long2Str(now, DateTimeUtil.DATE_FORMAT_SECOND);
mapper.binding(TemplateVariable.CREATE_TIME.getVariable(), strTime);
mapper.binding(TemplateVariable.DICT_LIST.getVariable(), directoryList);
FileUtil.nioWriteFile(mapper.render(), config.getOutPath() + DocGlobalConstants.FILE_SEPARATOR + outPutFileName);
return mapper;
}

private List<ApiDoc> listOfApiData(ApiConfig config, JavaProjectBuilder javaProjectBuilder) {
Expand Down
144 changes: 144 additions & 0 deletions src/main/java/com/ly/doc/builder/WordDocBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* Copyright (C) 2018-2023 smart-doc
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.ly.doc.builder;
chenqi146 marked this conversation as resolved.
Show resolved Hide resolved

import com.ly.doc.constants.DocGlobalConstants;
import com.ly.doc.factory.BuildTemplateFactory;
import com.ly.doc.helper.JavaProjectBuilderHelper;
import com.ly.doc.model.ApiConfig;
import com.ly.doc.model.ApiDoc;
import com.ly.doc.template.IDocBuildTemplate;
import com.power.common.util.DateTimeUtil;
import com.power.common.util.FileUtil;
import com.thoughtworks.qdox.JavaProjectBuilder;
import org.beetl.core.Template;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
* @author <a href="mailto:[email protected]">chenqi</a>
* @version 1.0
*/
public class WordDocBuilder {
private static final String TEMPLATE_DOCX = "template/word/template.docx";


private static final String BUILD_DOCX = "index.docx";
private static final String BUILD_ERROR_DOCX = "error.docx";
private static final String BUILD_DICT_DOCX = "dict.docx";

/**
* build controller api
*
* @param config config
*/
public static void buildApiDoc(ApiConfig config) throws Exception {
JavaProjectBuilder javaProjectBuilder = JavaProjectBuilderHelper.create();
buildApiDoc(config, javaProjectBuilder);
}

/**
* build controller api
*
* @param config config
*/
public static void buildApiDoc(ApiConfig config, JavaProjectBuilder javaProjectBuilder) throws Exception {
DocBuilderTemplate builderTemplate = new DocBuilderTemplate();
builderTemplate.checkAndInit(config, Boolean.TRUE);
config.setParamsDataToTree(false);
ProjectDocConfigBuilder configBuilder = new ProjectDocConfigBuilder(config, javaProjectBuilder);
IDocBuildTemplate<ApiDoc> docBuildTemplate = BuildTemplateFactory.getDocBuildTemplate(config.getFramework());
Objects.requireNonNull(docBuildTemplate, "doc build template is null");
List<ApiDoc> apiDocList = docBuildTemplate.getApiData(configBuilder);

if (config.isAllInOne()) {
String version = config.isCoverOld() ? "" : "-V" + DateTimeUtil.long2Str(System.currentTimeMillis(), DocGlobalConstants.DATE_FORMAT_YYYY_MM_DD_HH_MM);
String docName = builderTemplate.allInOneDocName(config, "AllInOne" + version + ".docx", ".docx");
apiDocList = docBuildTemplate.handleApiGroup(apiDocList, config);
String outPath = config.getOutPath();
FileUtil.mkdirs(outPath);
Template tpl = builderTemplate.buildAllRenderDocTemplate(apiDocList, config, javaProjectBuilder, DocGlobalConstants.ALL_IN_ONE_WORD_XML_TPL, null, null);
copyAndReplaceDocx(tpl.render(), outPath + DocGlobalConstants.FILE_SEPARATOR + docName);
} else {
FileUtil.mkdir(config.getOutPath());
for (ApiDoc doc : apiDocList) {
Template template = builderTemplate.buildApiDocTemplate(doc, config, DocGlobalConstants.WORD_XML_TPL);
copyAndReplaceDocx(template.render(), config.getOutPath() + DocGlobalConstants.FILE_SEPARATOR + doc.getName() + BUILD_DOCX);
}
Template errorCodeDocTemplate = builderTemplate.buildErrorCodeDocTemplate(config, DocGlobalConstants.WORD_ERROR_XML_TPL, javaProjectBuilder);
copyAndReplaceDocx(errorCodeDocTemplate.render(), config.getOutPath() + DocGlobalConstants.FILE_SEPARATOR + BUILD_ERROR_DOCX);

Template directoryDataDocTemplate = builderTemplate.buildDirectoryDataDocTemplate(config, javaProjectBuilder, DocGlobalConstants.WORD_DICT_XML_TPL);
copyAndReplaceDocx(directoryDataDocTemplate.render(), config.getOutPath() + DocGlobalConstants.FILE_SEPARATOR + BUILD_DICT_DOCX);
}
}


/**
* replace docx content
*
* @param content doc content
* @param docxOutputPath docx output path
* @return
* @since 1.0.0
*/
public static void copyAndReplaceDocx(String content, String docxOutputPath) throws Exception {
InputStream resourceAsStream = WordDocBuilder.class.getClassLoader().getResourceAsStream(TEMPLATE_DOCX);
Objects.requireNonNull(resourceAsStream, "word template docx is not found");

ZipInputStream zipInputStream = new ZipInputStream(resourceAsStream);
ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(Paths.get(docxOutputPath)));
// Traverse the files in the compressed package
ZipEntry entry;
while ((entry = zipInputStream.getNextEntry()) != null) {
String entryName = entry.getName();

if (entryName.equals("word/document.xml")) {
zipOutputStream.putNextEntry(new ZipEntry(entryName));
byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
zipOutputStream.write(bytes, 0, bytes.length);
} else {
// copy
zipOutputStream.putNextEntry(entry);
byte[] buffer = new byte[1024];
int len;
while ((len = zipInputStream.read(buffer)) > 0) {
zipOutputStream.write(buffer, 0, len);
}
}

zipOutputStream.closeEntry();
zipInputStream.closeEntry();
}

zipInputStream.close();
zipOutputStream.close();
}

}
7 changes: 7 additions & 0 deletions src/main/java/com/ly/doc/constants/DocGlobalConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ public interface DocGlobalConstants {

String ALL_IN_ONE_HTML_TPL = "AllInOne.html";

String ALL_IN_ONE_WORD_XML_TPL = "word/AllInOneWordTemplate.xml";
String WORD_XML_TPL = "word/index.xml";
String WORD_ERROR_XML_TPL = "word/error.xml";
String WORD_DICT_XML_TPL = "word/dict.xml";

String HTML_API_DOC_TPL = "HtmlApiDoc.html";

String ERROR_CODE_LIST_MD_TPL = "ErrorCodeList.md";
Expand Down Expand Up @@ -259,4 +264,6 @@ public interface DocGlobalConstants {
String JAR_TEMP = "./smart-temp/";

String DEFAULT_PRIMITIVE = "defaultPrimitive";

String DATE_FORMAT_YYYY_MM_DD_HH_MM = "yyyyMMddHHmm";
}
44 changes: 44 additions & 0 deletions src/main/java/com/ly/doc/function/WordXmlEscape.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2018-2023 smart-doc
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.ly.doc.function;

import com.ly.doc.utils.DocUtil;
import org.beetl.core.Context;
import org.beetl.core.Function;

/**
* beetl template function
* @author yu 2021/6/26.
*/
public class WordXmlEscape implements Function {

@Override
public String call(Object[] paras, Context ctx) {
String str = String.valueOf(paras[0])
.replaceAll(" ", " ")
.replaceAll("&nbsp;&nbsp;", " ")
.replaceAll("&nbsp;", "")
.replaceAll("<br/>", "")
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;");
return DocUtil.getEscapeAndCleanComment(str);
}
}
1 change: 1 addition & 0 deletions src/main/resources/smart-doc-beetl.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
DELIMITER_STATEMENT_START=<%
DELIMITER_STATEMENT_END=%>
FN.htmlEscape=com.ly.doc.function.HtmlEscape
FN.wordXmlEscape=com.ly.doc.function.WordXmlEscape
FN.removeLineBreaks=com.ly.doc.function.RemoveLineBreaks
FN.lineBreaksToBr=com.ly.doc.function.LineBreaksToBr
FNP.strUtil=org.beetl.ext.fn.StringUtil
Loading
Loading