diff --git a/build.gradle b/build.gradle index 617b9d0fa..8399410db 100644 --- a/build.gradle +++ b/build.gradle @@ -73,6 +73,7 @@ allprojects { implementation 'com.fr.decision:fine-decision:' + frVersion implementation 'com.fr.schedule:fine-schedule:' + frVersion implementation 'com.fr.report:engine-report:' + frDevVersion + implementation 'com.fr.report:engine-x:' + frDevVersion implementation 'com.fr.report:engine-chart:' + frDevVersion implementation 'com.fr.report:engine-i18n:' + frDevVersion implementation 'com.fr.design:design-i18n:' + frDevVersion diff --git a/designer-base/src/main/java/com/fr/design/actions/file/BatchCompileAction.java b/designer-base/src/main/java/com/fr/design/actions/file/BatchCompileAction.java new file mode 100644 index 000000000..812d89e5f --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/actions/file/BatchCompileAction.java @@ -0,0 +1,47 @@ +package com.fr.design.actions.file; + +import com.fr.base.BaseUtils; +import com.fr.design.actions.UpdateAction; +import com.fr.design.menu.MenuKeySet; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.app.designer.transform.ui.BatchTransformPane; + +import javax.swing.KeyStroke; +import java.awt.Dialog; +import java.awt.event.ActionEvent; + +/** + * Created by kerry on 2019-12-10 + */ +public class BatchCompileAction extends UpdateAction { + public BatchCompileAction() { + this.setMenuKeySet(COMPILE); + this.setName(getMenuKeySet().getMenuKeySetName() + "..."); + this.setMnemonic(getMenuKeySet().getMnemonic()); + this.setSmallIcon(BaseUtils.readIcon("/com/fr/nx/app/designer/transform/batch_transform.png")); + } + + @Override + public void actionPerformed(ActionEvent e) { + BatchTransformPane batchTransformPane = new BatchTransformPane(); + Dialog dialog = batchTransformPane.showDialog(); + dialog.setVisible(true); + } + + private static final MenuKeySet COMPILE = new MenuKeySet() { + @Override + public char getMnemonic() { + return 'C'; + } + + @Override + public String getMenuName() { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Batch_Transform"); + } + + @Override + public KeyStroke getKeyStroke() { + return null; + } + }; +} diff --git a/designer-base/src/main/java/com/fr/design/actions/server/LocalAnalyzerAction.java b/designer-base/src/main/java/com/fr/design/actions/server/LocalAnalyzerAction.java new file mode 100644 index 000000000..358de5774 --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/actions/server/LocalAnalyzerAction.java @@ -0,0 +1,49 @@ +package com.fr.design.actions.server; + +import com.fr.base.BaseUtils; +import com.fr.design.actions.UpdateAction; +import com.fr.design.menu.MenuKeySet; +import com.fr.design.utils.DesignUtils; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.app.web.URLConstants; +import com.fr.stable.StableUtils; + +import javax.swing.KeyStroke; +import java.awt.event.ActionEvent; + +/** + * @author Maksim + * Created in 2020/11/5 11:44 上午 + */ +public class LocalAnalyzerAction extends UpdateAction { + + public LocalAnalyzerAction() { + this.setMenuKeySet(ANALYZER); + this.setName(getMenuKeySet().getMenuKeySetName()); + this.setMnemonic(getMenuKeySet().getMnemonic()); + this.setSmallIcon(BaseUtils.readIcon("/com/fr/nx/app/designer/transform/analyzer.png")); + } + + @Override + public void actionPerformed(ActionEvent e) { + String path = StableUtils.pathJoin("/url", URLConstants.ANALYZE_VIEW); + DesignUtils.visitEnvServerByParameters(path, new String[]{}, new String[]{}); + } + + private static final MenuKeySet ANALYZER = new MenuKeySet() { + @Override + public char getMnemonic() { + return 'R'; + } + + @Override + public String getMenuName() { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin-Engine_Analyzer_Menu_Name"); + } + + @Override + public KeyStroke getKeyStroke() { + return null; + } + }; +} diff --git a/designer-base/src/main/java/com/fr/design/gui/itree/filetree/FileTreeIcon.java b/designer-base/src/main/java/com/fr/design/gui/itree/filetree/FileTreeIcon.java index b418c1db4..e0515fe81 100644 --- a/designer-base/src/main/java/com/fr/design/gui/itree/filetree/FileTreeIcon.java +++ b/designer-base/src/main/java/com/fr/design/gui/itree/filetree/FileTreeIcon.java @@ -48,6 +48,9 @@ public class FileTreeIcon { public static final Icon MODERN_CHT_FILE_IMAGE_ICON = BaseUtils.readIcon("/com/fr/design/images/gui/modern_style_cht_file_icon_16x16.png"); + public static final Icon CPTX_ICON = BaseUtils.readIcon("/com/fr/nx/app/designer/cptx_file_icon.png"); + public static final Icon CPTX_LOCKED_ICON = BaseUtils.readIcon("/com/fr/nx/app/designer/cptx_file_icon_locked.png"); + public static final LockIcon FOLDER_LOCK_ICON = new LockIcon(BaseUtils.readImage("/com/fr/design/images/gui/fold.png")); public static final LockIcon FILE_LOCK_ICON = @@ -108,6 +111,7 @@ public class FileTreeIcon { public final static int CPT_FILE = 11; //.cpt public final static int FRM_FILE = 12; //.form .frm public final static int CHT_FILE = 13; //.chart .cht + public final static int CPTX_FILE = 14; //.cptx public static Icon getIcon(File file) { return FileTreeIcon.getIcon(file, false); @@ -252,6 +256,12 @@ public class FileTreeIcon { } else { return FileTreeIcon.MODERN_CPT_FILE_IMAGE_ICON; } + } else if (fileType == CPTX_FILE) { + if (isLocked) { + return FileTreeIcon.CPTX_LOCKED_ICON; + } else { + return FileTreeIcon.CPTX_ICON; + } } else if (fileType == FRM_FILE) { //form frm if (isLocked) { return FileTreeIcon.FRM_FILE_LOCK_ICON; @@ -314,6 +324,8 @@ public class FileTreeIcon { return BMP_FILE; } else if (fileName.endsWith(".cpt")) { return CPT_FILE; + } else if (fileName.endsWith(".cptx")) { + return CPTX_FILE; } else if (fileName.endsWith(".frm") || fileName.endsWith(".form")) { return FRM_FILE; } else if (fileName.endsWith(".cht") || fileName.endsWith(".chart")) { diff --git a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java index 483e74d9d..f558f93fc 100644 --- a/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java +++ b/designer-base/src/main/java/com/fr/design/mainframe/JTemplate.java @@ -3,6 +3,7 @@ package com.fr.design.mainframe; import com.fr.base.BaseUtils; import com.fr.base.FRContext; import com.fr.base.Parameter; +import com.fr.base.extension.FileExtension; import com.fr.base.io.BaseBook; import com.fr.base.iofile.attr.DesignBanCopyAttrMark; import com.fr.base.iofile.attr.TemplateIdAttrMark; @@ -14,6 +15,7 @@ import com.fr.design.ExtraDesignClassManager; import com.fr.design.actions.TableDataSourceAction; import com.fr.design.actions.edit.RedoAction; import com.fr.design.actions.edit.UndoAction; +import com.fr.design.actions.file.BatchCompileAction; import com.fr.design.actions.file.SaveAsTemplateAction; import com.fr.design.actions.file.SaveTemplateAction; import com.fr.design.actions.file.WebPreviewUtils; @@ -60,6 +62,8 @@ import com.fr.general.ComparatorUtils; import com.fr.log.FineLoggerFactory; import com.fr.plugin.context.PluginContext; import com.fr.plugin.context.PluginRuntime; +import com.fr.nx.app.designer.toolbar.CompileAction; +import com.fr.nx.app.designer.toolbar.TemplateTransformer; import com.fr.plugin.injectable.PluginModule; import com.fr.plugin.manage.PluginFilter; import com.fr.plugin.observer.PluginEvent; @@ -781,10 +785,12 @@ public abstract class JTemplate> protected boolean saveToNewFile(String oldName) { boolean result = false; + String path = this.editingFILE.getPath(); Set providers = ExtraDesignClassManager.getInstance().getArray(ReportSupportedFileUIProvider.XML_TAG); for (ReportSupportedFileUIProvider provider : providers) { - result = result || provider.saveToNewFile(this.editingFILE.getPath(), this); + result = result || provider.saveToNewFile(path, this); } + result = result || saveToNewFile4Cptx(path); if (!result) { result = result || this.saveFile(); //更换最近打开 @@ -794,6 +800,14 @@ public abstract class JTemplate> return result; } + private boolean saveToNewFile4Cptx(String targetPath) { + if (FileExtension.CPTX.matchExtension(targetPath)) { + TemplateTransformer.TO_CPTX.transform(this); + return true; + } + return false; + } + protected void mkNewFile(FILE file) { try { file.mkfile(); @@ -887,7 +901,7 @@ public abstract class JTemplate> } else if (DesignerMode.isAuthorityEditing()) { return new ShortCut[]{new SaveTemplateAction(this), new UndoAction(this), new RedoAction(this)}; } else { - return new ShortCut[]{new SaveTemplateAction(this), new SaveAsTemplateAction(this), new UndoAction(this), new RedoAction(this)}; + return new ShortCut[]{new SaveTemplateAction(this), new SaveAsTemplateAction(this), new BatchCompileAction(), new UndoAction(this), new RedoAction(this)}; } } @@ -1323,8 +1337,10 @@ public abstract class JTemplate> * @return 按钮组 */ public UIButton[] createExtraButtons() { + UIButton[] uiButtons = new UIButton[] { + (UIButton) new CompileAction().createToolBarComponent() + }; Set providers = ExtraDesignClassManager.getInstance().getArray(DesignerFrameUpButtonProvider.XML_TAG); - UIButton[] uiButtons = new UIButton[0]; for (DesignerFrameUpButtonProvider provider : providers) { uiButtons = ArrayUtils.addAll(uiButtons, provider.getUpButtons(getMenuState())); } diff --git a/designer-base/src/main/java/com/fr/design/preview/PagePlusPreview.java b/designer-base/src/main/java/com/fr/design/preview/PagePlusPreview.java new file mode 100644 index 000000000..04fd6623c --- /dev/null +++ b/designer-base/src/main/java/com/fr/design/preview/PagePlusPreview.java @@ -0,0 +1,41 @@ +package com.fr.design.preview; + +import com.fr.design.fun.impl.AbstractPreviewProvider; +import com.fr.general.web.ParameterConstants; +import com.fr.locale.InterProviderFactory; + +import java.util.HashMap; +import java.util.Map; + +import static com.fr.nx.app.web.v9.PagePlusActor.TYPE; + +public class PagePlusPreview extends AbstractPreviewProvider { + private static final int CODE = 100; + + @Override + public String nameForPopupItem() { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Page-Plus"); + } + + @Override + public String iconPathForPopupItem() { + return "com/fr/design/images/buttonicon/pages.png"; + } + + @Override + public String iconPathForLarge() { + return "com/fr/design/images/buttonicon/pageb24.png"; + } + + @Override + public int previewTypeCode() { + return CODE; + } + + @Override + public Map parametersForPreview() { + Map map = new HashMap(); + map.put(ParameterConstants.OP, TYPE); + return map; + } +} diff --git a/designer-base/src/main/java/com/fr/file/FILEChooserPane.java b/designer-base/src/main/java/com/fr/file/FILEChooserPane.java index 43c3f43ad..a97b7db9d 100644 --- a/designer-base/src/main/java/com/fr/file/FILEChooserPane.java +++ b/designer-base/src/main/java/com/fr/file/FILEChooserPane.java @@ -770,6 +770,7 @@ public class FILEChooserPane extends BasicPane { // ben:filefilter设置初值为cpt过滤 this.addChooseFILEFilter(new ChooseFileFilter(FileExtension.CPT, appName + Toolkit.i18nText("Fine-Design_Report_Template_File"))); + this.addChooseFILEFilter(new ChooseFileFilter(FileExtension.CPTX, appName + Toolkit.i18nText("Fine-Design_Report_Template_File"))); // richer:form文件 daniel 改成三个字 this.addChooseFILEFilter(new ChooseFileFilter(FileExtension.FRM, appName + Toolkit.i18nText("Fine-Design_Report_Template_File"))); diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/cptx/io/DesignReadWritableProvider.java b/designer-base/src/main/java/com/fr/nx/app/designer/cptx/io/DesignReadWritableProvider.java new file mode 100644 index 000000000..0896e66e9 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/cptx/io/DesignReadWritableProvider.java @@ -0,0 +1,60 @@ +package com.fr.nx.app.designer.cptx.io; + +import com.fr.common.annotations.Negative; +import com.fr.file.FILE; +import com.fr.nx.app.designer.utils.DesignerCptxFileUtils; +import com.fr.nx.cptx.io.handle.impl.AbstractCptxIOProvider; +import com.fr.nx.cptx.pack.util.CompiledReportInputStream; +import com.fr.nx.cptx.pack.util.CompiledReportOutputStream; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * 用于设计器保存编译后的结果并缓存可web预览模版 + * 没有可观的内存实现,内存实现方式复杂,赶发布,先提供简单的内存实现方式 + * + * @author: Maksim + * @Date: Created in 2020/4/10 + * @Description: 用于cptx写操作的Provider + */ +@Negative(until = "2020-05-10") +public class DesignReadWritableProvider extends AbstractCptxIOProvider { + /** + * 原文件 + */ + private final FILE file; + /** + * 编译文件夹 + */ + private final String compileDir; + + private CompiledReportOutputStream outputStream; + + public DesignReadWritableProvider(FILE file) { + this.file = file; + this.compileDir = DesignerCptxFileUtils.generateCompileDir(file); + } + + @Override + public InputStream open() throws Exception { + if (outputStream == null) { + throw new IOException("can not read cptx template before compile"); + } + return new CompiledReportInputStream(outputStream.toByteArray()); + } + + /** + * 对于写操作,用到的只有这个方法,以流形式来操作 + * + * @return 保存的目标输出流 + * @throws Exception e + */ + @Override + public OutputStream createTemp() throws Exception { + outputStream = new CompiledReportOutputStream(file.asOutputStream(), compileDir); + return outputStream; + } + +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/monitor/DesignerMetricRecorder.java b/designer-base/src/main/java/com/fr/nx/app/designer/monitor/DesignerMetricRecorder.java new file mode 100644 index 000000000..0644bd511 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/monitor/DesignerMetricRecorder.java @@ -0,0 +1,97 @@ +package com.fr.nx.app.designer.monitor; + +import com.fr.design.mainframe.errorinfo.ErrorInfo; +import com.fr.intelli.record.MetricRegistry; +import com.fr.json.JSON; +import com.fr.json.JSONFactory; +import com.fr.json.JSONObject; +import com.fr.message.ErrorMessage; +import com.fr.nx.app.designer.toolbar.TransformResult; + +/** + * Created by loy on 2021/1/18. + * + *

设计器埋点记录相关 + */ +public class DesignerMetricRecorder { + + private static final String ATTR_TEMPLATE_ID = "templateid"; + private static final String ATTR_USERNAME = "username"; + private static final String ATTR_UUID = "uuid"; + private static final String ATTR_ACTIVE_KEY = "activekey"; + private static final String ATTR_TEST_LOG = "testlog"; + private static final String ATTR_REPORT_CONTENT = "reportcontent"; + private static final String ATTR_ERROR_STACK = "errorstack"; + private static final String IDENTIFICATION = "view_plus"; + + private DesignerMetricRecorder() { + } + + /** + * 模板转换失败的埋点 + */ + public static void submitFailedTransform(TransformResult result, String templateID, String name, Exception e) { + if (isPressureTesting()) { + return; + } + saveAsJSON(result, templateID, name, e); + } + + /** + * 记录在设计器的错误信息收集中 + */ + private static void saveToErrorInfo(TransformResult result, String templateID, String name, Exception e, String unsupportMessage) { + JSONObject jo = JSONFactory.createJSON(JSON.OBJECT); + jo.put(ATTR_USERNAME, IDENTIFICATION); + jo.put(ATTR_UUID, IDENTIFICATION); + jo.put(ATTR_ACTIVE_KEY, IDENTIFICATION); + jo.put(ATTR_TEMPLATE_ID, templateID); + jo.put(ATTR_REPORT_CONTENT, name); + if (result == TransformResult.FAILED) { + jo.put(ATTR_TEST_LOG, e); + jo.put(ATTR_ERROR_STACK, e.getMessage()); + } else { + jo.put(ATTR_TEST_LOG, unsupportMessage); + } + new ErrorInfo().saveFileToCache(jo); + } + + /** + * 记录在fine_record_error表中 + */ + private static void saveToErrorMessage(TransformResult result, String templateID, String name, Exception e, String unsupportMessage) { + String errorMessage = unsupportMessage; + if (result == TransformResult.FAILED) { + errorMessage = e.getStackTrace()[0].toString(); + } + ErrorMessage message = ErrorMessage.build(e == null ? errorMessage : e.getMessage(), errorMessage); + message.setTname(name); + message.setDisplayName(name); + MetricRegistry.getMetric().submit(message); + } + + private static void saveAsJSON(TransformResult result, String templateID, String name, Exception e) { + saveToErrorInfo(result, templateID, name, e, null); + saveToErrorMessage(result, templateID, name, e, null); + } + + /** + * 预编译中不支持的功能的埋点 + */ + public static void submitUnSupportTransform(TransformResult result, String templateID, String name, String unsupportMessage) { + if (isPressureTesting()) { + return; + } + saveToErrorInfo(result, templateID, name, null, unsupportMessage); + saveToErrorMessage(result, templateID, name, null, unsupportMessage); + } + + /** + * 埋点对压测性能的影响控制参数 + * + * @return 是否压测 + */ + public static boolean isPressureTesting() { + return Boolean.parseBoolean(System.getProperty("monitorDebug")); + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/CompileAction.java b/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/CompileAction.java new file mode 100644 index 000000000..4496ecd9a --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/CompileAction.java @@ -0,0 +1,116 @@ +package com.fr.nx.app.designer.toolbar; + +import com.fr.base.extension.FileExtension; +import com.fr.design.actions.UpdateAction; +import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.JTemplate; +import com.fr.design.menu.MenuKeySet; +import com.fr.file.FILE; +import com.fr.file.FileNodeFILE; +import com.fr.general.IOUtils; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.app.designer.utils.CompileTransformUtil; + +import javax.swing.Icon; +import javax.swing.JComponent; +import javax.swing.KeyStroke; +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; + +import static com.fr.design.dialog.FineJOptionPane.showConfirmDialog; +import static com.fr.design.gui.syntax.ui.rtextarea.RTADefaultInputMap.DEFAULT_MODIFIER; +import static javax.swing.JOptionPane.OK_CANCEL_OPTION; +import static javax.swing.JOptionPane.OK_OPTION; +import static javax.swing.JOptionPane.WARNING_MESSAGE; + +/** + * Created by kerry on 2019-10-14 + */ +public class CompileAction extends UpdateAction { + public static final Icon TRANS_ICON = IOUtils.readIcon("/com/fr/nx/app/designer/transform.png"); + + private static final MenuKeySet COMPILE_ATTR = new MenuKeySet() { + @Override + public char getMnemonic() { + return 'C'; + } + + @Override + public String getMenuName() { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Transform-Tooltip"); + } + + @Override + public KeyStroke getKeyStroke() { + return KeyStroke.getKeyStroke(KeyEvent.VK_T, DEFAULT_MODIFIER); + } + }; + + public CompileAction() { + initMenuStyle(); + } + + private void initMenuStyle() { + this.setMenuKeySet(COMPILE_ATTR); + this.setName(getMenuKeySet().getMenuKeySetName()); + this.setMnemonic(getMenuKeySet().getMnemonic()); + this.setSmallIcon(TRANS_ICON); + this.setAccelerator(getMenuKeySet().getKeyStroke()); + } + + @Override + public void actionPerformed(ActionEvent e) { + JTemplate jtemplate = DesignerContext.getDesignerFrame().getSelectedJTemplate(); + if (jtemplate == null || jtemplate.getEditingFILE() == null) { + return; + } + FILE currentTemplate = jtemplate.getEditingFILE(); + if(currentTemplate instanceof FileNodeFILE){ + transformAndDisplay(jtemplate); + }else { + //模板不在报表环境下,提示保存 + int selVal = showConfirmDialog( + DesignerContext.getDesignerFrame(), + InterProviderFactory.getProvider().getLocText("Fine-Plugin-Engine_Preview_Message"), + InterProviderFactory.getProvider().getLocText("Fine-Plugin-Engine_Transformer_Tips"), + OK_CANCEL_OPTION, + WARNING_MESSAGE); + + if (OK_OPTION == selVal) { + //保存成功才会执行编译 + if (jtemplate.saveAsTemplate2Env()) { + transformAndDisplay(jtemplate); + } + } + } + + } + + //编译模板并展示 + private void transformAndDisplay(JTemplate jtemplate){ + String path = jtemplate.getEditingFILE().getPath(); + TemplateTransformer transformer = TemplateTransformer.parse(path); + FILE targetFile = CompileTransformUtil.getTargetFile(jtemplate, transformer); + TransformResult result = transformer.transform(jtemplate, targetFile); + jtemplate.fireJTemplateSaved(); + result.display(); + } + + @Override + public boolean isEnabled() { + JTemplate jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + String path = jt.getEditingFILE().getPath(); + return FileExtension.CPTX.matchExtension(path) || FileExtension.CPT.matchExtension(path); + } + + @Override + public JComponent createToolBarComponent() { + UIButton transBtn = (UIButton) super.createToolBarComponent(); + transBtn.set4ToolbarButton(); + transBtn.setToolTipText(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Transform-Tooltip")); + return transBtn; + } + +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TemplateTransformer.java b/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TemplateTransformer.java new file mode 100644 index 000000000..6294fa4d8 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TemplateTransformer.java @@ -0,0 +1,221 @@ +package com.fr.nx.app.designer.toolbar; + +import com.fr.base.extension.FileExtension; +import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.file.MutilTempalteTabPane; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.JTemplate; +import com.fr.file.FILE; +import com.fr.file.FileNodeFILE; +import com.fr.file.filetree.FileNode; +import com.fr.general.ComparatorUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.main.impl.WorkBook; +import com.fr.nx.app.designer.monitor.DesignerMetricRecorder; +import com.fr.nx.compile.CompileStatus; +import com.fr.nx.compile.ReportCompiler; +import com.fr.nx.compile.adapter.LegacyWorkBookAdapter; +import com.fr.nx.compile.util.ReportCompileUtils; +import com.fr.nx.cptx.CptxIOManager; +import com.fr.nx.cptx.cache.CptxTemplatePool; +import com.fr.nx.cptx.entry.CptxTemplate; +import com.fr.nx.cptx.io.handle.CptxTemplateHandle; +import com.fr.nx.app.designer.cptx.io.DesignReadWritableProvider; +import com.fr.nx.cptx.monitor.CompileMonitorHandler; +import com.fr.nx.cptx.utils.CptxFileUtils; +import com.fr.nx.data.layer.LayerItem; +import com.fr.nx.data.layer.LayerProps; +import com.fr.nx.template.compile.CompiledReport; +import com.fr.stable.StringUtils; +import org.jetbrains.annotations.NotNull; + +import java.io.OutputStream; + +import static com.fr.base.extension.FileExtension.ALL; +import static com.fr.base.extension.FileExtension.CPT; +import static com.fr.base.extension.FileExtension.CPTX; + +/** + * 模板转换器, 可以把cptx模板转为cpt, 或者cpt模板转为cptx. + * 以后可以扩展支持frm frmx互相转换 + */ + +public enum TemplateTransformer { + + TO_CPTX(CPT) { + @Override + public TransformResult transform(JTemplate jtemplate, FILE file) { + WorkBook workbook = (WorkBook) jtemplate.getTarget(); + TransformResultInfo resultInfo = compileCPTX(workbook, file); + if (needDoAfterTransformed(resultInfo.getResult())) { + doAfterTransformed(file, jtemplate); + } + return resultInfo.getResult(); + } + }, + TO_CPT(CPTX) { + @Override + public TransformResult transform(JTemplate jtemplate, FILE file) { + WorkBook workbook = (WorkBook) jtemplate.getTarget(); + try { + workbook.export(file.asOutputStream()); + doAfterTransformed(file, jtemplate); + return TransformResult.SUCCESS; + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + return TransformResult.FAILED; + } + } + }, + OTHER(ALL); + private FileExtension extension; + + TemplateTransformer(FileExtension extension) { + this.extension = extension; + } + + public TransformResult transform(JTemplate jtemplate) { + return transform(jtemplate, jtemplate.getEditingFILE()); + } + + public TransformResult transform(JTemplate jtemplate, FILE newFile) { + return TransformResult.UNSUPPORT; + } + + public static TemplateTransformer parse(String path) { + for (TemplateTransformer transformer : values()) { + if (transformer.extension.matchExtension(path)) { + return transformer; + } + } + return OTHER; + } + + private static boolean needDoAfterTransformed(TransformResult result) { + return ComparatorUtils.equals(TransformResult.SUCCESS, result) + || ComparatorUtils.equals(TransformResult.UNSUPPORT, result); + } + + private static void doAfterTransformed(FILE file, JTemplate jtemplate) { + JTemplate jt = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + if (ComparatorUtils.equals(file.getPath(), jt.getPath())) { + HistoryTemplateListCache.getInstance().closeSelectedReport(jt); + DesignerContext.getDesignerFrame().openTemplate(file); + return; + } + MutilTempalteTabPane.getInstance().setIsCloseCurrent(true); + MutilTempalteTabPane.getInstance().closeFormat(jt); + MutilTempalteTabPane.getInstance().closeSpecifiedTemplate(jt); + DesignerContext.getDesignerFrame().openTemplate(file); + } + + + public static FILE createOutputFile(String oldPath, String oldSuffix, String newSuffix) { + String newPath = generateNewPath(oldPath, oldSuffix, newSuffix); + return new FileNodeFILE(new FileNode(newPath, false)); + } + + private static String generateNewPath(String oldPath, String oldSuffix, String newSuffix) { + if (StringUtils.isEmpty(oldPath) || StringUtils.isEmpty(oldSuffix) || StringUtils.isEmpty(newSuffix)) { + return oldPath; + } + + if (oldPath.endsWith(oldSuffix)) { + return StringUtils.cutStringEndWith(oldPath, oldSuffix) + newSuffix; + } + return oldPath; + } + + /** + * 编译和保存 + * + * @param workbook work + * @param file file + * @return 编译保存结果 + */ + @NotNull + public static TransformResultInfo compileCPTX(WorkBook workbook, FILE file) { + //对于非工作目录的cptx,修改无需执行编译 + if(!(file instanceof FileNodeFILE)){ + try { + OutputStream outputStream = file.asOutputStream(); + workbook.export(outputStream); + return TransformResultInfo.generateResult(TransformResult.SUCCESS).saved(true); + }catch (Exception e){ + FineLoggerFactory.getLogger().error(e.getMessage(), e); + return TransformResultInfo.generateResult(TransformResult.FAILED).saved(false); + } + } + boolean saved; + CompiledReport report = null; + CompileStatus compileStatus = CompileStatus.SUCCEED; + if (workbook == null || file == null) { + return TransformResultInfo + .generateResult(TransformResult.FAILED, "work and file must not be null") + .saved(false); + } + String failMessage = null; + // 编译 + ReportCompiler compiler; + try { + compiler = new ReportCompiler(new LegacyWorkBookAdapter(workbook)); + long startTime = System.currentTimeMillis(); + compiler.compile(); + report = compiler.getCompiledReport(); + // 折叠树埋点 + LayerProps props = ReportCompileUtils.getLayerProps(report); + LayerItem[] items; + if (props != null && (items = props.getLayerItems()) != null) { + CompileMonitorHandler.submitTreeCompileFocusPoint( + startTime, file.getPath(), items.length, + props.getExpandLayer(), true + ); + } + compileStatus = compiler.getStatus(); + failMessage = compiler.getFailMessage(); + long endTime = System.currentTimeMillis(); + CompileMonitorHandler.submitCompileMessage(startTime, endTime, file.getPath(), ""); + if (compileStatus != CompileStatus.SUCCEED) { + DesignerMetricRecorder.submitUnSupportTransform( + TransformResult.UNSUPPORT, + workbook.getTemplateID(), + file.getName(), + compileStatus == CompileStatus.FAILED_UNSUPPORT ? failMessage : null + ); + } + } catch (Exception exception) { + String templateID = workbook.getTemplateID(); + String fileName = file.getName(); + DesignerMetricRecorder.submitFailedTransform(TransformResult.FAILED, templateID, fileName, exception); + FineLoggerFactory.getLogger().error(exception.getMessage(), exception); + } + + // 构建编译结果,当前的 cptx template 是未经反序列化钩子处理过的 cptx 模版对象,不能直接用于模版预览 + CptxTemplate template = CptxIOManager.createCptxTemplate(workbook, report, compileStatus, failMessage); + // 保存 + DesignReadWritableProvider cptx = new DesignReadWritableProvider(file); + CptxTemplateHandle handle = CptxIOManager.create(template, cptx); + try { + saved = handle.save(); + } catch (Exception exception) { + // 存储cptx格式报错或者失败 + FineLoggerFactory.getLogger().error(exception.getMessage(), exception); + DesignerMetricRecorder.submitFailedTransform(TransformResult.FAILED, workbook.getTemplateID(), file.getName(), exception); + return TransformResultInfo.generateResult(TransformResult.FAILED, failMessage).saved(false); + } + // 读取可 web 预览模版并缓存 + try { + // todo 是否考虑异步 + String timestampedPath = CptxFileUtils.getTimestampedPath(file.getPath()); + CptxTemplate previewCptxTemplate = CptxIOManager.open(cptx).getTemplate(); + CptxTemplatePool.getInstance().addCptxTemplate(timestampedPath, previewCptxTemplate); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + if (report == null) { + // 编译报错或者失败 + return TransformResultInfo.generateResult(TransformResult.FAILED, failMessage).saved(saved); + } + return TransformResultInfo.generateResult(TransformResult.parse(compileStatus), failMessage).saved(saved); + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TransformResult.java b/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TransformResult.java new file mode 100644 index 000000000..9668ec805 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TransformResult.java @@ -0,0 +1,55 @@ +package com.fr.nx.app.designer.toolbar; + +import com.fr.design.file.TemplateTreePane; +import com.fr.design.mainframe.DesignerContext; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.compile.CompileStatus; + +import javax.swing.JOptionPane; + +public enum TransformResult { + + SUCCESS(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Transform-Success")) { + @Override + public void display() { + // 转换成功后, 刷新下目录树 + TemplateTreePane.getInstance().refresh(); + super.display(); + } + }, + FAILED(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Transform-Failed")), + UNSUPPORT(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Transform-Unsupport")){ + @Override + public void display() { + JOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), TransformResult.SUCCESS.toString()); + } + }; + + private String msg; + + private TransformResult(String msg) { + this.msg = msg; + } + + // 展示结果 + public void display() { + JOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), this.toString()); + } + + @Override + public String toString() { + return this.msg; + } + + public static TransformResult parse(CompileStatus status) { + switch (status) { + case SUCCEED: + return TransformResult.SUCCESS; + case FAILED_UNSUPPORT: + return TransformResult.UNSUPPORT; + default: + return TransformResult.FAILED; + } + + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TransformResultInfo.java b/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TransformResultInfo.java new file mode 100644 index 000000000..e988e04e7 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/toolbar/TransformResultInfo.java @@ -0,0 +1,64 @@ +package com.fr.nx.app.designer.toolbar; + +import com.fr.locale.InterProviderFactory; +import com.fr.stable.StringUtils; + +/** + * Created by kerry on 2020-01-15 + */ +public class TransformResultInfo { + + private boolean saved; + private TransformResult result; + private final String transformLog; + + public static TransformResultInfo generateResult(TransformResult result, String transformLog) { + return new TransformResultInfo(result, transformLog); + } + + public static TransformResultInfo generateResult(TransformResult result) { + return new TransformResultInfo(result, StringUtils.EMPTY); + } + + + private TransformResultInfo(TransformResult result, String transformLog) { + this.result = result; + this.transformLog = transformLog; + this.saved = false; + } + + public boolean isSaved() { + return saved; + } + + public void setSaved(boolean saved) { + this.saved = saved; + } + + public TransformResultInfo saved(boolean saved) { + setSaved(saved); + return this; + } + + public TransformResult getResult() { + return result; + } + + public void setResult(TransformResult result) { + this.result = result; + } + + public String getTransformLog() { + switch (this.result) { + case FAILED: + return transformLog + "\n" + + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Failed_Tip"); + case SUCCESS: + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Success_Tip"); + case UNSUPPORT: + return transformLog + "\n" + + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Unsupport_Tip"); + } + return transformLog; + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformProgress.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformProgress.java new file mode 100644 index 000000000..7b11cd34c --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformProgress.java @@ -0,0 +1,25 @@ +package com.fr.nx.app.designer.transform; + +/** + * Created by kerry on 2019-12-10 + */ +public class BatchTransformProgress { + private double progress = 0.0D; + private int total = 0; + private int complete = 0; + + public BatchTransformProgress(int total) { + this.total = total; + } + + + public double getProgress() { + return this.progress; + } + + + public void updateProgress() { + this.complete++; + this.progress = this.complete * 1D / this.total; + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformUtil.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformUtil.java new file mode 100644 index 000000000..aef4eed21 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformUtil.java @@ -0,0 +1,25 @@ +package com.fr.nx.app.designer.transform; + +import com.fr.file.filetree.FileNode; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by kerry on 2020-02-14 + */ +public class BatchTransformUtil { + private BatchTransformUtil() { + + } + + public static FileNode[] filterTransformedFile(FileNode[] fileNodes, List transformedList){ + List list = new ArrayList(); + for (FileNode fileNode : fileNodes) { + if (!transformedList.contains(fileNode)) { + list.add(fileNode); + } + } + return list.toArray(new FileNode[list.size()]); + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformer.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformer.java new file mode 100644 index 000000000..0f6a76f83 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/BatchTransformer.java @@ -0,0 +1,71 @@ +package com.fr.nx.app.designer.transform; + +import com.fr.design.utils.concurrent.ThreadFactoryBuilder; +import com.fr.file.filetree.FileNode; +import com.fr.plugin.context.PluginContexts; +import com.fr.nx.app.designer.toolbar.TransformResultInfo; +import com.fr.nx.app.designer.utils.CompileTransformUtil; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; + +/** + * Created by kerry on 2019-12-10 + */ +public class BatchTransformer { + private BatchTransformProgress progress = new BatchTransformProgress(0); + private Map transformResults = new ConcurrentHashMap(); + private UpdateCallBack updateCallBack; + private static final int MAXPOOLSIZE = 5; + private static final String THREAD_NAME_TEMPLATE = "batchtransform-thread-%s"; + private ExecutorService threadPoolExecutor = + PluginContexts.currentContext().newFixedThreadPool(MAXPOOLSIZE, + new ThreadFactoryBuilder().setNameFormat(THREAD_NAME_TEMPLATE).build()); + + public BatchTransformer(UpdateCallBack updateCallBack) { + this.updateCallBack = updateCallBack; + } + + + public void batchTransform(List fileNodes) { + //先清理下 + transformResults.clear(); + progress = new BatchTransformProgress(fileNodes.size()); + for (FileNode fileNode : fileNodes) { + transform(fileNode); + } + } + + private void transform(final FileNode fileNode) { + + threadPoolExecutor.execute(new Runnable() { + @Override + public void run() { + TransformResultInfo transformResult = CompileTransformUtil.compileFile(fileNode); + transformResults.put(fileNode, transformResult); + updateTransformProgress(); + } + }); + } + + private synchronized void updateTransformProgress() { + progress.updateProgress(); + updateCallBack.updateProgress(progress.getProgress()); + } + + public void shutDown() { + if (threadPoolExecutor != null) { + threadPoolExecutor.shutdownNow(); + } + threadPoolExecutor = PluginContexts.currentContext().newFixedThreadPool(MAXPOOLSIZE, + new ThreadFactoryBuilder().setNameFormat(THREAD_NAME_TEMPLATE).build()); + updateCallBack.shutDown(); + } + + public Map getResults() { + return transformResults; + } + +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/UpdateCallBack.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/UpdateCallBack.java new file mode 100644 index 000000000..85e1a1151 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/UpdateCallBack.java @@ -0,0 +1,22 @@ +package com.fr.nx.app.designer.transform; + +/** + * Created by kerry on 2019-12-10 + */ +public interface UpdateCallBack { + /** + * 更新进度 + * @param progress 进度 + */ + void updateProgress(double progress); + + /** + * 进度中断 + */ + void shutDown(); + + /** + * 进度重置 + */ + void reset(); +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/BatchTransformDialog.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/BatchTransformDialog.java new file mode 100644 index 000000000..75b32cb27 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/BatchTransformDialog.java @@ -0,0 +1,61 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.utils.gui.GUICoreUtils; +import com.fr.locale.InterProviderFactory; + +import javax.swing.JDialog; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/** + * Created by kerry on 2019-12-19 + */ +public class BatchTransformDialog extends JDialog implements ActionListener { + private UIButton closeBtn; + + public BatchTransformDialog(Frame parent, JPanel contentPane) { + super(parent, true); + + this.setTitle(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Batch_Transform")); + this.setResizable(false); + JPanel defaultPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); + this.setContentPane(defaultPane); + + closeBtn = new UIButton(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Close")); + closeBtn.addActionListener(this); + JPanel buttonPanel = FRGUIPaneFactory.createRightFlowInnerContainer_S_Pane(); + buttonPanel.add(closeBtn); + + defaultPane.add(contentPane, BorderLayout.CENTER); + defaultPane.add(buttonPanel, BorderLayout.SOUTH); + + addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + dialogExit(); + } + }); + + this.getRootPane().setDefaultButton(closeBtn); + + this.setSize(new Dimension(900, 600)); + GUICoreUtils.centerWindow(this); + } + + @Override + public void actionPerformed(ActionEvent e) { + dialogExit(); + } + + + private void dialogExit() { + this.dispose(); + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/BatchTransformPane.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/BatchTransformPane.java new file mode 100644 index 000000000..c11e4e4ad --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/BatchTransformPane.java @@ -0,0 +1,223 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.base.extension.FileExtension; +import com.fr.design.dialog.BasicPane; +import com.fr.design.file.NodeAuthProcessor; +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.icontainer.UIScrollPane; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.itextfield.UITextField; +import com.fr.design.gui.itree.filetree.FileNodeComparator; +import com.fr.design.gui.itree.filetree.FileNodeConstants; +import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.DesignerFrame; +import com.fr.file.filetree.FileNode; +import com.fr.file.filetree.IOFileNodeFilter; +import com.fr.locale.InterProviderFactory; +import com.fr.log.FineLoggerFactory; +import com.fr.nx.app.designer.toolbar.TransformResultInfo; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.tree.DefaultTreeModel; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Created by kerry on 2019-12-10 + */ +public class BatchTransformPane extends BasicPane { + private UITextField searchField; + private TransformFileTree tree; + private Dialog showDialog; + private TransformPreparePane preparePane; + private TransformResultPane resultPane; + + + public BatchTransformPane() { + DesignerFrame designerFrame = DesignerContext.getDesignerFrame(); + this.showDialog = new BatchTransformDialog(designerFrame, this); + initPane(); + } + + private void initPane() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + initNorthPane(); + initSouthPane(); + } + + private void initNorthPane() { + UILabel northTip = new UILabel(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Tip")); + northTip.setForeground(Color.decode("#8F8F92")); + northTip.setBorder(BorderFactory.createEmptyBorder(0, 550, 10, 10)); + this.add(northTip, BorderLayout.NORTH); + } + + private void initSouthPane() { + preparePane = new TransformPreparePane(this.showDialog, this); + tree = new TransformFileTree(preparePane); + this.add(preparePane, BorderLayout.CENTER); + initLeftPane(); + initRightPane(); + } + + private void initLeftPane() { + IOFileNodeFilter filter = new IOFileNodeFilter(new String[]{FileExtension.CPT.getSuffix()}); + tree.setDigIn(true); + tree.setFileNodeFilter(filter); + UIScrollPane scrollPane = new UIScrollPane(tree); + scrollPane.setPreferredSize(new Dimension(320, 442)); + scrollPane.setBorder(BorderFactory.createEmptyBorder(12, 0, 0, 0)); + tree.refreshEnv(); + JPanel selectPane = FRGUIPaneFactory.createTitledBorderPaneCenter( + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Select_Template")); + JPanel searchPane = initSearchPane(); + JPanel jPanel = FRGUIPaneFactory.createBorderLayout_S_Pane(); + jPanel.add(searchPane, BorderLayout.NORTH); + jPanel.add(scrollPane, BorderLayout.WEST); + jPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + selectPane.add(jPanel); + selectPane.setPreferredSize(new Dimension(330, 501)); + this.add(selectPane, BorderLayout.WEST); + } + + + private void initRightPane() { + resultPane = new TransformResultPane(); + this.add(resultPane, BorderLayout.EAST); + } + + private JPanel initSearchPane() { + JPanel jPanel = FRGUIPaneFactory.createNormalFlowInnerContainer_M_Pane(); + jPanel.setBorder(BorderFactory.createEmptyBorder()); + searchField = new UITextField(); + searchField.requestFocus(); + searchField.addKeyListener(keyFieldKeyListener); + searchField.setPreferredSize(new Dimension(261, 20)); + jPanel.add(searchField); + UIButton searchBtn = new UIButton(com.fr.design.i18n.Toolkit.i18nText("Fine-Design_Basic_Search")); + searchBtn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + search(); + } + }); + searchBtn.setPreferredSize(new Dimension(44, 20)); + jPanel.add(searchBtn); + return jPanel; + } + + + private KeyAdapter keyFieldKeyListener = new KeyAdapter() { + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ENTER) { + search(); + e.consume(); + } + } + }; + + + + private void search() { + //重新构建TreeModel + filter(searchField.getText()); + ExpandMutableTreeNode rootNode = (ExpandMutableTreeNode) tree.getModel().getRoot(); + ((DefaultTreeModel) tree.getModel()).reload(rootNode); + // 展开所有isExpanded为true的TreeNode + rootNode.expandCurrentTreeNode(tree); + } + + public void filter(String filterString) { + NodeAuthProcessor.getInstance().refresh(); + DefaultTreeModel defaultTreeModel = (DefaultTreeModel) tree.getModel(); + ExpandMutableTreeNode rootTreeNode = (ExpandMutableTreeNode) defaultTreeModel.getRoot(); + NodeAuthProcessor.getInstance().fixTreeNodeAuth(rootTreeNode); + filter(rootTreeNode, filterString); + defaultTreeModel.reload(rootTreeNode); + } + + private void filter(ExpandMutableTreeNode rootTreeNode, String filterString) { + rootTreeNode.removeAllChildren(); + + FileNode[] fns; + fns = listFileNodes(rootTreeNode); + + ExpandMutableTreeNode[] subTreeNodes = NodeAuthProcessor.getInstance().parser2TreeNodeArray(fns); + + for (ExpandMutableTreeNode node : subTreeNodes) { + filter(node, filterString); + if (node.getChildCount() > 0 || (node.getChildCount() == 0 && node.toString().contains(filterString))) { + node.setExpanded(true); + rootTreeNode.add(node); + } + } + + } + + private FileNode[] listFileNodes(ExpandMutableTreeNode rootTreeNode) { + if (rootTreeNode == null) { + return new FileNode[0]; + } + + Object object = rootTreeNode.getUserObject(); + if (!(object instanceof FileNode)) { + return new FileNode[0]; + } + + FileNode[] fileNodes = null; + try { + fileNodes = tree.listFile(((FileNode) object).getEnvPath()); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + if (fileNodes == null) { + fileNodes = new FileNode[0]; + } + Arrays.sort(fileNodes, new FileNodeComparator(FileNodeConstants.getSupportFileTypes())); + return fileNodes; + } + + public void resetFilePaths(Map resultMap) { + this.tree.resetSelectedPaths(); + Iterator iterator = resultMap.keySet().iterator(); + List list = new ArrayList(); + while (iterator.hasNext()){ + FileNode node = iterator.next(); + list.add(node); + } + this.tree.addTransformedList(list); + this.tree.refresh(); + resultPane.populate(resultMap); + } + + public void removeSelectedNode(FileNode fileNode) { + String path = fileNode.getEnvPath(); + this.tree.removeSelectedPath(path); + } + + + @Override + protected String title4PopupWindow() { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Batch_Transform"); + } + + public Dialog showDialog() { + return this.showDialog; + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/PrepareTransformFileList.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/PrepareTransformFileList.java new file mode 100644 index 000000000..22d6afcfd --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/PrepareTransformFileList.java @@ -0,0 +1,79 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.base.BaseUtils; +import com.fr.design.constants.UIConstants; +import com.fr.design.gui.ilist.UIList; +import com.fr.design.gui.itree.filetree.FileTreeIcon; +import com.fr.file.filetree.FileNode; + +import javax.swing.DefaultListModel; +import javax.swing.Icon; +import javax.swing.JList; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; +import java.awt.Rectangle; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +/** + * Created by kerry on 2020-01-13 + */ +public class PrepareTransformFileList extends UIList { + private TransformPreparePane transformingPane; + private static final int DELETE_RANGE = 20; + + + public PrepareTransformFileList(final TransformPreparePane transformingPane) { + super(); + this.transformingPane = transformingPane; + this.setBackground(UIConstants.TREE_BACKGROUND); + this.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); + DefaultListModel listModel = new DefaultListModel(); + this.setModel(listModel); + this.setCellRenderer(new UIListControlCellRenderer() { + @Override + protected Icon getLeftLabelIcon(Object value) { + if (value instanceof FileNode) { + return FileTreeIcon.getIcon((FileNode) value); + } + return null; + } + + @Override + protected Icon getRightLabelIcon(Object value) { + return BaseUtils.readIcon("/com/fr/nx/app/designer/transform/tab_close.png"); + } + }); + this.addMouseListener(getListMouseListener()); + } + + + private MouseListener getListMouseListener() { + return new MouseAdapter() { + @Override + public void mouseReleased(MouseEvent evt) { + JList list = (JList) evt.getSource(); + int selectedIndex = list.getSelectedIndex(); + Rectangle rectangle = list.getCellBounds(selectedIndex, selectedIndex); + if (SwingUtilities.isLeftMouseButton(evt) + && pointInSelected(rectangle, evt.getX(), evt.getY())) { + Object value = PrepareTransformFileList.this.getSelectedValue(); + if (value != null) { + transformingPane.removeSelectedNode((FileNode) value); + } + } + } + }; + } + + private boolean pointInSelected(Rectangle rectangle, int x, int y) { + if (rectangle == null) { + return false; + } + return x >= rectangle.x + rectangle.width - DELETE_RANGE && + x <= rectangle.x + rectangle.width && + y >= rectangle.y && y <= rectangle.y + rectangle.height; + } +} + diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformFileTree.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformFileTree.java new file mode 100644 index 000000000..cbfcd253f --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformFileTree.java @@ -0,0 +1,204 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.base.FRContext; +import com.fr.base.extension.FileExtension; +import com.fr.design.gui.itree.checkboxtree.CheckBoxTree; +import com.fr.design.gui.itree.checkboxtree.CheckBoxTreeCellRenderer; +import com.fr.design.gui.itree.checkboxtree.CheckBoxTreeSelectionModel; +import com.fr.design.gui.itree.refreshabletree.ExpandMutableTreeNode; +import com.fr.design.remote.ui.tree.FileAuthorityTree; +import com.fr.file.filetree.FileNode; +import com.fr.general.ComparatorUtils; +import com.fr.nx.app.designer.transform.BatchTransformUtil; + +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreePath; +import java.awt.AlphaComposite; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Created by kerry on 2020-01-13 + */ +public class TransformFileTree extends FileAuthorityTree { + private TransformPreparePane preparePane; + private static final float CHECKBOX_ENABLE_OPACITY = 0.4f; + + private List transformedList = new ArrayList(); + + public TransformFileTree(TransformPreparePane preparePane) { + this.preparePane = preparePane; + } + + public void addTransformedList(List removeList) { + this.transformedList.addAll(removeList); + } + + @Override + protected CheckBoxTree.Handler createHandler() { + return new CheckBoxTree.Handler(this) { + @Override + public void mousePressed(MouseEvent e) { + super.mousePressed(e); + TreePath treePath = this.getTreePathForMouseEvent(e); + if (treePath == null) { + return; + } + CheckBoxTreeSelectionModel selectionModel = _tree.getCheckBoxTreeSelectionModel(); + boolean selected = selectionModel.isPathSelected(treePath, selectionModel.isDigIn()); + ExpandMutableTreeNode lastPathComponent = (ExpandMutableTreeNode) treePath.getLastPathComponent(); + List selectedFileNodes = filterBatchFileNode(lastPathComponent); + preparePane.removeTransformNode(selectedFileNodes); + if (selected) { + preparePane.addTransformNode(selectedFileNodes); + } + + } + }; + } + + + private List filterBatchFileNode(ExpandMutableTreeNode treeNode) { + List fileNodeList = new ArrayList<>(); + int childCount = treeNode.getChildCount(); + if (childCount > 0) { + loadPendingChildTreeNode(treeNode); + int expandChildCount = treeNode.getChildCount(); + for (int i = 0; i < expandChildCount; i++) { + fileNodeList.addAll(filterBatchFileNode((ExpandMutableTreeNode) treeNode.getChildAt(i))); + } + } else { + FileNode fileNode = (FileNode) treeNode.getUserObject(); + boolean locked = fileNodeLocked(treeNode); + if (!fileNode.isDirectory() && !locked) { + fileNodeList.add((FileNode) treeNode.getUserObject()); + } + } + return fileNodeList; + } + + + private boolean fileNodeLocked(ExpandMutableTreeNode treeNode) { + Object userObj = treeNode.getUserObject(); + if (userObj instanceof FileNode) { + FileNode node = (FileNode) userObj; + String lock = node.getLock(); + if (lock != null && !node.getUserID().equals(lock)) { + return true; + } + } + return false; + } + + public void resetSelectedPaths() { + CheckBoxTreeSelectionModel selectionModel = this.getCheckBoxTreeSelectionModel(); + selectionModel.clearSelection(); + } + + @Override + public void selectCheckBoxPaths(String[] paths) { + if (paths == null || paths.length == 0) { + return; + } + + DefaultTreeModel model = (DefaultTreeModel) this.getModel(); + ExpandMutableTreeNode treeNode = (ExpandMutableTreeNode) model.getRoot(); + List res = new ArrayList<>(); + for (String path : paths) { + TreePath treePath = getSelectedPath(treeNode, path); + if (treePath != null) { + res.add(treePath); + } + } + // 勾选中这些结点 + this.getCheckBoxTreeSelectionModel().setSelectionPaths(res.toArray(new TreePath[0])); + } + + private TreePath getSelectedPath(ExpandMutableTreeNode treeNode, String path) { + //可以只在选中的path中选择 + for (int i = 0, len = treeNode.getChildCount(); i < len; i++) { + ExpandMutableTreeNode childTreeNode = (ExpandMutableTreeNode) treeNode.getChildAt(i); + if (childTreeNode.getChildCount() > 0) { + loadPendingChildTreeNode(childTreeNode); + TreePath treePath = getSelectedPath(childTreeNode, path); + if (treePath != null) { + return treePath; + } + } + if (!(childTreeNode.getUserObject() instanceof FileNode)) { + continue; + } + FileNode fileNode = (FileNode) childTreeNode.getUserObject(); + if (ComparatorUtils.equals(path, fileNode.getEnvPath())) { + DefaultTreeModel model = (DefaultTreeModel) this.getModel(); + return new TreePath(model.getPathToRoot(childTreeNode)); + } + } + return null; + } + + public void removeSelectedPath(String path) { + if (path == null) { + return; + } + DefaultTreeModel model = (DefaultTreeModel) this.getModel(); + ExpandMutableTreeNode treeNode = (ExpandMutableTreeNode) model.getRoot(); + TreePath selectedPath = getSelectedPath(treeNode, path); + if (selectedPath != null) { + this.getCheckBoxTreeSelectionModel().removeSelectionPath(selectedPath); + + } + } + + + @Override + public FileNode[] listFile(String path) { + // 支持插件扩展, 先从env的filter拿, 再从插件拿 + Set supportTypes = new HashSet(); + for (String temp : this.filter.getSupportedTypes()) { + supportTypes.add(FileExtension.parse(temp)); + } + FileNode[] fileNodes = FRContext.getFileNodes().list( + path, + supportTypes.toArray(new FileExtension[supportTypes.size()]) + ); + return BatchTransformUtil.filterTransformedFile(fileNodes, transformedList); + } + + @Override + public boolean isCheckBoxEnabled(TreePath path) { + ExpandMutableTreeNode treeNode = (ExpandMutableTreeNode) path.getLastPathComponent(); + return !fileNodeLocked(treeNode); + } + + @Override + protected CheckBoxTreeCellRenderer createCellRenderer(TreeCellRenderer renderer) { + final CheckBoxTreeCellRenderer checkBoxTreeCellRenderer = new CheckBoxTreeCellRenderer(renderer) { + @Override + public void paint(Graphics g) { + if (!_checkBox.isEnabled()) { + AlphaComposite composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, CHECKBOX_ENABLE_OPACITY); + ((Graphics2D) g).setComposite(composite); + } + super.paint(g); + } + }; + addPropertyChangeListener(CELL_RENDERER_PROPERTY, new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + checkBoxTreeCellRenderer.setActualTreeRenderer((TreeCellRenderer) evt.getNewValue()); + } + }); + return checkBoxTreeCellRenderer; + } + + +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformPreparePane.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformPreparePane.java new file mode 100644 index 000000000..efbded1b0 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformPreparePane.java @@ -0,0 +1,130 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.gui.icontainer.UIScrollPane; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.file.filetree.FileNode; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.app.designer.toolbar.TransformResultInfo; +import com.fr.nx.app.designer.transform.BatchTransformer; +import com.fr.nx.app.designer.transform.UpdateCallBack; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.DefaultListModel; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Created by kerry on 2020-01-13 + */ +public class TransformPreparePane extends JPanel { + private static final String INITIAL_DISPLAY_TEXT = "0"; + private PrepareTransformFileList fileList; + private List selectedFileNodes; + private UpdateProgressDialog dialog; + private BatchTransformer transformer; + private UpdateCallBack updateProgressPane; + private Dialog parentDialog; + private UILabel transCountLabel; + private BatchTransformPane batchTransformPane; + private UIButton startTransform; + + public TransformPreparePane(Dialog parentDialog, BatchTransformPane batchTransformPane) { + this.batchTransformPane = batchTransformPane; + this.parentDialog = parentDialog; + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + updateProgressPane = new UpdateProgressPane(this); + transformer = new BatchTransformer(updateProgressPane); + fileList = new PrepareTransformFileList(this); + selectedFileNodes = new ArrayList<>(); + initPane(); + } + + private void initPane() { + JPanel transformPane = FRGUIPaneFactory.createTitledBorderPaneCenter( + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Template_To_Transform")); + transformPane.setPreferredSize(new Dimension(270, 501)); + startTransform = new UIButton(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Start")); + startTransform.setEnabled(false); + addStartTransformListener(startTransform); + transCountLabel = new UILabel(INITIAL_DISPLAY_TEXT); + JPanel northPane = FRGUIPaneFactory.createLeftFlowZeroGapBorderPane(); + northPane.add(startTransform); + northPane.add(Box.createHorizontalStrut(160)); + northPane.add(transCountLabel); + transformPane.add(northPane, BorderLayout.NORTH); + UIScrollPane scrollPane = new UIScrollPane(fileList); + scrollPane.setPreferredSize(new Dimension(260, 442)); + scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0)); + transformPane.add(scrollPane, BorderLayout.CENTER); + this.add(transformPane, BorderLayout.CENTER); + } + + private void addStartTransformListener(UIButton startTransform) { + startTransform.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + //开始转换 + transformer.batchTransform(selectedFileNodes); + displayProgressPane(); + } + + + private void displayProgressPane() { + dialog = new UpdateProgressDialog(transformer, parentDialog, (JPanel) updateProgressPane); + dialog.setVisible(true); + } + }); + } + + public void addTransformNode(List fileNodeList) { + selectedFileNodes.addAll(fileNodeList); + buildFileList(); + + } + + public void removeTransformNode(List fileNodeList) { + selectedFileNodes.removeAll(fileNodeList); + buildFileList(); + } + + public void removeSelectedNode(FileNode fileNode) { + selectedFileNodes.remove(fileNode); + buildFileList(); + this.batchTransformPane.removeSelectedNode(fileNode); + } + + private void buildFileList() { + DefaultListModel listModel = new DefaultListModel(); + int count = selectedFileNodes.size(); + for (int i = count - 1; i >= 0; i--) { + listModel.addElement(selectedFileNodes.get(i)); + } + this.fileList.setModel(listModel); + startTransform.setEnabled(selectedFileNodes.size() > 0); + transCountLabel.setText(String.valueOf(selectedFileNodes.size())); + } + + public void complete() { + dialog.dialogExit(); + updateProgressPane.reset(); + Map resultMap = transformer.getResults(); + resetFileNodeList(); + this.batchTransformPane.resetFilePaths(resultMap); + } + + private void resetFileNodeList(){ + this.selectedFileNodes.clear(); + buildFileList(); + } + +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformResultList.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformResultList.java new file mode 100644 index 000000000..83eb53400 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformResultList.java @@ -0,0 +1,70 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.base.BaseUtils; +import com.fr.design.constants.UIConstants; +import com.fr.design.gui.ilist.UIList; +import com.fr.design.gui.itree.filetree.FileTreeIcon; +import com.fr.file.filetree.FileNode; +import com.fr.nx.app.designer.toolbar.TransformResultInfo; + +import javax.swing.DefaultListModel; +import javax.swing.Icon; +import javax.swing.ListSelectionModel; +import java.util.Iterator; +import java.util.Map; + +/** + * Created by kerry on 2020-01-13 + */ +public class TransformResultList extends UIList { + private Map resultMap; + private static final Icon FAILED_ICON = BaseUtils.readIcon("/com/fr/nx/app/designer/transform/transform_failed.png"); + private static final Icon SUCCESS_ICON = BaseUtils.readIcon("/com/fr/nx/app/designer/transform/transform_success.png"); + private static final Icon UNSUPPORT_ICON = BaseUtils.readIcon("/com/fr/nx/app/designer/transform/transform_unsupport.png"); + + public TransformResultList() { + super(); + this.setBackground(UIConstants.TREE_BACKGROUND); + this.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); + DefaultListModel listModel = new DefaultListModel(); + this.setModel(listModel); + this.setCellRenderer(new UIListControlCellRenderer() { + @Override + protected Icon getLeftLabelIcon(Object value) { + if (value instanceof FileNode) { + return FileTreeIcon.getIcon((FileNode) value); + } + return null; + } + + @Override + protected Icon getRightLabelIcon(Object value) { + if (value != null) { + TransformResultInfo resultInfo = resultMap.get(value); + switch (resultInfo.getResult()) { + case FAILED: + return FAILED_ICON; + case SUCCESS: + return SUCCESS_ICON; + case UNSUPPORT: + return UNSUPPORT_ICON; + } + } + return null; + } + }); + } + + public void populate(Map resultMap) { + this.resultMap = resultMap; + DefaultListModel listModel = new DefaultListModel(); + Iterator> iterator = resultMap.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + FileNode key = entry.getKey(); + listModel.addElement(key); + } + this.setModel(listModel); + } + +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformResultPane.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformResultPane.java new file mode 100644 index 000000000..6bed5aafc --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/TransformResultPane.java @@ -0,0 +1,110 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.design.gui.icontainer.UIScrollPane; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.itextarea.UITextArea; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.file.filetree.FileNode; +import com.fr.general.ComparatorUtils; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.app.designer.toolbar.TransformResult; +import com.fr.nx.app.designer.toolbar.TransformResultInfo; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.util.Iterator; +import java.util.Map; + +/** + * Created by kerry on 2020-01-10 + */ +public class TransformResultPane extends JPanel { + private TransformResultList resultList; + private UITextArea resultInfo; + private Map resultMap; + private UILabel successTip; + + public TransformResultPane() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + initPane(); + } + + private void initPane() { + JPanel resultPane = FRGUIPaneFactory.createTitledBorderPaneCenter( + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Result")); + JPanel northPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + resultList = new TransformResultList(); + addValueChangeListener(); + UIScrollPane scrollPane = new UIScrollPane(resultList); + scrollPane.setPreferredSize(new Dimension(260, 260)); + scrollPane.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 0)); + successTip = new UILabel(""); + successTip.setBorder(BorderFactory.createEmptyBorder(5, 150, 5, 10)); + northPane.add(successTip, BorderLayout.NORTH); + northPane.add(scrollPane, BorderLayout.CENTER); + resultPane.add(northPane); + + JPanel resultInfoPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + resultInfoPane.setPreferredSize(new Dimension(270, 165)); + resultInfoPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 10, 5)); + UILabel transformLogLabel = new UILabel( + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Logs")); + transformLogLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0)); + resultInfoPane.add(transformLogLabel, BorderLayout.NORTH); + resultInfo = new UITextArea(); + resultInfo.setBackground(Color.white); + resultInfo.setLineWrap(true); + resultInfo.setWrapStyleWord(true); + resultInfo.setEditable(false); + UIScrollPane resultScrollPane = new UIScrollPane(resultInfo); + resultInfoPane.add(resultScrollPane, BorderLayout.CENTER); + this.add(resultPane, BorderLayout.NORTH); + this.add(resultInfoPane, BorderLayout.CENTER); + } + + private void addValueChangeListener() { + resultList.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + FileNode selectedValue = (FileNode) resultList.getSelectedValue(); + if (resultInfo != null && selectedValue != null) { + TransformResultInfo result = resultMap.get(selectedValue); + resultInfo.setText(result.getTransformLog()); + } + } + }); + } + + public void populate(Map resultMap) { + int count = getSuccessCount(resultMap); + String tip = InterProviderFactory.getProvider().getLocText( + "Fine-Plugin_Engine_Transform_Result_Tip", String.valueOf(count)); + successTip.setText(tip); + this.resultMap = resultMap; + resultList.populate(resultMap); + if (resultList.getModel().getSize() > 0) { + resultList.setSelectedIndex(0); + FileNode firstResult = (FileNode) resultList.getModel().getElementAt(0); + resultInfo.setText(resultMap.get(firstResult).getTransformLog()); + } + } + + private int getSuccessCount(Map resultMap) { + int count = 0; + Iterator> iterator = resultMap.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + TransformResultInfo resultInfo = entry.getValue(); + if (ComparatorUtils.equals(TransformResult.SUCCESS, resultInfo.getResult())) { + count++; + } + } + return count; + } + +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UIListControlCellRenderer.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UIListControlCellRenderer.java new file mode 100644 index 000000000..47f677238 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UIListControlCellRenderer.java @@ -0,0 +1,86 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.stable.StringUtils; +import sun.swing.DefaultLookup; + +import javax.swing.Icon; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.ListCellRenderer; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; + +/** + * Created by kerry on 2020-01-14 + */ +public class UIListControlCellRenderer extends JPanel implements ListCellRenderer { + private UILabel content; + private UILabel controlLabel; + private Color initialLabelForeground; + + public UIListControlCellRenderer() { + initPane(); + } + + private void initPane() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + content = new UILabel(); + initialLabelForeground = content.getForeground(); + controlLabel = new UILabel(); + controlLabel.setPreferredSize(new Dimension(16, 20)); + this.add(content, BorderLayout.CENTER); + this.add(controlLabel, BorderLayout.EAST); + } + + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + setComponentOrientation(list.getComponentOrientation()); + Color bg = null; + Color fg = null; + + JList.DropLocation dropLocation = list.getDropLocation(); + if (dropLocation != null + && !dropLocation.isInsert() + && dropLocation.getIndex() == index) { + + bg = DefaultLookup.getColor(this, ui, "List.dropCellBackground"); + fg = DefaultLookup.getColor(this, ui, "List.dropCellForeground"); + + isSelected = true; + } + + if (isSelected) { + setBackground(bg == null ? list.getSelectionBackground() : bg); + setForeground(fg == null ? list.getSelectionForeground() : fg); + content.setForeground(Color.WHITE); + + } else { + setBackground(list.getBackground()); + setForeground(list.getForeground()); + content.setForeground(initialLabelForeground); + } + + content.setText((value == null) ? StringUtils.EMPTY : value.toString()); + Icon leftLabelIcon = getLeftLabelIcon(value); + if (leftLabelIcon != null) { + content.setIcon(leftLabelIcon); + } + Icon rightLabelIcon = getRightLabelIcon(value); + if (rightLabelIcon != null) { + controlLabel.setIcon(rightLabelIcon); + } + return this; + } + + protected Icon getLeftLabelIcon(Object value) { + return null; + } + + protected Icon getRightLabelIcon(Object value) { + return null; + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UpdateProgressDialog.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UpdateProgressDialog.java new file mode 100644 index 000000000..6d9bb009b --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UpdateProgressDialog.java @@ -0,0 +1,70 @@ +package com.fr.nx.app.designer.transform.ui; + +import com.fr.design.gui.ibutton.UIButton; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.utils.gui.GUICoreUtils; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.app.designer.transform.BatchTransformer; + +import javax.swing.JDialog; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Dialog; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; + +/** + * Created by kerry on 2019-12-19 + */ +public class UpdateProgressDialog extends JDialog implements ActionListener { + + private UIButton pauseBtn; + private BatchTransformer transformer; + + public UpdateProgressDialog(BatchTransformer transformer, Dialog parent, JPanel contentPane) { + super(parent, true); + this.transformer = transformer; + initPane(contentPane); + } + + private void initPane(JPanel contentPane) { + this.setTitle(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Format_Transform")); + this.setResizable(false); + JPanel defaultPane = FRGUIPaneFactory.createBorderLayout_L_Pane(); + this.setContentPane(defaultPane); + + pauseBtn = new UIButton(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Suspend")); + pauseBtn.addActionListener(this); + JPanel buttonPanel = FRGUIPaneFactory.createRightFlowInnerContainer_S_Pane(); + buttonPanel.add(pauseBtn); + + defaultPane.add(contentPane, BorderLayout.CENTER); + defaultPane.add(buttonPanel, BorderLayout.SOUTH); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + transformer.shutDown(); + dialogExit(); + } + }); + + this.getRootPane().setDefaultButton(pauseBtn); + + this.setSize(new Dimension(262, 122)); + GUICoreUtils.centerWindow(this); + } + + @Override + public void actionPerformed(ActionEvent e) { + transformer.shutDown(); + dialogExit(); + } + + public void dialogExit() { + this.dispose(); + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UpdateProgressPane.java b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UpdateProgressPane.java new file mode 100644 index 000000000..5fb57c369 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/transform/ui/UpdateProgressPane.java @@ -0,0 +1,72 @@ +package com.fr.nx.app.designer.transform.ui; + + +import com.fr.base.BaseUtils; +import com.fr.decision.update.data.UpdateConstants; +import com.fr.design.dialog.BasicPane; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.general.ComparatorUtils; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.app.designer.transform.UpdateCallBack; +import com.sun.java.swing.plaf.motif.MotifProgressBarUI; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import java.awt.BorderLayout; +import java.awt.Dimension; + + +/** + * Created by kerry on 2019-12-11 + */ +public class UpdateProgressPane extends BasicPane implements UpdateCallBack { + private TransformPreparePane contentPane; + private JProgressBar progressBar; + + public UpdateProgressPane(TransformPreparePane contentPane) { + this.contentPane = contentPane; + initPane(); + } + + private void initPane() { + this.setPreferredSize(new Dimension(262, 60)); + UILabel icon = new UILabel(BaseUtils.readIcon("/com/fr/nx/app/designer/transform/transforming.png")); + icon.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10)); + this.add(icon, BorderLayout.WEST); + JPanel centerPane = FRGUIPaneFactory.createBorderLayout_S_Pane(); + UILabel title = new UILabel(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transforming")); + title.setBorder(BorderFactory.createEmptyBorder(0, 0, 8, 0)); + centerPane.add(title, BorderLayout.NORTH); + progressBar = new JProgressBar(); + progressBar.setUI(new MotifProgressBarUI()); + progressBar.setForeground(UpdateConstants.BAR_COLOR); + centerPane.add(progressBar, BorderLayout.CENTER); + this.add(centerPane, BorderLayout.CENTER); + } + + + @Override + public void updateProgress(double progress) { + progressBar.setValue((int) (progress * 100)); + if (ComparatorUtils.equals(progress, 1D)) { + contentPane.complete(); + } + } + + @Override + public void shutDown() { + contentPane.complete(); + } + + @Override + public void reset() { + progressBar.setValue(0); + } + + @Override + protected String title4PopupWindow() { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Suspend"); + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/utils/CompileTransformUtil.java b/designer-base/src/main/java/com/fr/nx/app/designer/utils/CompileTransformUtil.java new file mode 100644 index 000000000..3de788cd1 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/utils/CompileTransformUtil.java @@ -0,0 +1,55 @@ +package com.fr.nx.app.designer.utils; + +import com.fr.design.mainframe.JTemplate; +import com.fr.file.FILE; +import com.fr.file.filetree.FileNode; +import com.fr.general.ComparatorUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.main.impl.WorkBook; +import com.fr.nx.app.designer.toolbar.TemplateTransformer; +import com.fr.nx.app.designer.toolbar.TransformResult; +import com.fr.nx.app.designer.toolbar.TransformResultInfo; +import com.fr.workspace.WorkContext; + +import java.io.InputStream; + +import static com.fr.base.extension.FileExtension.CPT; +import static com.fr.base.extension.FileExtension.CPTX; + +/** + * Created by kerry on 2019-12-04 + */ +public class CompileTransformUtil { + + + public static TransformResultInfo compileFile(FileNode fileNode) { + long start = System.currentTimeMillis(); + InputStream in = WorkContext.getWorkResource().openStream(fileNode.getEnvPath()); + WorkBook workBook = new WorkBook(); + TransformResultInfo result = null; + try { + workBook.readStream(in); + FILE file = TemplateTransformer.createOutputFile(fileNode.getEnvPath(), CPT.getSuffix(), CPTX.getSuffix()); + result = TemplateTransformer.compileCPTX(workBook, file); + } catch (Exception ignore) { + result = TransformResultInfo.generateResult(TransformResult.FAILED); + } + long end = System.currentTimeMillis(); + FineLoggerFactory.getLogger().debug(fileNode.getName() + " compile cost : " + (end - start) +" ms "); + return result; + } + + + public static FILE getTargetFile(JTemplate jTemplate, TemplateTransformer transformer) { + FILE file = null; + if (ComparatorUtils.equals(TemplateTransformer.TO_CPTX, transformer)) { + file = TemplateTransformer.createOutputFile(jTemplate.getEditingFILE().getPath(), + CPT.getSuffix(), CPTX.getSuffix()); + + } else if (ComparatorUtils.equals(TemplateTransformer.TO_CPT, transformer)) { + file = TemplateTransformer.createOutputFile(jTemplate.getEditingFILE().getPath(), + CPTX.getSuffix(), CPT.getSuffix()); + } + return file; + } +} diff --git a/designer-base/src/main/java/com/fr/nx/app/designer/utils/DesignerCptxFileUtils.java b/designer-base/src/main/java/com/fr/nx/app/designer/utils/DesignerCptxFileUtils.java new file mode 100644 index 000000000..a51e04768 --- /dev/null +++ b/designer-base/src/main/java/com/fr/nx/app/designer/utils/DesignerCptxFileUtils.java @@ -0,0 +1,79 @@ +package com.fr.nx.app.designer.utils; + +import com.fr.file.FILE; +import com.fr.general.CommonIOUtils; +import com.fr.log.FineLoggerFactory; +import com.fr.main.impl.WorkBook; +import com.fr.nx.marshal.impl.xml.DefaultXMLObjectUnmarshaler; +import com.fr.nx.marshal.util.MarshalUtil; +import com.fr.nx.cptx.entry.metadata.CptxMetadata; +import com.fr.nx.cptx.io.handle.impl.OriginCptProvider; +import com.fr.nx.cptx.marshal.util.CptxMarshalUtil; +import com.fr.nx.cptx.utils.CptxFileUtils; +import com.fr.stable.EncodeConstants; +import com.fr.stable.StableUtils; +import com.fr.stable.xml.XMLableReader; +import com.fr.workspace.WorkContext; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; + +/** + * Created by loy on 2021/1/18. + * + *

用于设计器的cptx的一些工具类 + *

+ */ +public class DesignerCptxFileUtils { + + /** + * 根据file来获取cptx中的cpt + * @param file + * @return + */ + public static WorkBook getWorkBook(final FILE file){ + try (InputStream input = file.asInputStream(); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { + CommonIOUtils.copyBinaryTo(input, outputStream); + OriginCptProvider provider = new OriginCptProvider(outputStream.toByteArray()); + return CptxFileUtils.getWorkBook(provider); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return null; + } + + /** + * 获取模板的元数据 + * @param file + * @return + */ + public static CptxMetadata getMetadata(FILE file){ + try { + String metadataPath = StableUtils.pathJoin(generateCompileDir(file), CptxMarshalUtil.NAME_METADATA); + if(!WorkContext.getWorkResource().exist(metadataPath)){ + return null; + } + InputStream metadataInput = WorkContext.getWorkResource().openStream(metadataPath); + XMLableReader reader = XMLableReader.createXMLableReader( + new InputStreamReader(metadataInput, EncodeConstants.ENCODING_UTF_8)); + CptxMetadata metadata = (CptxMetadata) new DefaultXMLObjectUnmarshaler().unmarshal( + MarshalUtil.createMarshalableExtra(CptxMetadata.class), reader); + return metadata; + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return null; + } + + /** + * 根据文件获取编译后的文件夹绝对路径,比如原文件位于reportlets/test/merge.cptx,转化后为evn://assets/engine/test/merge文件夹 + * @param file + * @return + */ + public static String generateCompileDir(FILE file){ + String filePath = file.getPath(); + return CptxFileUtils.generateCompileDir(filePath); + } +} diff --git a/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TemplateTransformerDebugTest.java b/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TemplateTransformerDebugTest.java new file mode 100644 index 000000000..6a8f8247b --- /dev/null +++ b/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TemplateTransformerDebugTest.java @@ -0,0 +1,239 @@ +package com.fr.nx.app.designer.toolbar; + +import com.fr.base.Parameter; +import com.fr.base.chart.BaseChartCollection; +import com.fr.base.io.XMLEncryptKey; +import com.fr.base.io.XMLEncryptUtils; +import com.fr.base.iofile.IOFileAttrMarkManager; +import com.fr.base.parameter.ParameterUI; +import com.fr.chart.chartattr.ChartCollection; +import com.fr.config.dao.DaoContext; +import com.fr.config.dao.impl.LocalClassHelperDao; +import com.fr.config.dao.impl.LocalEntityDao; +import com.fr.config.dao.impl.LocalXmlEntityDao; +import com.fr.file.MemFILE; +import com.fr.form.DefaultFormOperator; +import com.fr.form.FormOperator; +import com.fr.form.main.ExtraFormClassManager; +import com.fr.form.main.Form; +import com.fr.form.main.FormHyperlink; +import com.fr.form.main.parameter.FormParameterUI; +import com.fr.form.parameter.FormSubmitButton; +import com.fr.form.plugin.DefaultSwitcherImpl; +import com.fr.general.xml.GeneralXMLTools; +import com.fr.io.EncryptUtils; +import com.fr.js.FormHyperlinkProvider; +import com.fr.main.impl.WorkBook; +import com.fr.nx.calculable.Calculable; +import com.fr.nx.calculable.type.ConstantCalculable; +import com.fr.nx.cell.CellKey; +import com.fr.nx.cell.CellTemplate; +import com.fr.nx.cptx.cache.CptxTemplatePool; +import com.fr.nx.cptx.entry.CptxTemplate; +import com.fr.nx.cptx.resource.ImageResourceRef; +import com.fr.page.BaseSinglePagePrintable; +import com.fr.page.BaseSingleReportCache; +import com.fr.page.ClippedChartPage; +import com.fr.page.ClippedECPage; +import com.fr.page.ClippedPageProvider; +import com.fr.page.PDF2Painter; +import com.fr.page.PageGeneratorProvider; +import com.fr.page.PagePainter; +import com.fr.page.PagePainterProvider; +import com.fr.page.PageSetChainProvider; +import com.fr.page.PageSetProvider; +import com.fr.page.PageXmlOperator; +import com.fr.page.PageXmlProvider; +import com.fr.page.PaperSettingProvider; +import com.fr.page.ReportPage; +import com.fr.page.ReportPageAttrProvider; +import com.fr.page.ReportPageProvider; +import com.fr.page.SheetPage; +import com.fr.page.SinglePagePrintable; +import com.fr.page.SingleReportCache; +import com.fr.page.generator.PaginateReportPageGenerator; +import com.fr.page.generator.PolyReportPageGenerator; +import com.fr.page.generator.SheetPageGenerator; +import com.fr.page.pageset.ArrayPageSet; +import com.fr.page.pageset.PageSetChain; +import com.fr.page.stable.PaperSetting; +import com.fr.page.stable.ReportPageAttr; +import com.fr.plugin.attr.CalculatorAttrMark; +import com.fr.runtime.FineRuntime; +import com.fr.stable.EssentialUtils; +import com.fr.stable.bridge.BridgeMark; +import com.fr.stable.bridge.StableFactory; +import com.fr.stable.fun.WidgetSwitcher; +import com.fr.stable.module.Module; +import com.fr.stable.plugin.ExtraFormClassManagerProvider; +import com.fr.transaction.Configurations; +import com.fr.transaction.LocalConfigurationHelper; +import com.fr.xml.ReportXMLUtils; +import org.easymock.EasyMock; +import org.easymock.IArgumentMatcher; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.easymock.PowerMock; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import java.io.InputStream; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({CptxTemplatePool.class, XMLEncryptUtils.class}) +@SuppressStaticInitializationFor({"com.fr.nx.cptx.cache.CptxTemplatePool", "com.fr.base.io.XMLEncryptUtils"}) +@PowerMockIgnore({"com.sun.tools.attach.*", "sun.tools.attach.*"}) +public class TemplateTransformerDebugTest { + + @BeforeClass + public static void beforeClass() { + System.setProperty("apple.awt.UIElement", "true"); + StableFactory.registerXMLDescription(ReportPageAttrProvider.XML_TAG, new ReportPageAttr()); + StableFactory.registerMarkedClass(ReportPageProvider.XML_TAG, ReportPage.class); + StableFactory.registerMarkedClass(PaperSettingProvider.XML_TAG, PaperSetting.class); + StableFactory.registerMarkedClass(ReportPageProvider.XML_TAG_4_SHEET, SheetPage.class); + StableFactory.registerMarkedClass(PagePainterProvider.XML_TAG, PagePainter.class); + StableFactory.registerMarkedClass(PageSetChainProvider.XML_TAG, PageSetChain.class); + StableFactory.registerMarkedClass(PageXmlProvider.XML_TAG, PageXmlOperator.class); + StableFactory.registerMarkedClass(BaseSinglePagePrintable.XML_TAG, SinglePagePrintable.class); + StableFactory.registerMarkedClass(BaseSingleReportCache.XML_TAG, SingleReportCache.class); + StableFactory.registerMarkedClass(PageSetProvider.XML_TAG_4_ARRAY, ArrayPageSet.class); + StableFactory.registerMarkedClass(ClippedPageProvider.XML_TAG_EC, ClippedECPage.class); + StableFactory.registerMarkedClass(ClippedPageProvider.XML_TAG_CHART, ClippedChartPage.class); + StableFactory.registerMarkedClass(PageGeneratorProvider.XML_TAG_PAGE, PaginateReportPageGenerator.class); + StableFactory.registerMarkedClass(PageGeneratorProvider.XML_TAG_POLY, PolyReportPageGenerator.class); + StableFactory.registerMarkedClass(PageGeneratorProvider.XML_TAG_SHEET, SheetPageGenerator.class); + StableFactory.registerMarkedClass(PagePainterProvider.XML_TAG_4_PDF, PDF2Painter.class); + StableFactory.registerMarkedClass(BridgeMark.SUBMIT_BUTTON, FormSubmitButton.class); + StableFactory.registerMarkedClass(FormOperator.MARK_STRING, DefaultFormOperator.class); + StableFactory.registerMarkedClass(Module.FORM_MODULE, Form.class); + StableFactory.registerMarkedClass(ParameterUI.FORM_XML_TAG, FormParameterUI.class); + StableFactory.registerMarkedClass(FormHyperlinkProvider.XML_TAG, FormHyperlink.class); + StableFactory.registerMarkedObject(WidgetSwitcher.XML_TAG, new DefaultSwitcherImpl()); + StableFactory.registerMarkedClass(ExtraFormClassManagerProvider.XML_TAG, ExtraFormClassManager.class); + StableFactory.registerXMLDescription(BaseChartCollection.XML_TAG, new ChartCollection()); + StableFactory.registerXMLDescription(Parameter.XML_TAG, new Parameter()); + FineRuntime.start(); + GeneralXMLTools.Object_Tokenizer = new ReportXMLUtils.ReportObjectTokenizer(); + GeneralXMLTools.Object_XML_Writer_Finder = new ReportXMLUtils.ReportObjectXMLWriterFinder(); + DaoContext.setEntityDao(new LocalEntityDao()); + DaoContext.setClassHelperDao(new LocalClassHelperDao()); + DaoContext.setXmlEntityDao(new LocalXmlEntityDao()); + Configurations.setHelper(new LocalConfigurationHelper()); + + IOFileAttrMarkManager.register(new CalculatorAttrMark()); + } + + @Before + public void before() { + Whitebox.setInternalState(XMLEncryptUtils.class, "KEY", new XMLEncryptKey()); + } + + @Test + public void testUnsupportedCompile() { + WorkBook workbook = readCpt("read-write-expand-order.cpt"); + + CptxTemplatePool pool = EasyMock.mock(CptxTemplatePool.class); + PowerMock.mockStatic(CptxTemplatePool.class); + EasyMock.expect(CptxTemplatePool.getInstance()).andReturn(pool).anyTimes(); + + PowerMock.replay(CptxTemplatePool.class); + pool.addCptxTemplate(EasyMock.anyString(), matchUnsupportedWebPreviewCptxTemplate()); + EasyMock.expectLastCall().once(); + + EasyMock.replay(pool); + TemplateTransformer.compileCPTX(workbook, new MemFILE("read-write-expand-order.cpt")); + + EasyMock.verify(pool); + PowerMock.verify(CptxTemplatePool.class); + + } + + @Test + public void testImageRefCompile() { + WorkBook workbook = readCpt("read-write-image-ref.cpt"); + + CptxTemplatePool pool = EasyMock.mock(CptxTemplatePool.class); + PowerMock.mockStatic(CptxTemplatePool.class); + EasyMock.expect(CptxTemplatePool.getInstance()).andReturn(pool).anyTimes(); + + PowerMock.replay(CptxTemplatePool.class); + pool.addCptxTemplate(EasyMock.anyString(), matchImageRefWebPreviewCptxTemplate()); + EasyMock.expectLastCall().once(); + + EasyMock.replay(pool); + TemplateTransformer.compileCPTX(workbook, new MemFILE("read-write-image-ref.cpt")); + + EasyMock.verify(pool); + PowerMock.verify(CptxTemplatePool.class); + + } + + private CptxTemplate matchImageRefWebPreviewCptxTemplate() { + EasyMock.reportMatcher(new IArgumentMatcher() { + @Override + public boolean matches(Object argument) { + if (argument instanceof CptxTemplate) { + CptxTemplate cptxTemplate = (CptxTemplate) argument; + Assert.assertNotNull(cptxTemplate.getTemplate()); + Assert.assertNotNull(cptxTemplate.getTemplate().getCompileResult()); + Assert.assertEquals(1, cptxTemplate.getTemplate().getCompileResult().getCellBlocks().length); + Assert.assertNotNull(cptxTemplate.getTemplate().getCompileResult().getCellBlocks()[0].getDataStructure()); + Assert.assertNotNull(cptxTemplate.getTemplate().getCompileResult().getCellBlocks()[0].getDataStructure().getCells()); + CellTemplate cellTemplate = cptxTemplate.getTemplate().getCompileResult().getCellBlocks()[0].getDataStructure().getCells().get(CellKey.fromString("A2")); + Assert.assertNotNull(cellTemplate); + Calculable calculable = cellTemplate.getCalculable(); + ConstantCalculable constantCalculable = calculable.unWrap(ConstantCalculable.KEY); + Assert.assertNotNull(constantCalculable); + Assert.assertTrue(constantCalculable.get() instanceof ImageResourceRef); + return true; + } + return false; + } + + @Override + public void appendTo(StringBuffer buffer) { + buffer.append("cptx template should be previewed on the web side but not."); + } + }); + return null; + } + + private CptxTemplate matchUnsupportedWebPreviewCptxTemplate() { + EasyMock.reportMatcher(new IArgumentMatcher() { + @Override + public boolean matches(Object argument) { + if (argument instanceof CptxTemplate) { + CptxTemplate cptxTemplate = (CptxTemplate) argument; + Assert.assertEquals("unsupported feature: sort after expand", + cptxTemplate.getMetadata().getFailMessage()); + return true; + } + return false; + } + + @Override + public void appendTo(StringBuffer buffer) { + buffer.append("should find unsupported error message but not"); + } + }); + return null; + } + + private static WorkBook readCpt(String subPath) { + InputStream is = TemplateTransformerDebugTest.class.getClassLoader().getResourceAsStream("cpt/" + subPath); + WorkBook wb = new WorkBook(); + try { + wb.readStream(EncryptUtils.decodeInputStream(is)); + } catch (Exception e) { + throw new RuntimeException(e); + } + return wb; + } +} diff --git a/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TemplateTransformerTest.java b/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TemplateTransformerTest.java new file mode 100644 index 000000000..e0afe9313 --- /dev/null +++ b/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TemplateTransformerTest.java @@ -0,0 +1,85 @@ +package com.fr.nx.app.designer.toolbar; + +import com.fr.base.FRContext; +import com.fr.base.operator.common.CommonOperator; +import com.fr.file.FILE; +import com.fr.file.filetree.FileNodes; +import com.fr.invoke.Reflect; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.easymock.PowerMock; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import static com.fr.nx.app.designer.toolbar.TemplateTransformer.OTHER; +import static com.fr.nx.app.designer.toolbar.TemplateTransformer.TO_CPT; +import static com.fr.nx.app.designer.toolbar.TemplateTransformer.TO_CPTX; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({FRContext.class}) +@PowerMockIgnore({"sun.tools.attach.*", "com.sun.tools.*", "com.fr.license.*"}) +public class TemplateTransformerTest { + + @Test + public void testParse() { + assertEquals(TO_CPTX, TemplateTransformer.parse("/sdf/abc.cpt")); + assertEquals(TO_CPT, TemplateTransformer.parse("/sdf/abc.cptx")); + assertEquals(OTHER, TemplateTransformer.parse("/sdf/abc.frm")); + assertEquals(OTHER, TemplateTransformer.parse(null)); + assertEquals(OTHER, TemplateTransformer.parse("abc")); + } + + @Test + public void testCompileFailed() { + TransformResultInfo result = TemplateTransformer.compileCPTX(null, null); + assertEquals(result.getResult(), TransformResult.FAILED); + } + + @Test + public void testGenerateNewPath() { + assertEquals(generateNewPath("/abc/dd.cpt", ".cpt", ".cptx"), "/abc/dd.cptx"); + assertEquals(generateNewPath("/abc/dd.cptx", ".cpt", ".cptx"), "/abc/dd.cptx"); + assertEquals(generateNewPath("/abc/dd.cptx", ".cptx", ".cpt"), "/abc/dd.cpt"); + assertEquals(generateNewPath("/abc/dd.frm", ".cpt", ".cptx"), "/abc/dd.frm"); + assertNull(generateNewPath(null, null, null)); + assertEquals(generateNewPath("abc", null, null), "abc"); + } + + private String generateNewPath(String oldPath, String oldSuffix, String newSuffix) { + return Reflect.on(TemplateTransformer.class).call("generateNewPath", oldPath, oldSuffix, newSuffix).get(); + } + + @Test + public void testCreateOutputFile() { + CommonOperator commonOperator = EasyMock.mock(CommonOperator.class); + FileNodes fileNodes = EasyMock.mock(FileNodes.class); + EasyMock.expect(fileNodes.getSupportedTypes()).andReturn(new String[]{"cptx"}).anyTimes(); + PowerMock.mockStatic(FRContext.class); + EasyMock.expect(FRContext.getCommonOperator()).andReturn(commonOperator).anyTimes(); + EasyMock.expect(FRContext.getFileNodes()).andReturn(fileNodes).anyTimes(); + EasyMock.expect(commonOperator.getWebRootPath()).andReturn("/WebInf/").anyTimes(); + EasyMock.replay(commonOperator, fileNodes); + PowerMock.replayAll(); + FILE file1 = TemplateTransformer.createOutputFile("WorkBook1.cpt", ".cpt", ".cptx"); + FILE file2 = TemplateTransformer.createOutputFile("WorkBook1.cpt", ".cptx", ".cpt"); + assertEquals("WorkBook1.cptx", file1.getPath()); + assertEquals("WorkBook1.cpt", file2.getPath()); + } + + + @Test + public void testNeedDoAfterTransformed() { + Assert.assertTrue(needDoAfterTransformed(TransformResult.SUCCESS)); + Assert.assertTrue(needDoAfterTransformed(TransformResult.UNSUPPORT)); + Assert.assertFalse(needDoAfterTransformed(TransformResult.FAILED)); + } + + private boolean needDoAfterTransformed(TransformResult result) { + return Reflect.on(TemplateTransformer.class).call("needDoAfterTransformed", result).get(); + } +} diff --git a/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TransformResultInfoTest.java b/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TransformResultInfoTest.java new file mode 100644 index 000000000..66a32b95f --- /dev/null +++ b/designer-base/src/test/java/com/fr/nx/app/designer/toolbar/TransformResultInfoTest.java @@ -0,0 +1,32 @@ +package com.fr.nx.app.designer.toolbar; + +import com.fr.locale.InterProviderFactory; +import org.junit.Assert; +import org.junit.Test; + +/** + * Created by kerry on 2020-01-15 + */ +public class TransformResultInfoTest { + @Test + public void testGetTransformLog() { + TransformResultInfo resultInfo1 = TransformResultInfo.generateResult(TransformResult.FAILED, "failed"); + Assert.assertEquals("failed\n" + + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Failed_Tip"), + resultInfo1.getTransformLog()); + TransformResultInfo resultInfo2 = TransformResultInfo.generateResult(TransformResult.SUCCESS); + Assert.assertEquals(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Success_Tip"), + resultInfo2.getTransformLog()); + TransformResultInfo resultInfo3 = TransformResultInfo.generateResult(TransformResult.UNSUPPORT, "unsupport"); + Assert.assertEquals("unsupport\n" + + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Transform_Unsupport_Tip"), + resultInfo3.getTransformLog()); + + } + + @Test + public void testSaved() { + TransformResultInfo resultInfo1 = TransformResultInfo.generateResult(TransformResult.FAILED, "failed").saved(false); + Assert.assertFalse(resultInfo1.isSaved()); + } +} diff --git a/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformProgressTest.java b/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformProgressTest.java new file mode 100644 index 000000000..c0b68c0ab --- /dev/null +++ b/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformProgressTest.java @@ -0,0 +1,27 @@ +package com.fr.nx.app.designer.transform; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Created by kerry on 2020-01-15 + */ +public class BatchTransformProgressTest { + @Test + public void testGetProgress(){ + BatchTransformProgress batchTransformProgress = new BatchTransformProgress(100); + Assert.assertEquals(0.0D, batchTransformProgress.getProgress(), 0.0D); + } + + @Test + public void testUpdateProgress(){ + BatchTransformProgress batchTransformProgress = new BatchTransformProgress(100); + for (int i = 0; i < 80; i++) { + batchTransformProgress.updateProgress(); + } + Assert.assertEquals(0.8D, batchTransformProgress.getProgress(), 0.0D); + batchTransformProgress.updateProgress(); + Assert.assertEquals(0.81D, batchTransformProgress.getProgress(), 0.0D); + + } +} diff --git a/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformUtilTest.java b/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformUtilTest.java new file mode 100644 index 000000000..b3adc1246 --- /dev/null +++ b/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformUtilTest.java @@ -0,0 +1,35 @@ +package com.fr.nx.app.designer.transform; + +import com.fr.file.filetree.FileNode; +import org.junit.Assert; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by kerry on 2020-02-14 + */ +public class BatchTransformUtilTest { + @Test + public void testFilterTransformedFile(){ + FileNode[] fileNodes = new FileNode[]{ + new FileNode("test1", false), new FileNode("test2", false) + }; + List transformedFileNodes = new ArrayList(); + FileNode[] result1 = BatchTransformUtil.filterTransformedFile(fileNodes, transformedFileNodes); + Assert.assertEquals(2, result1.length); + + transformedFileNodes.add( new FileNode("test1", false)); + FileNode[] result2 = BatchTransformUtil.filterTransformedFile(fileNodes, transformedFileNodes); + Assert.assertEquals(1, result2.length); + + transformedFileNodes.add( new FileNode("test2", false)); + FileNode[] result3 = BatchTransformUtil.filterTransformedFile(fileNodes, transformedFileNodes); + Assert.assertEquals(0, result3.length); + + transformedFileNodes.add( new FileNode("test3", false)); + FileNode[] result4 = BatchTransformUtil.filterTransformedFile(fileNodes, transformedFileNodes); + Assert.assertEquals(0, result4.length); + } +} diff --git a/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformerTest.java b/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformerTest.java new file mode 100644 index 000000000..d29d3e742 --- /dev/null +++ b/designer-base/src/test/java/com/fr/nx/app/designer/transform/BatchTransformerTest.java @@ -0,0 +1,124 @@ +package com.fr.nx.app.designer.transform; + +import org.junit.Before; +import org.junit.Test; + +import java.util.concurrent.CountDownLatch; + +/** + * Created by kerry on 2020-01-15 + */ +// FIXME @kerry 打包失败,暂时先注释 +//@RunWith(value = PowerMockRunner.class) +//@PrepareForTest({CompileTransformUtil.class, PluginContexts.class}) +//@PowerMockIgnore({"sun.tools.attach.*", "com.sun.tools.*"}) +public class BatchTransformerTest { + + private static final int MAXPOOLSIZE = 5; + private static final int COREPOOLSIZE = 3; + private static final String THREAD_NAME_TEMPLATE = "batchtransform-thread-%s"; + + @Before + public void setUp() { +// FineRuntime.start(); +// ExecutorService threadPoolExecutor = new ThreadPoolExecutor(COREPOOLSIZE, MAXPOOLSIZE, +// 0L, TimeUnit.MILLISECONDS, +// new LinkedBlockingQueue(), +// new ThreadFactoryBuilder().setNameFormat(THREAD_NAME_TEMPLATE).build()); +// PowerMock.mockStatic(PluginContexts.class); +// PluginContext pluginContext = EasyMock.mock(PluginContext.class); +// EasyMock.expect(pluginContext.newFixedThreadPool( +// EasyMock.anyInt(), EasyMock.anyObject(ThreadFactory.class))) +// .andReturn(threadPoolExecutor).anyTimes(); +// EasyMock.expect(PluginContexts.currentContext()).andReturn(pluginContext).anyTimes(); +// +// PowerMock.mockStatic(CompileTransformUtil.class); +// TransformResultInfo resultInfo = TransformResultInfo.generateResult(TransformResult.SUCCESS); +// EasyMock.expect(CompileTransformUtil.compileFile(EasyMock.anyObject(FileNode.class))) +// .andReturn(resultInfo).anyTimes(); +// EasyMock.replay(pluginContext); +// PowerMock.replayAll(); + } + + @Test + public void testBatchTransform() { +// final CountDownLatch countDownLatch = new CountDownLatch(2); +// BatchTransformer transformer = new BatchTransformer(new MockUpdateCallBack(countDownLatch)); +// List nodeList = new ArrayList<>(); +// FileNode test1 = new FileNode("path1", false); +// FileNode test2 = new FileNode("path2", false); +// nodeList.add(test1); +// nodeList.add(test2); +// transformer.batchTransform(nodeList); +// try { +// countDownLatch.await(5, TimeUnit.SECONDS); +// } catch (InterruptedException e) { +// Assert.fail(); +// } +// Map transformResults = transformer.getResults(); +// Assert.assertEquals(2, transformResults.size()); +// Assert.assertEquals(TransformResult.SUCCESS, transformResults.get(test1).getResult()); +// Assert.assertEquals(TransformResult.SUCCESS, transformResults.get(test2).getResult()); + + } + + @Test + public void testTransform() { +// final CountDownLatch countDownLatch = new CountDownLatch(1); +// BatchTransformer transformer = new BatchTransformer(new MockUpdateCallBack(countDownLatch)); +// FileNode test = new FileNode("path", false); +// Reflect.on(transformer).call("transform", test); +// try { +// countDownLatch.await(5, TimeUnit.SECONDS); +// } catch (InterruptedException e) { +// Assert.fail(); +// } +// Map transformResults = transformer.getResults(); +// Assert.assertEquals(1, transformResults.size()); +// Assert.assertEquals(TransformResult.SUCCESS, transformResults.get(test).getResult()); + } + + @Test + public void testShutDown() { +// final CountDownLatch countDownLatch = new CountDownLatch(1); +// BatchTransformer transformer = new BatchTransformer(new MockUpdateCallBack(countDownLatch)); +// List nodeList = new ArrayList<>(); +// FileNode test = new FileNode("path", false); +// nodeList.add(test); +// transformer.batchTransform(nodeList); +// transformer.shutDown(); +// try { +// countDownLatch.await(3, TimeUnit.SECONDS); +// } catch (InterruptedException e) { +// Assert.fail(); +// } +// Map transformResults = transformer.getResults(); +// Assert.assertEquals(1, transformResults.size()); +// Assert.assertEquals(TransformResult.SUCCESS, transformResults.get(test).getResult()); + } + + + private class MockUpdateCallBack implements UpdateCallBack { + private CountDownLatch countDownLatch; + + + public MockUpdateCallBack(CountDownLatch countDownLatch) { + this.countDownLatch = countDownLatch; + } + + @Override + public void updateProgress(double progress) { + countDownLatch.countDown(); + } + + @Override + public void shutDown() { + + } + + @Override + public void reset() { + + } + } +} diff --git a/designer-base/src/test/java/com/fr/nx/app/designer/utils/CompileTransformUtilTest.java b/designer-base/src/test/java/com/fr/nx/app/designer/utils/CompileTransformUtilTest.java new file mode 100644 index 000000000..653bbdef2 --- /dev/null +++ b/designer-base/src/test/java/com/fr/nx/app/designer/utils/CompileTransformUtilTest.java @@ -0,0 +1,58 @@ +package com.fr.nx.app.designer.utils; + +import com.fr.base.FRContext; +import com.fr.base.operator.common.CommonOperator; +import com.fr.design.mainframe.JTemplate; +import com.fr.file.FILE; +import com.fr.file.filetree.FileNodes; +import com.fr.nx.app.designer.toolbar.TemplateTransformer; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.easymock.PowerMock; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +/** + * Created by kerry on 2019-12-04 + */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({FRContext.class}) +@PowerMockIgnore({"sun.tools.attach.*", "com.sun.tools.*"}) +public class CompileTransformUtilTest { + @Test + public void testGetTargetFile() { + CommonOperator commonOperator = EasyMock.mock(CommonOperator.class); + FileNodes fileNodes = EasyMock.mock(FileNodes.class); + EasyMock.expect(fileNodes.getSupportedTypes()).andReturn(new String[]{"cptx"}).anyTimes(); + PowerMock.mockStatic(FRContext.class); + EasyMock.expect(FRContext.getCommonOperator()).andReturn(commonOperator).anyTimes(); + EasyMock.expect(FRContext.getFileNodes()).andReturn(fileNodes).anyTimes(); + EasyMock.expect(commonOperator.getWebRootPath()).andReturn("/WebInf/").anyTimes(); + EasyMock.replay(commonOperator, fileNodes); + PowerMock.replayAll(); + JTemplate jTemplate1 = EasyMock.mock(JTemplate.class); + FILE file1 = EasyMock.mock(FILE.class); + EasyMock.expect(jTemplate1.getEditingFILE()).andReturn(file1).anyTimes(); + EasyMock.expect(file1.getPath()).andReturn("WorkBook1.cpt").anyTimes(); + EasyMock.replay(jTemplate1, file1); + FILE targetFile = CompileTransformUtil.getTargetFile(jTemplate1, TemplateTransformer.TO_CPTX); + Assert.assertEquals("WorkBook1.cptx", targetFile.getPath()); + + targetFile = CompileTransformUtil.getTargetFile(jTemplate1, TemplateTransformer.TO_CPT); + Assert.assertEquals("WorkBook1.cpt", targetFile.getPath()); + + JTemplate jTemplate2 = EasyMock.mock(JTemplate.class); + FILE file2 = EasyMock.mock(FILE.class); + EasyMock.expect(jTemplate2.getEditingFILE()).andReturn(file2).anyTimes(); + EasyMock.expect(file2.getPath()).andReturn("WorkBook1.cpt").anyTimes(); + EasyMock.replay(jTemplate2, file2); + targetFile = CompileTransformUtil.getTargetFile(jTemplate2, TemplateTransformer.TO_CPTX); + Assert.assertEquals("WorkBook1.cptx", targetFile.getPath()); + + targetFile =CompileTransformUtil.getTargetFile(jTemplate2, TemplateTransformer.TO_CPT); + Assert.assertEquals("WorkBook1.cpt", targetFile.getPath()); + } +} diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/JWorkBook.java b/designer-realize/src/main/java/com/fr/design/mainframe/JWorkBook.java index ff14d54f4..fa1fe93cd 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/JWorkBook.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/JWorkBook.java @@ -55,6 +55,7 @@ import com.fr.design.module.DesignModuleFactory; import com.fr.design.parameter.ParameterDefinitePane; import com.fr.design.parameter.ParameterInputPane; import com.fr.design.preview.MobilePreview; +import com.fr.design.preview.PagePlusPreview; import com.fr.design.preview.PagePreview; import com.fr.design.preview.ViewPreview; import com.fr.design.preview.WriteEnhancePreview; @@ -918,7 +919,7 @@ public class JWorkBook extends JTemplate { public PreviewProvider[] supportPreview() { PreviewProvider[] templatePreviews = super.supportPreview(); return ArrayUtils.addAll(new PreviewProvider[]{ - new PagePreview(), new WritePreview(), new ViewPreview(), new WriteEnhancePreview(), new MobilePreview() + new PagePreview(), new WritePreview(), new ViewPreview(), new WriteEnhancePreview(), new MobilePreview(), new PagePlusPreview() }, templatePreviews); } @@ -1194,6 +1195,7 @@ public class JWorkBook extends JTemplate { protected void addChooseFILEFilter(FILEChooserPane fileChooser) { String appName = ProductConstants.APP_NAME; fileChooser.addChooseFILEFilter(new ChooseFileFilter(FileExtension.CPT, appName + Toolkit.i18nText("Fine-Design_Report_Template_File"))); + fileChooser.addChooseFILEFilter(new ChooseFileFilter(FileExtension.CPTX, appName + Toolkit.i18nText("Fine-Design_Report_Template_File"))); addExtraChooseFILEFilter(fileChooser); } diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/app/DesignerAppActivator.java b/designer-realize/src/main/java/com/fr/design/mainframe/app/DesignerAppActivator.java index ec425847d..23ddcbe8a 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/app/DesignerAppActivator.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/app/DesignerAppActivator.java @@ -4,6 +4,7 @@ import com.fr.design.mainframe.App; import com.fr.design.mainframe.JTemplateFactory; import com.fr.module.Activator; import com.fr.module.extension.Prepare; +import com.fr.nx.app.designer.CptxApp; import java.util.List; @@ -33,7 +34,7 @@ public class DesignerAppActivator extends Activator implements Prepare { @Override public void prepare() { - addMutable(App.KEY, new CptApp(), new FormApp(), new XlsApp(), new XlsxApp()); + addMutable(App.KEY, new CptApp(), new CptxApp(), new FormApp(), new XlsApp(), new XlsxApp()); } } diff --git a/designer-realize/src/main/java/com/fr/design/mainframe/cell/settingpane/CellExpandExtraAttrPane.java b/designer-realize/src/main/java/com/fr/design/mainframe/cell/settingpane/CellExpandExtraAttrPane.java index 13c288f92..e9af09a4e 100644 --- a/designer-realize/src/main/java/com/fr/design/mainframe/cell/settingpane/CellExpandExtraAttrPane.java +++ b/designer-realize/src/main/java/com/fr/design/mainframe/cell/settingpane/CellExpandExtraAttrPane.java @@ -7,6 +7,7 @@ import com.fr.design.layout.TableLayout; import com.fr.design.layout.TableLayoutHelper; import com.fr.design.ui.util.UIUtil; import com.fr.event.EventDispatcher; +import com.fr.nx.app.designer.cell.CellTreeAttrPanelProvider; import com.fr.plugin.context.PluginContext; import com.fr.plugin.injectable.PluginModule; import com.fr.plugin.manage.PluginFilter; @@ -19,7 +20,10 @@ import java.awt.BorderLayout; import java.awt.Component; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import static com.fr.plugin.observer.PluginEventType.AfterRun; @@ -114,14 +118,14 @@ public class CellExpandExtraAttrPane extends JPanel { extras = new ArrayList<>(); } extras.clear(); - Set attrProviders = ExtraDesignClassManager.getInstance().getArray(CellExpandAttrPanelProvider.MARK_STRING); - if (attrProviders != null) { - for (CellExpandAttrPanelProvider attrProvider : attrProviders) { - if (attrProvider.isDisplayable()) { - BasicBeanPane extra = attrProvider.createPanel(); - if (extra != null) { - extras.add(extra); - } + Set attrProviders = new LinkedHashSet<>(Collections.singletonList(new CellTreeAttrPanelProvider())); + Optional.>of(ExtraDesignClassManager.getInstance().getArray(CellExpandAttrPanelProvider.MARK_STRING)) + .ifPresent(attrProviders::addAll); + for (CellExpandAttrPanelProvider attrProvider : attrProviders) { + if (attrProvider.isDisplayable()) { + BasicBeanPane extra = attrProvider.createPanel(); + if (extra != null) { + extras.add(extra); } } } diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/CptxApp.java b/designer-realize/src/main/java/com/fr/nx/app/designer/CptxApp.java new file mode 100644 index 000000000..9186a6734 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/CptxApp.java @@ -0,0 +1,53 @@ +package com.fr.nx.app.designer; + +import com.fr.base.extension.FileExtension; +import com.fr.design.mainframe.AbstractAppProvider; +import com.fr.design.mainframe.JTemplate; +import com.fr.file.FILE; +import com.fr.main.impl.WorkBook; +import com.fr.nx.app.designer.utils.DesignerCptxFileUtils; + +/** + * 支持设计器打开cptx文件 + */ +public class CptxApp extends AbstractAppProvider { + + public CptxApp() { + StartupAssist.initDesignModule(); + } + + @Override + public String[] defaultExtensions() { + return new String[]{FileExtension.CPTX.getExtension()}; + } + + @Override + public void process() { + super.process(); + StartupAssist.reloadTemplate(); + } + + /** + * 该方法只是为了读到cptx中的cpt + * + * @param tplFile file + * @return template + */ + @Override + public JTemplate openTemplate(FILE tplFile) { + WorkBook workBook = DesignerCptxFileUtils.getWorkBook(tplFile); + if (workBook == null) { + workBook = new WorkBook(); + } + return new JStreamBook(workBook, tplFile); + } + + @Override + public WorkBook asIOFile(FILE file) { + WorkBook workBook = DesignerCptxFileUtils.getWorkBook(file); + if (workBook == null) { + workBook = new WorkBook(); + } + return workBook; + } +} diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/JStreamBook.java b/designer-realize/src/main/java/com/fr/nx/app/designer/JStreamBook.java new file mode 100644 index 000000000..2fa748f95 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/JStreamBook.java @@ -0,0 +1,117 @@ +package com.fr.nx.app.designer; + +import com.fr.base.extension.FileExtension; +import com.fr.design.actions.file.export.CSVExportAction; +import com.fr.design.actions.file.export.PDFExportAction; +import com.fr.design.actions.file.export.SVGExportAction; +import com.fr.design.actions.file.export.TextExportAction; +import com.fr.design.actions.file.export.WordExportAction; +import com.fr.design.mainframe.JWorkBook; +import com.fr.design.menu.MenuDef; +import com.fr.design.menu.ShortCut; +import com.fr.file.FILE; +import com.fr.general.ComparatorUtils; +import com.fr.locale.InterProviderFactory; +import com.fr.log.FineLoggerFactory; +import com.fr.main.impl.WorkBook; +import com.fr.nx.app.designer.utils.DesignerCptxFileUtils; +import com.fr.nx.cptx.entry.metadata.CptxMetadata; +import com.fr.nx.app.designer.menu.CalculateAttrAction; +import com.fr.nx.app.designer.toolbar.TemplateTransformer; +import com.fr.nx.app.designer.toolbar.TransformResult; +import com.fr.nx.app.designer.toolbar.TransformResultInfo; +import com.fr.stable.StringUtils; +import com.fr.stable.project.ProjectConstants; +import com.fr.third.jodd.util.ArraysUtil; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; + +public class JStreamBook extends JWorkBook { + + + public JStreamBook(WorkBook workBook, FILE file) { + super(workBook, file); + } + + @Override + protected boolean saveFile() { + boolean saved; + TransformResultInfo resultInfo = TemplateTransformer.compileCPTX(getTarget(), getEditingFILE()); + saved = resultInfo.isSaved(); + this.setSaved(saved); + if (saved) { + this.fireJTemplateSaved(); + } + return saved; + } + + @Override + protected boolean saveToNewFile(String oldName) { + String path = this.getEditingFILE().getPath(); + try { + if (!path.startsWith(ProjectConstants.REPORTLETS_NAME)) { + File file = new File(path); + if (file.getParentFile() != null && !file.getParentFile().exists()) { + file.getParentFile().mkdirs(); + } + file.createNewFile(); + OutputStream outputStream = new FileOutputStream(file); + this.getTarget().export(outputStream); + return true; + }else { + TransformResult result; + if (FileExtension.CPT.matchExtension(path)) { + result = TemplateTransformer.TO_CPT.transform(this); + } else { + result = TemplateTransformer.TO_CPTX.transform(this); + } + return ComparatorUtils.equals(TransformResult.SUCCESS, result); + } + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + } + return false; + } + + + /** + * 保存文件的后缀名 + * + * @return 后缀的字符串 + */ + @Override + public String suffix() { + return ".cptx"; + } + + @Override + protected void addShortCut(MenuDef exportMenuDef, MenuDef excelExportMenuDef) { + exportMenuDef.addShortCut(excelExportMenuDef, new PDFExportAction(this), new WordExportAction(this), new SVGExportAction(this), + new CSVExportAction(this), new TextExportAction(this)); + } + + + @Override + public String getPath() { + return getEditingFILE().getPath() + getSuffix(); + } + + public String getTemplateName() { + return getEditingFILE().getName() + getSuffix(); + } + + private String getSuffix() { + CptxMetadata metadata = DesignerCptxFileUtils.getMetadata(this.getEditingFILE()); + if (metadata != null && metadata.isForceCpt()) { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Compatibility_Mode"); + } + return StringUtils.EMPTY; + } + + @Override + public ShortCut[] shortcut4TemplateMenu() { + return ArraysUtil.insert(super.shortcut4TemplateMenu(), new CalculateAttrAction(this), 5); + } +} diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/StartupAssist.java b/designer-realize/src/main/java/com/fr/nx/app/designer/StartupAssist.java new file mode 100644 index 000000000..be4d9ddc8 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/StartupAssist.java @@ -0,0 +1,75 @@ +package com.fr.nx.app.designer; + +import com.fr.base.Parameter; +import com.fr.base.extension.FileExtension; +import com.fr.common.annotations.Negative; +import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.mainframe.JTemplate; +import com.fr.design.mainframe.JTemplateFactory; +import com.fr.design.module.DesignModuleFactory; +import com.fr.design.parameter.AbstractParameterReader; +import com.fr.design.ui.util.UIUtil; +import com.fr.log.FineLoggerFactory; +import com.fr.main.impl.WorkBook; +import com.fr.nx.cptx.CptxIOManager; +import com.fr.nx.cptx.io.handle.CptxTemplateHandle; + +/** + * 保证设计器默认 cptx 启动正常 + * + * @author yaohwu + * created by yaohwu at 2020/6/5 16:28 + */ +public class StartupAssist { + + public static void initDesignModule() { + DesignModuleFactory.registerParameterReader(new AbstractParameterReader() { + @Override + public Parameter[] readParameterFromPath(String tplPath) { + if (accept(tplPath, FileExtension.CPTX.getSuffix())) { + try { + CptxTemplateHandle handle = CptxIOManager.open(tplPath); + WorkBook book = handle.getTemplate().getLegacyWorkBook(); + return book.getParameters(); + } catch (Exception e) { + FineLoggerFactory.getLogger().error(e.getMessage(), e); + return new Parameter[0]; + } + } + return null; + } + }); + } + + private static void reload() { + JTemplate old = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + JTemplate template = JTemplateFactory.createJTemplate(old.getEditingFILE()); + DesignerContext.getDesignerFrame().addAndActivateJTemplate(template); + HistoryTemplateListCache.getInstance().setCurrentEditingTemplate(template); + } + + /** + * 主代码修复后就可以删除了 + * + * 主代码任务链接 + */ + @Negative(until = "2021-09-01") + public static void reloadTemplate() { + if (isCptxJTemplate()) { + UIUtil.invokeLaterIfNeeded(() -> { + reload(); + DesignerContext.getDesignerFrame().setVisible(true); + DesignerContext.getDesignerFrame().resizeFrame(); + }); + } + } + + private static boolean isCptxJTemplate() { + JTemplate jTemplate = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + String path; + return jTemplate != null && + (path = jTemplate.getPath()) != null + && path.endsWith(FileExtension.CPTX.getExtension()); + } +} diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/cell/CellTreeAttrPanel.java b/designer-realize/src/main/java/com/fr/nx/app/designer/cell/CellTreeAttrPanel.java new file mode 100644 index 000000000..fcab8a861 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/cell/CellTreeAttrPanel.java @@ -0,0 +1,109 @@ +package com.fr.nx.app.designer.cell; + +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.event.UIObserverListener; +import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.gui.ilable.MultilineLabel; +import com.fr.design.mainframe.CellWidgetPropertyPane; +import com.fr.design.mainframe.ElementCasePane; +import com.fr.design.mainframe.JTemplate; +import com.fr.form.ui.Widget; +import com.fr.locale.InterProviderFactory; +import com.fr.report.cell.TemplateCellElement; +import com.fr.report.web.button.form.TreeNodeToggleButton; + +import java.awt.BorderLayout; +import java.awt.Color; + +/** + * @author yaohwu + * created by yaohwu at 2020/4/26 16:33 + */ +public class CellTreeAttrPanel extends BasicBeanPane { + private static final Color TIP_COLOR = new Color(0x8f8f92); + + private TemplateCellElement cellElement = null; + + private final UICheckBox showAsTreeNodeCheckBox; + + private final UIObserverListener listener = new UIObserverListener() { + @Override + public void doChange() { + JTemplate template = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + if (template != null) { + if (CellTreeAttrPanel.this.cellElement != null) { + CellTreeAttrPanel.this.updateBean(); + } + template.fireTargetModified(); + CellWidgetPropertyPane.getInstance().populate((ElementCasePane) template.getCurrentElementCasePane()); + } + } + }; + + private static final class Holder { + private static final CellTreeAttrPanel INSTANCE = new CellTreeAttrPanel(); + } + + public static CellTreeAttrPanel getInstance() { + return Holder.INSTANCE; + } + + private CellTreeAttrPanel() { + this.setLayout(new BorderLayout()); + showAsTreeNodeCheckBox = + new UICheckBox(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Cell_Show_As_Tree_Node")); + this.add(showAsTreeNodeCheckBox, BorderLayout.NORTH); + MultilineLabel multilineLabel = + new MultilineLabel(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Cell_Show_As_Tree_Node_TIP")); + multilineLabel.setForeground(TIP_COLOR); + this.add(multilineLabel, BorderLayout.CENTER); + } + + /** + * 展示数据 + * + * @param ob 待展示的对象 + */ + @Override + public void populateBean(TemplateCellElement ob) { + this.cellElement = ob; + Widget widget = cellElement.getWidget(); + showAsTreeNodeCheckBox.registerChangeListener(null); + showAsTreeNodeCheckBox.setSelected(widget instanceof TreeNodeToggleButton); + showAsTreeNodeCheckBox.registerChangeListener(listener); + } + + /** + * 保存数据 + * + * @return 待保存的对象 + */ + @Override + public TemplateCellElement updateBean() { + if (this.cellElement == null) { + return null; + } + if (showAsTreeNodeCheckBox.isSelected()) { + cellElement.setWidget(createTreeToggleButton()); + } else { + cellElement.setWidget(null); + } + return this.cellElement; + } + + @Override + public void updateBean(TemplateCellElement ob) { + this.cellElement = ob; + updateBean(); + } + + @Override + protected String title4PopupWindow() { + return ""; + } + + private TreeNodeToggleButton createTreeToggleButton() { + return new TreeNodeToggleButton(); + } +} diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/cell/CellTreeAttrPanelProvider.java b/designer-realize/src/main/java/com/fr/nx/app/designer/cell/CellTreeAttrPanelProvider.java new file mode 100644 index 000000000..e50e9309a --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/cell/CellTreeAttrPanelProvider.java @@ -0,0 +1,30 @@ +package com.fr.nx.app.designer.cell; + +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.file.HistoryTemplateListCache; +import com.fr.design.fun.impl.AbstractCellExpandAttrPanelProvider; +import com.fr.design.mainframe.JTemplate; +import com.fr.nx.app.designer.JStreamBook; +import com.fr.report.cell.TemplateCellElement; + +/** + * @author yaohwu + * created by yaohwu at 2020/4/26 16:32 + */ +public class CellTreeAttrPanelProvider extends AbstractCellExpandAttrPanelProvider { + + @Override + public BasicBeanPane createPanel() { + return CellTreeAttrPanel.getInstance(); + } + + /** + * 只有新引擎 cptx 模版才展示 + * 先注释掉Override,适配老的主jar,保留这个方法是为了在新主jar下支持这个功能 + */ + // @Override + public boolean isDisplayable() { + JTemplate current = HistoryTemplateListCache.getInstance().getCurrentEditingTemplate(); + return current instanceof JStreamBook; + } +} diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/menu/CalculateAttrAction.java b/designer-realize/src/main/java/com/fr/nx/app/designer/menu/CalculateAttrAction.java new file mode 100644 index 000000000..d318f725a --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/menu/CalculateAttrAction.java @@ -0,0 +1,76 @@ +package com.fr.nx.app.designer.menu; + +import com.fr.base.BaseUtils; +import com.fr.design.actions.JTemplateAction; +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.dialog.DialogActionAdapter; +import com.fr.design.dialog.UIDialog; +import com.fr.design.mainframe.DesignerContext; +import com.fr.design.menu.MenuKeySet; +import com.fr.locale.InterProviderFactory; +import com.fr.main.impl.WorkBook; +import com.fr.nx.app.designer.JStreamBook; +import com.fr.plugin.attr.CalculatorAttrMark; + +import javax.swing.KeyStroke; +import java.awt.event.ActionEvent; + +public class CalculateAttrAction extends JTemplateAction { + + public CalculateAttrAction(JStreamBook jTemplate) { + super(jTemplate); + initMenuStyle(); + } + + private void initMenuStyle() { + this.setMenuKeySet(CALCULATE_FIT_ATTR); + this.setName(getMenuKeySet().getMenuKeySetName() + "..."); + this.setMnemonic(getMenuKeySet().getMnemonic()); + this.setSmallIcon(BaseUtils.readIcon("/com/fr/design/images/bbs/center.png")); + } + + /** + * Action触发事件 + * + * @param e 事件 + */ + public void actionPerformed(ActionEvent e) { + final JStreamBook jwb = getEditingComponent(); + if (jwb == null) { + return; + } + WorkBook workBook = jwb.getTarget(); + CalculatorAttrMark mark = workBook.getAttrMark(CalculatorAttrMark.MARK); + CalculateAttrPane attrPane = new CalculateAttrPane(); + showReportFitDialog(mark, jwb, workBook, attrPane); + } + + private void showReportFitDialog(CalculatorAttrMark mark, final JStreamBook jwb, final WorkBook workBook, final BasicBeanPane attrPane) { + attrPane.populateBean(mark); + UIDialog dialog = attrPane.showMediumWindow(DesignerContext.getDesignerFrame(), new DialogActionAdapter() { + @Override + public void doOk() { + workBook.addAttrMark(attrPane.updateBean()); + jwb.fireTargetModified(); + } + }); + dialog.setVisible(true); + } + + private static final MenuKeySet CALCULATE_FIT_ATTR = new MenuKeySet() { + @Override + public char getMnemonic() { + return 'C'; + } + + @Override + public String getMenuName() { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Attr"); + } + + @Override + public KeyStroke getKeyStroke() { + return null; + } + }; +} \ No newline at end of file diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/menu/CalculateAttrPane.java b/designer-realize/src/main/java/com/fr/nx/app/designer/menu/CalculateAttrPane.java new file mode 100644 index 000000000..a0f53c938 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/menu/CalculateAttrPane.java @@ -0,0 +1,200 @@ +package com.fr.nx.app.designer.menu; + +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.dialog.DialogActionAdapter; +import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.gui.ilable.ActionLabel; +import com.fr.design.gui.ilable.UILabel; +import com.fr.design.gui.ispinner.UISpinner; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.layout.TableLayoutHelper; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.app.designer.menu.bean.FeatureFlagBean; +import com.fr.nx.feature.FeatureFlags; +import com.fr.nx.feature.FeatureManager; +import com.fr.plugin.attr.CalculatorAttrMark; + +import javax.swing.BorderFactory; +import javax.swing.BoxLayout; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; + +public class CalculateAttrPane extends BasicBeanPane { + + private static final Color TIPS_FONT_COLOR = new Color(0x8f8f92); + + // private UICheckBox calculateEnd; + private UICheckBox queryCache; + + private UICheckBox treeAsyncQuery; + + private UICheckBox multiSourceMode; + + private UISpinner treeExpandLayer; + + private JPanel treeConfigPanel; + + /** + * 当前模版从未设置过模版计算属性 + */ + private boolean isEmptyAttr = false; + + public CalculateAttrPane() { + initComponents(); + } + + private void initComponents() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + + JPanel featureFlagPanel = FRGUIPaneFactory.createRightFlowInnerContainer_S_Pane(); + ActionLabel featureLabel = new ActionLabel(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Experimental_Feature")); + featureLabel.setFont(new Font(null, Font.PLAIN, 10)); + featureLabel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JDialog wDialog = createJDialog(); + wDialog.setVisible(true); + } + }); + featureFlagPanel.add(featureLabel); + + JPanel calculateAttrPanel = FRGUIPaneFactory.createY_AXISBoxInnerContainer_S_Pane(); + calculateAttrPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 4, 0)); + +// JPanel calculatedEndPanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); +// calculateEnd = new UICheckBox(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Calculate-End")); +// calculatedEndPanel.add(calculateEnd); + JPanel queryCachePanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); + queryCache = new UICheckBox(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Calculate_Query_Cache")); + queryCachePanel.add(queryCache); + JPanel multiSourcePanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); + multiSourceMode = new UICheckBox(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Feature_Multi_Source")); + multiSourcePanel.add(multiSourceMode); + +// calculateAttrPanel.add(calculatedEndPanel); + calculateAttrPanel.add(queryCachePanel); + calculateAttrPanel.add(multiSourcePanel); + + + treeConfigPanel = FRGUIPaneFactory.createTitledBorderPane( + InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Tree_Config") + ); + treeConfigPanel.setVisible(FeatureManager.getInstance().isFlagEnable(FeatureFlags.TREE)); + treeConfigPanel.setLayout(new BoxLayout(treeConfigPanel, BoxLayout.Y_AXIS)); + + JPanel treeAsyncQueryPanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); + treeAsyncQuery = new UICheckBox(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Tree_Async_Query")); + treeAsyncQueryPanel.add(treeAsyncQuery); + + JPanel treeExpandLayerPanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); + UILabel treeExpandLayerLabel = new UILabel(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Tree_Expand_Layer")); + treeExpandLayer = new UISpinner(1, Integer.MAX_VALUE, 1, 1); + // 功能不支持,设置先禁用 + treeExpandLayer.setEnabled(false); + + treeExpandLayerPanel.add(treeExpandLayerLabel); + treeExpandLayerPanel.add(treeExpandLayer); + + JPanel treeExpandLayerLabelPanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); + UILabel multilineLabel = new UILabel(InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Tree_Expand_Layer_Label")); + multilineLabel.setForeground(TIPS_FONT_COLOR); + treeExpandLayerLabelPanel.add(multilineLabel); + + + treeConfigPanel.add(treeAsyncQueryPanel); + treeConfigPanel.add(treeExpandLayerPanel); + treeConfigPanel.add(treeExpandLayerLabelPanel); + + List components = new ArrayList<>(); + components.add(new Component[]{featureFlagPanel}); + components.add(new Component[]{calculateAttrPanel}); + components.add(new Component[]{treeConfigPanel}); + + JPanel content = + TableLayoutHelper.createGapTableLayoutPane( + components.toArray(new Component[0][]), + TableLayoutHelper.FILL_LASTCOLUMN, + 5, + 5 + ); + + this.add(content, BorderLayout.CENTER); + } + + @Override + public void populateBean(CalculatorAttrMark attrMark) { + if (attrMark == null) { + isEmptyAttr = true; + // 默认折叠树配置 + treeAsyncQuery.setSelected(FeatureManager.getInstance().isFlagEnable(FeatureFlags.TREE_ASYNC)); + return; + } +// calculateEnd.setSelected(attrMark.isCalculateEnd()); + queryCache.setSelected(attrMark.isQueryCache()); + treeAsyncQuery.setSelected(attrMark.isTreeAsyncQuery()); + treeExpandLayer.setValue(attrMark.getTreeExpandLayer()); + multiSourceMode.setSelected(attrMark.isUseMultiSourceMode()); + isEmptyAttr = false; + } + + + /** + * 提交数据 + * + * @return 界面上的更新数据 + */ + @Override + public CalculatorAttrMark updateBean() { + return new CalculatorAttrMark(false, queryCache.isSelected()) + .treeAsyncQuery(treeAsyncQuery.isSelected()) + .treeExpandLayer((int) treeExpandLayer.getValue()) + .useMultiSourceMode(multiSourceMode.isSelected()); + } + + /** + * 标题 + * + * @return 标题 + */ + @Override + protected String title4PopupWindow() { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine-Attr"); + } + + private void showFeatureConfigPane() { + treeConfigPanel.setVisible(FeatureManager.getInstance().isFlagEnable(FeatureFlags.TREE)); + } + + private void resetTreeAsyncConfigPane() { + if (isEmptyAttr) { + treeAsyncQuery.setSelected(FeatureManager.getInstance().isFlagEnable(FeatureFlags.TREE_ASYNC)); + } + } + + + private JDialog createJDialog() { + final FeatureFlagsPanel panel = new FeatureFlagsPanel(); + panel.populateBean(new FeatureFlagBean()); + return panel.showSmallWindow(SwingUtilities.getWindowAncestor(CalculateAttrPane.this), new DialogActionAdapter() { + @Override + public void doOk() { + panel.updateBean().saveModification(); + super.doOk(); + CalculateAttrPane.this.resetTreeAsyncConfigPane(); + CalculateAttrPane.this.showFeatureConfigPane(); + CalculateAttrPane.this.revalidate(); + CalculateAttrPane.this.repaint(); + } + }); + } +} diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/menu/FeatureFlagsPanel.java b/designer-realize/src/main/java/com/fr/nx/app/designer/menu/FeatureFlagsPanel.java new file mode 100644 index 000000000..eaee45551 --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/menu/FeatureFlagsPanel.java @@ -0,0 +1,73 @@ +package com.fr.nx.app.designer.menu; + +import com.fr.design.beans.BasicBeanPane; +import com.fr.design.gui.icheckbox.UICheckBox; +import com.fr.design.layout.FRGUIPaneFactory; +import com.fr.design.layout.TableLayoutHelper; +import com.fr.locale.InterProviderFactory; +import com.fr.nx.app.designer.menu.bean.FeatureFlagBean; +import com.fr.nx.feature.FeatureFlag; +import com.fr.stable.collections.combination.Pair; + +import javax.swing.BorderFactory; +import javax.swing.JPanel; +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * Created by loy on 2020/4/14. + */ +public class FeatureFlagsPanel extends BasicBeanPane { + + private FeatureFlagBean bean; + private JPanel mainPanel; + + public FeatureFlagsPanel() { + this.setLayout(FRGUIPaneFactory.createBorderLayout()); + this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + mainPanel = FRGUIPaneFactory.createY_AXISBoxInnerContainer_S_Pane(); + mainPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 4, 0)); + JPanel content = + TableLayoutHelper.createGapTableLayoutPane( + new Component[][]{new Component[]{mainPanel}}, + TableLayoutHelper.FILL_LASTCOLUMN, + 5, + 5 + ); + + this.add(content, BorderLayout.CENTER); + } + + @Override + public void populateBean(FeatureFlagBean featureFlagBean) { + this.bean = featureFlagBean; + mainPanel.removeAll(); + for (Pair entry : featureFlagBean.getAllFlags()) { + final FeatureFlag flag = entry.getFirst(); + Boolean selected = entry.getSecond(); + JPanel itemPanel = FRGUIPaneFactory.createNormalFlowInnerContainer_S_Pane(); + final UICheckBox cb = new UICheckBox(flag.getDisplayName()); + cb.setSelected(selected); + cb.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + bean.modifyFlag(flag, cb.isSelected()); + } + }); + itemPanel.add(cb); + mainPanel.add(itemPanel); + } + } + + @Override + public FeatureFlagBean updateBean() { + return bean; + } + + @Override + protected String title4PopupWindow() { + return InterProviderFactory.getProvider().getLocText("Fine-Plugin_Engine_Experimental_Feature"); + } +} diff --git a/designer-realize/src/main/java/com/fr/nx/app/designer/menu/bean/FeatureFlagBean.java b/designer-realize/src/main/java/com/fr/nx/app/designer/menu/bean/FeatureFlagBean.java new file mode 100644 index 000000000..4410d266d --- /dev/null +++ b/designer-realize/src/main/java/com/fr/nx/app/designer/menu/bean/FeatureFlagBean.java @@ -0,0 +1,36 @@ +package com.fr.nx.app.designer.menu.bean; + +import com.fr.nx.feature.FeatureFlag; +import com.fr.nx.feature.FeatureManager; +import com.fr.stable.collections.combination.Pair; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by loy on 2020/4/14. + */ +public class FeatureFlagBean { + + private Map modifyMap = new HashMap<>(); + + public List> getAllFlags() { + List> flagList = new ArrayList<>(); + for (FeatureFlag flag : FeatureManager.getSupportFlags()) { + flagList.add(new Pair<>(flag, FeatureManager.getInstance().isFlagEnable(flag))); + } + return flagList; + } + + public void modifyFlag(FeatureFlag flag, boolean value) { + modifyMap.put(flag, value); + } + + public void saveModification() { + for (Map.Entry entry : modifyMap.entrySet()) { + FeatureManager.getInstance().setFlagEnable(entry.getKey(), entry.getValue()); + } + } +} diff --git a/designer-realize/src/main/java/com/fr/start/MainDesigner.java b/designer-realize/src/main/java/com/fr/start/MainDesigner.java index 162a2044f..4bcf4387f 100644 --- a/designer-realize/src/main/java/com/fr/start/MainDesigner.java +++ b/designer-realize/src/main/java/com/fr/start/MainDesigner.java @@ -6,6 +6,7 @@ import com.fr.design.DesignerEnvManager; import com.fr.design.actions.file.WebPreviewUtils; import com.fr.design.actions.file.newReport.NewPolyReportAction; import com.fr.design.actions.file.newReport.NewWorkBookAction; +import com.fr.design.actions.server.LocalAnalyzerAction; import com.fr.design.actions.server.ServerConfigManagerAction; import com.fr.design.actions.server.StyleListAction; import com.fr.design.actions.server.WidgetManagerAction; @@ -165,6 +166,7 @@ public class MainDesigner extends BaseDesigner { if (WorkContext.getCurrent().isRoot()) { menuDef.addShortCut(new ServerConfigManagerAction(), new StyleListAction(), new WidgetManagerAction()); menuDef.addShortCut(new ChartPreStyleAction(), new ChartEmptyDataStyleAction(),new ChartMapEditorAction()); + menuDef.addShortCut(new LocalAnalyzerAction()); } insertMenu(menuDef, MenuHandler.SERVER); diff --git a/designer-realize/src/test/java/com/fr/nx/app/designer/CptxAppTest.java b/designer-realize/src/test/java/com/fr/nx/app/designer/CptxAppTest.java new file mode 100644 index 000000000..26ca454c5 --- /dev/null +++ b/designer-realize/src/test/java/com/fr/nx/app/designer/CptxAppTest.java @@ -0,0 +1,54 @@ +package com.fr.nx.app.designer; + +import com.fr.file.AbstractFILE; +import com.fr.file.FILE; +import com.fr.main.impl.WorkBook; +import com.fr.nx.app.designer.utils.DesignerCptxFileUtils; +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.junit.runners.MethodSorters; +import org.powermock.api.easymock.PowerMock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.modules.junit4.PowerMockRunnerDelegate; + + +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@RunWith(PowerMockRunner.class) +@PowerMockRunnerDelegate(JUnit4.class) +@PrepareForTest({ + DesignerCptxFileUtils.class +}) +public class CptxAppTest { + + @Test + public void testDefaultExtensions() { + CptxApp app = new CptxApp(); + String[] extendsions = app.defaultExtensions(); + Assert.assertEquals("cptx", extendsions[0]); + } + + @Test + public void testAsIOFile() { + PowerMock.mockStatic(DesignerCptxFileUtils.class); + FILE file = new AbstractFILE() { + }; + WorkBook workBook = new WorkBook(); + EasyMock.expect(DesignerCptxFileUtils.getWorkBook(file)) + .andReturn(workBook).once() + .andReturn(null).once(); + + PowerMock.replay(DesignerCptxFileUtils.class); + Assert.assertSame(workBook, new CptxApp().asIOFile(file)); + + Assert.assertNotNull(new CptxApp().asIOFile(file)); + + PowerMock.verify(DesignerCptxFileUtils.class); + + + } +} diff --git a/designer-realize/src/test/java/com/fr/nx/app/designer/JStreamBookTest.java b/designer-realize/src/test/java/com/fr/nx/app/designer/JStreamBookTest.java new file mode 100644 index 000000000..236096f0d --- /dev/null +++ b/designer-realize/src/test/java/com/fr/nx/app/designer/JStreamBookTest.java @@ -0,0 +1,121 @@ +package com.fr.nx.app.designer; + +import com.fr.file.FILE; +import com.fr.main.impl.WorkBook; +import com.fr.nx.app.designer.toolbar.TemplateTransformer; +import com.fr.nx.app.designer.toolbar.TransformResult; +import com.fr.nx.app.designer.toolbar.TransformResultInfo; +import org.easymock.EasyMock; +import org.easymock.IAnswer; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.powermock.api.easymock.PowerMock; +import org.powermock.core.classloader.annotations.PowerMockIgnore; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor; +import org.powermock.modules.junit4.PowerMockRunner; + +@RunWith(PowerMockRunner.class) +@PrepareForTest({TemplateTransformer.class, JStreamBook.class}) +@PowerMockIgnore({"com.fr.license.*"}) +@SuppressStaticInitializationFor({"com.fr.plugin.designer.JStreamBook"}) +public class JStreamBookTest { + + + @Test + public void saveFile() { + + PowerMock.mockStatic(TemplateTransformer.class); + final boolean[] fireSave = {false}; + + + JStreamBook workbook = + EasyMock.partialMockBuilder(VirtualJStreamBook.class) + .addMockedMethod("fireJTemplateSaved") + .addMockedMethod("getTarget") + .addMockedMethod("getEditingFILE") + .createMock(); + // 保存成功 + EasyMock.expect(TemplateTransformer.compileCPTX(null, null)) + .andReturn(TransformResultInfo.generateResult(TransformResult.UNSUPPORT, "fake Unsupported").saved(true)) + .once(); + PowerMock.replay(TemplateTransformer.class); + + workbook.fireJTemplateSaved(); + EasyMock.expectLastCall().andAnswer(new IAnswer() { + @Override + public Void answer() throws Throwable { + fireSave[0] = true; + return null; + } + }).once(); + + EasyMock.expect(workbook.getTarget()).andAnswer(new IAnswer() { + @Override + public WorkBook answer() throws Throwable { + return null; + } + }).once(); + + EasyMock.expect(workbook.getEditingFILE()).andAnswer(new IAnswer() { + @Override + public FILE answer() throws Throwable { + return null; + } + }).once(); + + EasyMock.replay(workbook); + + boolean saved = workbook.saveFile(); + Assert.assertTrue(saved); + Assert.assertTrue(fireSave[0]); + + PowerMock.verify(TemplateTransformer.class); + EasyMock.verify(workbook); + + fireSave[0] = false; + + // 保存失败 + PowerMock.reset(TemplateTransformer.class); + EasyMock.expect(TemplateTransformer.compileCPTX(null, null)) + .andReturn(TransformResultInfo.generateResult(TransformResult.FAILED, "fake Unsupported").saved(false)) + .once(); + PowerMock.replay(TemplateTransformer.class); + EasyMock.reset(workbook); + + EasyMock.expect(workbook.getTarget()).andAnswer(new IAnswer() { + @Override + public WorkBook answer() throws Throwable { + return null; + } + }).once(); + + EasyMock.expect(workbook.getEditingFILE()).andAnswer(new IAnswer() { + @Override + public FILE answer() throws Throwable { + return null; + } + }).once(); + EasyMock.replay(workbook); + + saved = workbook.saveFile(); + Assert.assertFalse(saved); + Assert.assertFalse(fireSave[0]); + + PowerMock.verify(TemplateTransformer.class); + EasyMock.verify(workbook); + } + + private static final class VirtualJStreamBook extends JStreamBook { + + public VirtualJStreamBook(WorkBook workBook, FILE file) { + super(workBook, file); + } + + @Override + public String toString() { + return this.getClass().getName() + "fake to string"; + } + } +} \ No newline at end of file diff --git a/designer-realize/src/test/resources/cpt/read-write-expand-order.cpt b/designer-realize/src/test/resources/cpt/read-write-expand-order.cpt new file mode 100644 index 000000000..3401f6fcc --- /dev/null +++ b/designer-realize/src/test/resources/cpt/read-write-expand-order.cpt @@ -0,0 +1,79 @@ + + + + +


+ + + + + + + + + + + + + + + + + + + + + + + + + +tC;K&-B[7`5WK?,Xelf@&8+0+s;]Ad+gFe2#R^R0KRcH9&1(_,Q&m?CF2#j +L2g`#\[sRl3^"LL;I*q<:m`bRtAnUh^m(8[R[\Qdap[PoDZI+D(a6:V.`2I2?>R^Sd'MY!@@k@GfZiTaUt[[X*Qn(Vqi1,QX!S[g +X8cV#:o/U(RN!]A[-MCK!$>RYIs_SOW[_9PE1L2Q+u4)L'*_+9GA>FbhqkF3>rH*rsS)m6q"c>/>qr/Vjd(?R(*:-.oLG%<9eZ1]Al^J"e-:(+>+Q1NPOaSJo+/S+i4n&T% +\/J4IBcE%5,_GkC[%jH'kh$2]A+l)FI""N?9]AXFHef&/Q\Pm:q*oGRR,WLM]A._;-d'aOX+8Xcia>YISk1g?.bi%E?!"e!Dq4AIRE5*J06XZ"l9/,[%5lSYHM*Gsc;De0X(/o,#On+F;om!UF7s$[mR(>_*`AIpT;n1NSp*r +5Md*5kOL1RpseeaT5!G,kk_1&F%\@jJs#W%4nbD$X(dC(\O+&KX.Vp>]AY-LoL^V=LM7!$S/!&hR'nH_0hm_H,f=2p+p`R&".Le>"lHKmKAHnSIm69U'g +LYG,>[,VlJ&!5>PutHqRJ4XG^$7ZL6(YG+-e=+b;LUuH3Zrc58GNFlF?j1-+QXQ(%S6.V?:G +1jf/GeC6q@_Q>S5"##8tiN'(`qU.T7U?<]A'i8Uhe/g^>U%!HX@Al,.N83)TcC)q$'k_OQ-?: +_D-F="?_k3O8O[DbknE3?fn3U+EJ3:q;#XccA+E^R:k_hA7K7g.e*(3U#a[& +%V(jo6,q3)+CL/"I32M"cKPBGLn2M%"r'=faV2lDBHCm%MHBI]AOIZj%Z*1+*FVK8J!g'*3c# +"WMR3*Y&1h_H@PEJC*H;"Y@nbp:f4UB61F[-H4phL"^(VI"gQST8h*)bt+Jo(MXVpo+u.k@X +tpaqkFs9$ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/designer-realize/src/test/resources/cpt/read-write-image-ref.cpt b/designer-realize/src/test/resources/cpt/read-write-image-ref.cpt new file mode 100644 index 000000000..62797764a --- /dev/null +++ b/designer-realize/src/test/resources/cpt/read-write-image-ref.cpt @@ -0,0 +1,78 @@ + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + +tC;K&-B[7`5WK?,Xelf@&8+0+s;]Ad+gFe2#R^R0KRcH9&1(_,Q&m?CF2#j +L2g`#\[sRl3^"LL;I*q<:m`bRtAnUh^m(8[R[\Qdap[PoDZI+D(a6:V.`2I2?>R^Sd'MY!@@k@GfZiTaUt[[X*Qn(Vqi1,QX!S[g +X8cV#:o/U(RN!]A[-MCK!$>RYIs_SOW[_9PE1L2Q+u4)L'*_+9GA>FbhqkF3>rH*rsS)m6q"c>/>qr/Vjd(?R(*:-.oLG%<9eZ1]Al^J"e-:(+>+Q1NPOaSJo+/S+i4n&T% +\/J4IBcE%5,_GkC[%jH'kh$2]A+l)FI""N?9]AXFHef&/Q\Pm:q*oGRR,WLM]A._;-d'aOX+8Xcia>YISk1g?.bi%E?!"e!Dq4AIRE5*J06XZ"l9/,[%5lSYHM*Gsc;De0X(/o,#On+F;om!UF7s$[mR(>_*`AIpT;n1NSp*r +5Md*5kOL1RpseeaT5!G,kk_1&F%\@jJs#W%4nbD$X(dC(\O+&KX.Vp>]AY-LoL^V=LM7!$S/!&hR'nH_0hm_H,f=2p+p`R&".Le>"lHKmKAHnSIm69U'g +LYG,>[,VlJ&!5>PutHqRJ4XG^$7ZL6(YG+-e=+b;LUuH3Zrc58GNFlF?j1-+QXQ(%S6.V?:G +1jf/GeC6q@_Q>S5"##8tiN'(`qU.T7U?<]A'i8Uhe/g^>U%!HX@Al,.N83)TcC)q$'k_OQ-?: +_D-F="?_k3O8O[DbknE3?fn3U+EJ3:q;#XccA+E^R:k_hA7K7g.e*(3U#a[& +%V(jo6,q3)+CL/"I32M"cKPBGLn2M%"r'=faV2lDBHCm%MHBI]AOIZj%Z*1+*FVK8J!g'*3c# +"WMR3*Y&1h_H@PEJC*H;"Y@nbp:f4UB61F[-H4phL"^(VI"gQST8h*)bt+Jo(MXVpo+u.k@X +tpaqkFs9$ + + + + + + + + + + + + + +
+ + + + + + + + + + + +