package com.fr.design.utils; import com.fanruan.workplace.http.HttpConstants; import com.fine.theme.light.ui.laf.FineLightLaf; import com.formdev.flatlaf.extras.FlatUIDefaultsInspector; import com.fr.base.FeedBackInfo; import com.fr.base.ServerConfig; import com.fr.base.Utils; import com.fr.concurrent.NamedThreadFactory; import com.fr.design.DesignerEnvManager; import com.fr.design.ExtraDesignClassManager; import com.fr.design.deeplink.DeepLinkCore; import com.fr.design.dialog.FineJOptionPane; import com.fr.design.fun.DefaultValueAdjustProvider; import com.fr.design.fun.DesignerEnvProcessor; import com.fr.design.i18n.DesignExtendLanguageConfig; import com.fr.design.i18n.Toolkit; import com.fr.design.mainframe.DesignerContext; import com.fr.design.ui.util.UIUtil; import com.fr.exit.DesignerExiter; import com.fr.file.FileFILE; import com.fr.general.ComparatorUtils; import com.fr.general.FRFont; import com.fr.general.GeneralContext; import com.fr.general.LocaleType; import com.fr.general.SupportLocale; import com.fr.log.FineLoggerFactory; import com.fr.plugin.injectable.PluginModule; import com.fr.stable.ArrayUtils; import com.fr.stable.CommonCodeUtils; import com.fr.stable.CommonUtils; import com.fr.stable.StableUtils; import com.fr.stable.StringUtils; import com.fr.stable.bridge.ObjectHolder; import com.fr.stable.os.OperatingSystem; import com.fr.start.ServerStarter; import com.fr.start.common.DesignerStartupContext; import com.fr.start.common.DesignerStartupUtil; import com.fr.start.server.DesignEmbedHelper; import com.fr.value.NotNullLazyValue; import com.fr.workspace.WorkContext; import org.jetbrains.annotations.NotNull; import javax.swing.SwingUtilities; import java.awt.Desktop; import java.awt.Font; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * Some util method of Designer */ public class DesignUtils { private static Integer port; private static boolean started = false; private DesignUtils() { } public synchronized static void setPort(int port) { DesignUtils.port = port; } public synchronized static int getPort() { if (port == null) { setPort(DesignerPort.getInstance().getMessagePort()); } return port; } /** * 判断设计器有没有启动 * * @return 启动了返回true */ public static boolean isStarted() { return started; } /** * 判断设计器端口是否被其他程序占用 * 尝试去通信,无回应就是其他程序占用端口,否则需要继续判断是否为设计器进程未关闭 * * @return */ public static boolean isPortOccupied() { ExecutorService executor = null; Future future = null; try (Socket socket = new Socket("localhost", getPort()); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8)); PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8)))) { writer.println("check"); writer.flush(); executor = Executors.newSingleThreadExecutor(); future = executor.submit(new Callable() { @Override public String call() throws Exception { String line; while ((line = reader.readLine()) != null) { if ("response".equals(line)) { // 正常通信 上一次设计器进程未关闭 started = true; return line; } } return StringUtils.EMPTY; } }); future.get(2, TimeUnit.SECONDS); return false; } catch (TimeoutException e) { future.cancel(true); return true; } catch (Exception ignore) { } finally { if (executor != null) { executor.shutdownNow(); } } return false; } /** * 向服务器发送命令行,给服务器端处理 * * @param lines 命令行 */ public static void clientSend(String[] lines, Socket socket) { if (lines == null || lines.length == 0) { return; } PrintWriter writer = null; try { writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8))); for (int i = 0; i < lines.length; i++) { writer.println(lines[i]); } writer.flush(); } catch (Exception e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } finally { if (writer != null) { writer.close(); } } } public static void clientSend(String[] lines) { if (lines == null || lines.length == 0) { return; } try (Socket socket = new Socket("localhost", getPort())) { clientSend(lines, socket); } catch (Exception ignore) { } } /** * 建立监听端口 * * @param startPort 端口 * @param suffixs 文件后缀 */ @SuppressWarnings("squid:S2095") public static void createListeningServer(final int startPort, final String[] suffixs) { ExecutorService service = Executors.newSingleThreadExecutor(new NamedThreadFactory("DesignClientListener")); service.execute(new Runnable() { @Override public void run() { ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(startPort); } catch (IOException e1) { FineLoggerFactory.getLogger().error("Cannot create server socket on " + getPort()); } while (true) { try { if (serverSocket != null) { // 接收客户连接 Socket socket = serverSocket.accept(); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8)); String line; while ((line = reader.readLine()) != null) { if (line.startsWith("demo")) { DesignerEnvManager.getEnvManager().setCurrentEnv2Default(); ServerStarter.browserDemoURL(); } else if ("check".equals(line)) { clientSend(new String[]{"response"}, socket); } else if ("end".equals(line)) { DesignerExiter.getInstance().execute(); } else if (StringUtils.isNotEmpty(line)) { File f = new File(line); String path = f.getAbsolutePath(); boolean isMatch = false; for (String suffix : suffixs) { isMatch = isMatch || path.endsWith(suffix); } if (isMatch) { // ui线程作为打开入口 UIUtil.invokeLaterIfNeeded(new Runnable() { @Override public void run() { DesignerStartupContext context = DesignerStartupContext.getInstance(); // 如果在启动页展示中 if (DesignerStartupUtil.openTemplateIfOnWaiting(f)) { return; } // 如果是在启动中 if (context.isOnStartup()) { // 之前就有这样的问题 return; } // 打开模板 DesignerContext.getDesignerFrame().openTemplate(new FileFILE(f)); } }); } else if (DeepLinkCore.getInstance().accept(line)) { String url = line; UIUtil.invokeLaterIfNeeded(new Runnable() { @Override public void run() { DeepLinkCore.getInstance().receiveDeeplink2(url); } }); } } } reader.close(); socket.close(); } else { FineLoggerFactory.getLogger().error("Cannot create server socket on " + getPort()); break; } } catch (IOException ignored) { } } } }); } public static void responseToClient(Socket socket) { try (OutputStream outputStream = socket.getOutputStream()) { outputStream.write("reponse".getBytes(StandardCharsets.UTF_8)); outputStream.flush(); } catch (IOException ignore) { } } /** * 弹出对话框,显示报错 * * @param message 报错信息 */ public static void errorMessage(String message) { final String final_msg = message; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { FineJOptionPane.showMessageDialog(DesignerContext.getDesignerFrame(), final_msg); } }); } public static void refreshDesignerFrame() { // 刷新DesignerFrame里面的面板 SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (DesignerContext.getDesignerFrame() == null) { return; } DesignerContext.getDesignerFrame().refreshEnv(); DesignerContext.getDesignerFrame().repaint();// kunsnat: 切换环境后 刷新下 报表. 比如图表某些风格改变. } }); } /** * p:初始化look and feel, 把一切放到这个里面.可以让多个地方调用. */ public static void initLookAndFeel() { FineLightLaf.setup(); FlatUIDefaultsInspector.install( "ctrl shift alt Y" ); } private static boolean isTextField(String key) { return key.startsWith("TextField.") || key.startsWith("PasswordField."); } /** * 获取当前系统语言下设计器界面使用的默认字体 * * @return 默认字体 */ public static FRFont getDefaultGUIFont() { return CURRENT_LOCALE_FONT.getValue(); } /** * 获取当前系统语言下设计器用的默认字体 */ private static final NotNullLazyValue CURRENT_LOCALE_FONT = new NotNullLazyValue() { @NotNull @Override protected FRFont compute() { FRFont guiFRFont; Locale defaultLocale = GeneralContext.getLocale(); // JDK9 之后宋体在计算label中字母的空间上出现问题,暂时先用雅黑兼容,以后再统一字体 if (StableUtils.getMajorJavaVersion() >= 9 && OperatingSystem.isWindows()) { guiFRFont = getNamedFont("Microsoft YaHei"); } else if (isDisplaySimSun(defaultLocale)) { guiFRFont = getNamedFont("SimSun"); } else { guiFRFont = getNamedFont("Dialog"); } String file = Toolkit.i18nText("Fine-Design_Basic_File"); char displayChar = file.charAt(0); if (!guiFRFont.canDisplay(displayChar)) { //如果不能用默认的语言显示字体, 比如想在英文系统里用中文设计器 //默认语言(中文:宋体, 英文:Tahoma, 其他:Dialog) guiFRFont = getNamedFont("SimSun"); if (!guiFRFont.canDisplay(displayChar)) { //比如想在中文或英文系统里用韩文设计器 guiFRFont = getNamedFont("Dialog"); if (!guiFRFont.canDisplay(displayChar)) { FineLoggerFactory.getLogger().error(Toolkit.i18nText("Fine-Design_Basic_Base_SimSun_Not_Found")); } } } return guiFRFont; } }; private static FRFont getNamedFont(String name) { return FRFont.getInstance(name, Font.PLAIN, 12); } private static boolean isDisplaySimSun(Locale defaultLocale) { return ComparatorUtils.equals(defaultLocale, Locale.SIMPLIFIED_CHINESE); } private static boolean isDisplayDialog(Locale defaultLocale) { return ComparatorUtils.equals(defaultLocale, Locale.TRADITIONAL_CHINESE) || ComparatorUtils.equals(defaultLocale, Locale.JAPANESE) || ComparatorUtils.equals(defaultLocale, Locale.JAPAN) || ComparatorUtils.equals(defaultLocale, Locale.KOREAN) || ComparatorUtils.equals(defaultLocale, Locale.KOREA); } /** * 默认情况下访问服务器环境 */ public static void visitEnvServer() { visitEnvServerByParameters(ServerStarter.DEFAULT_SERVER_BASE_ROUTE, new String[]{}, new String[]{}); } /** * 访问服务器环境 * * @param names 参数名字 * @param values 参数值 */ public static void visitEnvServerByParameters(String baseRoute, String[] names, String[] values) { visitEnvServerByParameters(baseRoute, names, values, HttpConstants.PUBLIC); } /** * 访问服务器环境 * * @param names 参数名字 * @param values 参数值 */ public static void visitEnvServerByParameters(String baseRoute, String[] names, String[] values, String serverType) { int len = Math.min(ArrayUtils.getLength(names), ArrayUtils.getLength(values)); String[] nameValuePairs = new String[len]; for (int i = 0; i < len; i++) { //设计器里面据说为了改什么界面统一, 把分隔符统一用File.separator, 意味着在windows里面报表路径变成了\ //以前的超链, 以及预览url什么的都是/, 产品组的意思就是用到的地方替换下, 真恶心. String value = values[i].replaceAll("\\\\", "/"); // 两次 encode 支持中文和特殊符号,避免跳转登录后预览400报错 nameValuePairs[i] = CommonCodeUtils.encodeURIComponent(CommonCodeUtils.encodeURIComponent(names[i])) + "=" + CommonCodeUtils.encodeURIComponent(CommonCodeUtils.encodeURIComponent(value)); } String postfixOfUri = (nameValuePairs.length > 0 ? "?" + StableUtils.join(nameValuePairs, "&") : StringUtils.EMPTY); if (!WorkContext.getCurrent().isLocal()) { try { String urlPath = getWebBrowserPath(serverType); Desktop.getDesktop().browse(new URI(urlPath + baseRoute + postfixOfUri)); } catch (Exception e) { FineLoggerFactory.getLogger().error("cannot open the url Successful", e); } } else { try { String web = DesignEmbedHelper.getAppFolderName(); String url = "http://localhost:" + DesignerEnvManager.getEnvManager().getEmbedServerPort() + "/" + web + "/" + ServerConfig.getInstance().getServletName() + baseRoute + postfixOfUri; ServerStarter.browserURLWithLocalEnv(url); } catch (Throwable ignored) { } } } private static String getWebBrowserPath(String serverPath) { String urlPath = WorkContext.getCurrent().getPath() + serverPath; DesignerEnvProcessor processor = ExtraDesignClassManager.getInstance().getSingle(DesignerEnvProcessor.XML_TAG); if (processor != null) { //cas访问的时候, url要处理下. urlPath = processor.getWebBrowserURL(urlPath); } return urlPath; } /** * 将用户反馈发送至服务器 * * @param feedBackInfo 用户反馈 * @return 发送成功返回true * @throws Exception 异常 */ public static boolean sendFeedBack(FeedBackInfo feedBackInfo) throws Exception { return true; } /** * FR25.0版本出来需要进行适配下 * * @param xmlDesignerVersion * @return */ public static String parseVersion(String xmlDesignerVersion) { if (StringUtils.isNotEmpty(xmlDesignerVersion)) { return String.valueOf(xmlDesignerVersion.charAt(0) - 'A'); } return xmlDesignerVersion; } public static DefaultValueAdjustProvider getValueAdjust() { ExtraDesignClassManager extraDesignClassManager = PluginModule.getAgent(PluginModule.ExtraDesign); if (extraDesignClassManager != null) { Set providers = extraDesignClassManager.getArray(DefaultValueAdjustProvider.MARK_STRING); for (DefaultValueAdjustProvider provider : providers) { if (provider.selector().accept(new ObjectHolder())) { return provider; } } } return null; } /** * 获取设计器可用字体 * * @return */ public static String[] getAvailableFontFamilyNames4Report() { DefaultValueAdjustProvider valueAdjust = DesignUtils.getValueAdjust(); if (valueAdjust != null) { return valueAdjust.getAvailableFontFamilyNames4Report(); } return Utils.getAvailableFontFamilyNames4Report(); } /** * 获取设计器可用的开放的语言配置,包括默认开放的五种语言和finedb扩展项 * @return Locale和翻译对应的key */ public static Map getAvailableLanguages() { Map map = new LinkedHashMap<>(SupportLocale.getInstance().getLocaleMap()); map.remove(LocaleType.RU.getLocale()); for (Map.Entry entry : DesignExtendLanguageConfig.getInstance().getExtendedDesignLocales().entrySet()) { map.put(CommonUtils.stringToLocale(entry.getKey()), entry.getValue()); } return Collections.unmodifiableMap(map); } }