You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
516 lines
20 KiB
516 lines
20 KiB
package com.fr.design.utils; |
|
|
|
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.gui.UILookAndFeel; |
|
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.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.value.NotNullLazyValue; |
|
import com.fr.workspace.WorkContext; |
|
import org.jetbrains.annotations.NotNull; |
|
|
|
import javax.swing.SwingUtilities; |
|
import javax.swing.UIManager; |
|
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.Enumeration; |
|
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<String> 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<String>() { |
|
@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() { |
|
// p:隐藏对话框的系统标题风格,用look and feel定义的标题风格. |
|
try { |
|
UIManager.setLookAndFeel(UILookAndFeel.class.getName()); |
|
} catch (Exception e) { |
|
FineLoggerFactory.getLogger().error("Substance Raven Graphite failed to initialize"); |
|
} |
|
//获取当前系统语言下设计器用的默认字体 |
|
FRFont guiFRFont = getDefaultGUIFont(); |
|
//指定UIManager中字体 |
|
Enumeration keys = UIManager.getDefaults().keys(); |
|
while (keys.hasMoreElements()) { |
|
String key = keys.nextElement().toString(); |
|
|
|
if (key.endsWith(".font")) { |
|
UIManager.put(key, isTextField(key) ? getNamedFont("Dialog") : guiFRFont); |
|
} |
|
} |
|
} |
|
|
|
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<FRFont> CURRENT_LOCALE_FONT = new NotNullLazyValue<FRFont>() { |
|
@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(StringUtils.EMPTY, new String[]{}, new String[]{}); |
|
} |
|
|
|
/** |
|
* 访问服务器环境 |
|
* |
|
* @param names 参数名字 |
|
* @param values 参数值 |
|
*/ |
|
public static void visitEnvServerByParameters(String baseRoute, String[] names, String[] values) { |
|
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(); |
|
Desktop.getDesktop().browse(new URI(urlPath + baseRoute + postfixOfUri)); |
|
} catch (Exception e) { |
|
FineLoggerFactory.getLogger().error("cannot open the url Successful", e); |
|
} |
|
} else { |
|
try { |
|
String web = GeneralContext.getCurrentAppNameOfEnv(); |
|
String url = "http://localhost:" + DesignerEnvManager.getEnvManager().getEmbedServerPort() |
|
+ "/" + web + "/" + ServerConfig.getInstance().getServletName() + baseRoute |
|
+ postfixOfUri; |
|
ServerStarter.browserURLWithLocalEnv(url); |
|
} catch (Throwable ignored) { |
|
} |
|
} |
|
} |
|
|
|
private static String getWebBrowserPath() { |
|
String urlPath = WorkContext.getCurrent().getPath(); |
|
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<DefaultValueAdjustProvider> 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<Locale, String> getAvailableLanguages() { |
|
Map<Locale, String> map = new LinkedHashMap<>(SupportLocale.getInstance().getLocaleMap()); |
|
for (Map.Entry<String, String> entry : DesignExtendLanguageConfig.getInstance().getExtendedDesignLocales().entrySet()) { |
|
map.put(CommonUtils.stringToLocale(entry.getKey()), entry.getValue()); |
|
} |
|
return Collections.unmodifiableMap(map); |
|
} |
|
}
|
|
|