Merge pull request #557 from Zhuoyuan1/dev

DBeaver导入链接实现
This commit is contained in:
ji
2023-10-13 21:54:46 +08:00
committed by GitHub
7 changed files with 647 additions and 15 deletions

View File

@ -57,7 +57,7 @@ public class ConverterController {
// 验证文件后缀
String fileExtension = FileUtils.getFileExtension(Objects.requireNonNull(file.getOriginalFilename()));
if (!fileExtension.equalsIgnoreCase(FileUtils.ConfigFile.DBP.name())) {
return DataResult.error("1", "上传的文件必须是ncx文件!");
return DataResult.error("1", "上传的文件必须是dbp文件!");
}
File temp = new File(ConfigUtils.CONFIG_BASE_PATH + File.separator + UUID.randomUUID() + ".tmp");
file.transferTo(temp);

View File

@ -0,0 +1,29 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2023 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ai.chat2db.server.web.api.controller.ncx.dbeaver;
/**
* Value encryptor
*/
public interface DBSValueEncryptor {
byte[] encryptValue(byte[] value);
byte[] decryptValue(byte[] value);
}

View File

@ -0,0 +1,105 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2023 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ai.chat2db.server.web.api.controller.ncx.dbeaver;
import org.apache.poi.util.IOUtils;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
/**
* Default value encryptor.
*
* Uses Eclipse secure preferences to read/write secrets.
*/
public class DefaultValueEncryptor implements DBSValueEncryptor {
public static final String CIPHER_NAME = "AES/CBC/PKCS5Padding";
public static final String KEY_ALGORITHM = "AES";
private static final byte[] LOCAL_KEY_CACHE = new byte[] { -70, -69, 74, -97, 119, 74, -72, 83, -55, 108, 45, 101, 61, -2, 84, 74 };
private final SecretKey secretKey;
private final Cipher cipher;
public DefaultValueEncryptor(SecretKey secretKey) {
this.secretKey = secretKey;
try {
this.cipher = Cipher.getInstance(CIPHER_NAME);
} catch (Exception e) {
throw new IllegalStateException("Internal error during encrypted init", e);
}
}
/**
* 通过 DBeaver 源码查看到默认的 SecretKey
**/
public static SecretKey getLocalSecretKey() {
return new SecretKeySpec(LOCAL_KEY_CACHE, DefaultValueEncryptor.KEY_ALGORITHM);
}
public static SecretKey makeSecretKeyFromPassword(String password) {
byte[] bytes = password.getBytes(StandardCharsets.UTF_8);
byte[] passBytes = Arrays.copyOf(bytes, 16);
return new SecretKeySpec(passBytes, KEY_ALGORITHM);
}
@Override
public byte[] encryptValue(byte[] value) {
try {
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] iv = cipher.getIV();
ByteArrayOutputStream resultBuffer = new ByteArrayOutputStream();
try (CipherOutputStream cipherOut = new CipherOutputStream(resultBuffer, cipher)) {
resultBuffer.write(iv);
cipherOut.write(value);
}
return resultBuffer.toByteArray();
} catch (Exception e) {
throw new RuntimeException("Error encrypting value", e);
}
}
@Override
public byte[] decryptValue(byte[] value) {
try (InputStream byteStream = new ByteArrayInputStream(value)) {
byte[] fileIv = new byte[16];
byteStream.read(fileIv);
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(fileIv));
try (CipherInputStream cipherIn = new CipherInputStream(byteStream, cipher)) {
ByteArrayOutputStream resultBuffer = new ByteArrayOutputStream();
IOUtils.copy(cipherIn, resultBuffer);
return resultBuffer.toByteArray();
}
} catch (Exception e) {
throw new RuntimeException("Error decrypting value", e);
}
}
}

View File

@ -37,31 +37,31 @@ public enum DataBaseType {
/**
* Mariadb
**/
Mariadb("jdbc:mariadb://%s:%s"),
MARIADB("jdbc:mariadb://%s:%s"),
/**
* DM
**/
DM("jdbc:dm://%s:%s"),
/**
* KINGBASE8
* KINGBASE
**/
KINGBASE8("jdbc:kingbase8://%s:%s"),
KINGBASE("jdbc:kingbase8://%s:%s"),
/**
* Presto
**/
Presto("jdbc:presto://%s:%s"),
PRESTO("jdbc:presto://%s:%s"),
/**
* OceanBase
**/
OceanBase("jdbc:oceanbase://%s:%s"),
OCEANBASE("jdbc:oceanbase://%s:%s"),
/**
* Hive
**/
Hive("jdbc:hive2://%s:%s"),
HIVE("jdbc:hive2://%s:%s"),
/**
* ClickHouse
**/
ClickHouse("jdbc:clickhouse://%s:%s");
CLICKHOUSE("jdbc:clickhouse://%s:%s");
private String urlString;
@ -76,7 +76,8 @@ public enum DataBaseType {
public static DataBaseType matchType(String value) {
if (StringUtils.isNotEmpty(value)) {
for (DataBaseType dataBase : DataBaseType.values()) {
if (dataBase.name().equals(value.toUpperCase())) {
//kingbase -> kingbase8
if (value.toUpperCase().contains(dataBase.name())) {
return dataBase;
}
}

View File

@ -0,0 +1,58 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2023 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ai.chat2db.server.web.api.controller.ncx.enums;
/**
* Import/Export constants
*/
public class ExportConstants {
public static final String ARCHIVE_FILE_EXT = ".dbp"; //NON-NLS-1
public static final String CONFIG_FILE = ".dbeaver"; //NON-NLS-1
public static final String CONFIG_DATASOURCE_FILE = "data-sources.json"; //NON-NLS-1
public static final String CONFIG_CREDENTIALS_FILE = "credentials-config.json"; //NON-NLS-1
public static final String DIR_PROJECTS = "projects"; //NON-NLS-1
public static final String DIR_DRIVERS = "drivers"; //NON-NLS-1
public static final String DIR_CONNECTIONS = "connections"; //NON-NLS-1
public static final String DIR_CONFIGURATION = "configuration"; //NON-NLS-1
public static final String GENERIC = "generic"; //NON-NLS-1
public static final String META_FILENAME = "meta.xml"; //NON-NLS-1
public static final String TAG_ARCHIVE = "archive"; //NON-NLS-1
public static final String TAG_SOURCE = "source";
public static final String TAG_PROJECTS = "projects"; //NON-NLS-1
public static final String TAG_PROJECT = "project"; //NON-NLS-1
public static final String TAG_RESOURCE = "resource"; //NON-NLS-1
public static final String TAG_ATTRIBUTE = "attribute"; //NON-NLS-1
public static final String TAG_LIBRARIES = "libraries"; //NON-NLS-1
public static final String ATTR_VERSION = "version"; //NON-NLS-1
public static final String ATTR_HOST = "host";
public static final String ATTR_ADDRESS = "address";
public static final String ATTR_TIME = "time";
public static final String ATTR_QUALIFIER = "qualifier"; //NON-NLS-1
public static final String ATTR_NAME = "name"; //NON-NLS-1
public static final String ATTR_VALUE = "value"; //NON-NLS-1
public static final String ATTR_DIRECTORY = "directory"; //NON-NLS-1
public static final String ATTR_DESCRIPTION = "description"; //NON-NLS-1
public static final String ATTR_CHARSET = "charset"; //NON-NLS-1
public static final String ATTR_PATH = "path"; //NON-NLS-1
public static final String ATTR_FILE = "file"; //NON-NLS-1
}

View File

@ -3,31 +3,52 @@ package ai.chat2db.server.web.api.controller.ncx.service.impl;
import ai.chat2db.server.domain.core.util.DesUtil;
import ai.chat2db.server.domain.repository.entity.DataSourceDO;
import ai.chat2db.server.domain.repository.mapper.DataSourceMapper;
import ai.chat2db.server.tools.common.util.ConfigUtils;
import ai.chat2db.server.tools.common.util.ContextUtils;
import ai.chat2db.server.web.api.controller.ncx.cipher.CommonCipher;
import ai.chat2db.server.web.api.controller.ncx.dbeaver.DefaultValueEncryptor;
import ai.chat2db.server.web.api.controller.ncx.enums.DataBaseType;
import ai.chat2db.server.web.api.controller.ncx.enums.ExportConstants;
import ai.chat2db.server.web.api.controller.ncx.enums.VersionEnum;
import ai.chat2db.server.web.api.controller.ncx.factory.CipherFactory;
import ai.chat2db.server.web.api.controller.ncx.service.ConverterService;
import ai.chat2db.server.web.api.controller.ncx.vo.UploadVO;
import ai.chat2db.server.web.api.util.XMLUtils;
import ai.chat2db.spi.model.SSHInfo;
import cn.hutool.core.io.FileUtil;
import com.alibaba.excel.util.FileUtils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.w3c.dom.*;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.*;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* ConverterServiceImpl
@ -51,6 +72,10 @@ public class ConverterServiceImpl implements ConverterService {
* xml连接信息开始标志位
**/
private static final String BEGIN = "#BEGIN#";
/**
* 密码json的key
**/
private static final String connection = "#connection";
@Autowired
private DataSourceMapper dataSourceMapper;
@ -65,7 +90,6 @@ public class ConverterServiceImpl implements ConverterService {
@Override
public UploadVO uploadFile(File file) {
UploadVO vo = new UploadVO();
try {
// List<Map <连接名Map<属性名,值>>> 要导入的连接
@ -117,10 +141,130 @@ public class ConverterServiceImpl implements ConverterService {
return vo;
}
@SneakyThrows
@Override
public UploadVO dbpUploadFile(File file) {
return null;
UploadVO vo = new UploadVO();
Document metaTree;
//等待删除的projects
List<String> projects = new ArrayList<>();
try (ZipFile zipFile = new ZipFile(file, ZipFile.OPEN_READ)) {
ZipEntry metaEntry = zipFile.getEntry(ExportConstants.META_FILENAME);
if (metaEntry == null) {
throw new RuntimeException("Cannot find meta file");
}
try (InputStream metaStream = zipFile.getInputStream(metaEntry)) {
metaTree = XMLUtils.parseDocument(metaStream);
} catch (Exception e) {
throw new RuntimeException("Cannot parse meta file: " + e.getMessage());
}
Element projectsElement = XMLUtils.getChildElement(metaTree.getDocumentElement(), ExportConstants.TAG_PROJECTS);
if (projectsElement != null) {
final Collection<Element> projectList = XMLUtils.getChildElementList(projectsElement, ExportConstants.TAG_PROJECT);
for (Element projectElement : projectList) {
//获取项目名称
String projectName = projectElement.getAttribute(ExportConstants.ATTR_NAME);
//导入匹配文件目录
String config = ConfigUtils.CONFIG_BASE_PATH + File.separator + projectName + File.separator + ExportConstants.CONFIG_FILE;
importDbeaverConfig(new File(config),
projectElement,
//不可替换成File.separator
ExportConstants.DIR_PROJECTS + "/" + projectName + "/",
zipFile);
//加入删除名单
projects.add(projectName);
//配置的json文件
File json = new File(config + File.separator + ExportConstants.CONFIG_DATASOURCE_FILE);
JSONObject jsonObject = JSON.parseObject(new FileInputStream(json));
JSONObject connections = jsonObject.getJSONObject(ExportConstants.DIR_CONNECTIONS);
Set<String> keys = connections.keySet();
for (String key : keys) {
JSONObject configurations = connections.getJSONObject(key);
JSONObject configuration = configurations.getJSONObject(ExportConstants.DIR_CONFIGURATION);
//匹配数据库类型
String provider = configurations.getString("provider");
if (provider.equals(ExportConstants.GENERIC)) {
//自定义驱动
JSONObject drivers = jsonObject.getJSONObject(ExportConstants.DIR_DRIVERS);
//获得驱动id
String driverId = configurations.getString("driver");
//获得所有generic
JSONObject generics = drivers.getJSONObject(provider);
//获得自己的驱动
JSONObject generic = generics.getJSONObject(driverId);
//如果不存在,则不导入
if (null == generic) {
continue;
}
//赋值驱动名称,用来确定数据库的类型
provider = generic.getString("name");
}
DataBaseType dataBaseType = DataBaseType.matchType(provider.toUpperCase());
DataSourceDO dataSourceDO;
//未匹配到数据库类型dbeaver支持自定义驱动等但chat2DB暂不支持
if (null != dataBaseType) {
//密码信息
File credentials = new File(config + File.separator + ExportConstants.CONFIG_CREDENTIALS_FILE);
DefaultValueEncryptor defaultValueEncryptor = new DefaultValueEncryptor(DefaultValueEncryptor.getLocalSecretKey());
JSONObject credentialsJson = JSON.parseObject(defaultValueEncryptor.decryptValue(Files.readAllBytes(credentials.toPath())));
dataSourceDO = new DataSourceDO();
Date dateTime = new Date();
dataSourceDO.setGmtCreate(dateTime);
dataSourceDO.setGmtModified(dateTime);
//插入用户id
dataSourceDO.setUserId(ContextUtils.getUserId());
dataSourceDO.setAlias(configurations.getString("name"));
dataSourceDO.setHost(configuration.getString("host"));
dataSourceDO.setPort(configuration.getString("port"));
dataSourceDO.setUrl(configuration.getString("url"));
//ssh设置为false
SSHInfo sshInfo = new SSHInfo();
sshInfo.setUse(false);
dataSourceDO.setSsh(JSON.toJSONString(sshInfo));
if (null != credentialsJson) {
JSONObject userInfo = credentialsJson.getJSONObject(key);
JSONObject userPassword = userInfo.getJSONObject(connection);
dataSourceDO.setUserName(userPassword.getString("user"));
DesUtil desUtil = new DesUtil(DesUtil.DES_KEY);
String password = userPassword.getString("password");
String encryptStr = desUtil.encrypt(Optional.ofNullable(password).orElse(""), "CBC");
dataSourceDO.setPassword(encryptStr);
}
dataSourceDO.setType(dataBaseType.name());
dataSourceMapper.insert(dataSourceDO);
}
}
}
}
}
//删除临时文件
FileUtils.delete(file);
//删除导入dbeaver时dbp产生的临时配置文件
projects.forEach(v -> FileUtils.delete(new File(ConfigUtils.CONFIG_BASE_PATH + File.separator + v)));
return vo;
}
@SneakyThrows
private static void importDbeaverConfig(File resource, Element resourceElement, String containerPath, ZipFile zipFile) {
for (Element childElement : XMLUtils.getChildElementList(resourceElement, ExportConstants.TAG_RESOURCE)) {
String childName = childElement.getAttribute(ExportConstants.ATTR_NAME);
String entryPath = containerPath + childName;
ZipEntry resourceEntry = zipFile.getEntry(entryPath);
if (resourceEntry == null) {
continue;
}
boolean isDirectory = resourceEntry.isDirectory();
if (isDirectory) {
File folder = new File(resource.getPath());
if (!folder.exists()) {
FileUtil.mkdir(folder);
}
importDbeaverConfig(folder, childElement, entryPath + "/", zipFile);
} else {
File file = new File(resource.getPath() + File.separator + childName);
FileUtil.writeFromStream(zipFile.getInputStream(resourceEntry), file, true);
}
}
}
@SneakyThrows
@ -154,6 +298,8 @@ public class ConverterServiceImpl implements ConverterService {
dataSourceDO.setGmtCreate(dateTime);
dataSourceDO.setGmtModified(dateTime);
dataSourceDO.setAlias(rootElement.getAttribute("name"));
//插入用户id
dataSourceDO.setUserId(ContextUtils.getUserId());
// 获取子元素 database-info
Element databaseInfoElement = (Element) rootElement.getElementsByTagName("database-info").item(0);
@ -182,6 +328,10 @@ public class ConverterServiceImpl implements ConverterService {
}
}
//ssh设置为false
SSHInfo sshInfo = new SSHInfo();
sshInfo.setUse(false);
dataSourceDO.setSsh(JSON.toJSONString(sshInfo));
dataSourceDO.setHost(host);
dataSourceDO.setPort(port);
dataSourceDO.setUrl(jdbcUrl);
@ -224,6 +374,8 @@ public class ConverterServiceImpl implements ConverterService {
dataSourceDO.setAlias(resultMap.get("ConnectionName"));
dataSourceDO.setUserName(resultMap.get("UserName"));
dataSourceDO.setType(resultMap.get("ConnType"));
//插入用户id
dataSourceDO.setUserId(ContextUtils.getUserId());
//password 为解密出来的密文再使用chat2db的加密
DesUtil desUtil = new DesUtil(DesUtil.DES_KEY);
String encryptStr = desUtil.encrypt(password, "CBC");

View File

@ -0,0 +1,287 @@
/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2023 DBeaver Corp and others
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ai.chat2db.server.web.api.util;
import org.apache.batik.xml.XMLException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Common XML utils
*/
public class XMLUtils {
public static Document parseDocument(String fileName)
throws XMLException {
return parseDocument(new java.io.File(fileName));
}
public static Document parseDocument(java.io.File file) throws XMLException {
try (InputStream is = new FileInputStream(file)) {
return parseDocument(new InputSource(is));
} catch (IOException e) {
throw new XMLException("Error opening file '" + file + "'", e);
}
}
public static Document parseDocument(InputStream is) throws XMLException {
return parseDocument(new InputSource(is));
}
public static Document parseDocument(java.io.Reader is) throws XMLException {
return parseDocument(new InputSource(is));
}
public static Document parseDocument(InputSource source) throws XMLException {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
DocumentBuilder xmlBuilder = dbf.newDocumentBuilder();
return xmlBuilder.parse(source);
} catch (Exception er) {
throw new XMLException("Error parsing XML document", er);
}
}
public static Document createDocument()
throws XMLException {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder xmlBuilder = dbf.newDocumentBuilder();
return xmlBuilder.newDocument();
} catch (Exception er) {
throw new XMLException("Error creating XML document", er);
}
}
public static Element getChildElement(Element element, String childName) {
if (element == null) {
return null;
}
for (Node node = element.getFirstChild(); node != null; node = node.getNextSibling()) {
if (node.getNodeType() == Node.ELEMENT_NODE &&
((Element) node).getTagName().equals(childName)) {
return (Element) node;
}
}
return null;
}
public static String getChildElementBody(Element element, String childName) {
if (element == null) {
return null;
}
for (Node node = element.getFirstChild(); node != null; node = node.getNextSibling()) {
if (node.getNodeType() == Node.ELEMENT_NODE &&
((Element) node).getTagName().equals(childName)) {
return getElementBody((Element) node);
}
}
return null;
}
public static String getElementBody( Element element) {
return element.getTextContent();
}
// Get list of all child elements of specified node
public static List<Element> getChildElementList(
Element parent,
String nodeName) {
List<Element> list = new ArrayList<>();
if (parent != null) {
for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) {
if (node.getNodeType() == Node.ELEMENT_NODE &&
nodeName.equals(node.getNodeName())) {
list.add((Element) node);
}
}
}
return list;
}
// Get list of all child elements of specified node
public static Collection<Element> getChildElementListNS(
Element parent,
String nsURI) {
List<Element> list = new ArrayList<>();
if (parent != null) {
for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) {
if (node.getNodeType() == Node.ELEMENT_NODE &&
node.getNamespaceURI().equals(nsURI)) {
list.add((Element) node);
}
}
}
return list;
}
// Get list of all child elements of specified node
public static Collection<Element> getChildElementListNS(
Element parent,
String nodeName,
String nsURI) {
List<Element> list = new ArrayList<>();
for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) {
if (node.getNodeType() == Node.ELEMENT_NODE &&
node.getLocalName().equals(nodeName) &&
node.getNamespaceURI().equals(nsURI)) {
list.add((Element) node);
}
}
return list;
}
// Get list of all child elements of specified node
public static Collection<Element> getChildElementList(
Element parent,
String[] nodeNameList) {
List<Element> list = new ArrayList<>();
for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
for (int i = 0; i < nodeNameList.length; i++) {
if (node.getNodeName().equals(nodeNameList[i])) {
list.add((Element) node);
}
}
}
}
return list;
}
// Find one child element with specified name
public static Element findChildElement(
Element parent) {
for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
return (Element) node;
}
}
return null;
}
public static Object escapeXml(Object obj) {
if (obj == null) {
return null;
} else if (obj instanceof CharSequence) {
return escapeXml((CharSequence) obj);
} else {
return obj;
}
}
public static String escapeXml(CharSequence str) {
if (str == null) {
return null;
}
StringBuilder res = null;
int strLength = str.length();
for (int i = 0; i < strLength; i++) {
char c = str.charAt(i);
String repl = encodeXMLChar(c);
if (repl == null) {
if (res != null) {
res.append(c);
}
} else {
if (res == null) {
res = new StringBuilder(str.length() + 5);
for (int k = 0; k < i; k++) {
res.append(str.charAt(k));
}
}
res.append(repl);
}
}
return res == null ? str.toString() : res.toString();
}
public static boolean isValidXMLChar(char c) {
return (c >= 32 || c == '\n' || c == '\r' || c == '\t');
}
/**
* Encodes a char to XML-valid form replacing &,',",<,> with special XML encoding.
*
* @param ch char to convert
* @return XML-encoded text
*/
public static String encodeXMLChar(char ch) {
switch (ch) {
case '&':
return "&amp;";
case '\"':
return "&quot;";
case '\'':
return "&#39;";
case '<':
return "&lt;";
case '>':
return "&gt;";
default:
return null;
}
}
public static XMLException adaptSAXException(Exception toCatch) {
if (toCatch instanceof XMLException) {
return (XMLException) toCatch;
} else if (toCatch instanceof org.xml.sax.SAXException) {
String message = toCatch.getMessage();
Exception embedded = ((org.xml.sax.SAXException) toCatch).getException();
if (embedded != null && embedded.getMessage() != null && embedded.getMessage().equals(message)) {
// Just SAX wrapper - skip it
return adaptSAXException(embedded);
} else {
return new XMLException(
message,
embedded != null ? adaptSAXException(embedded) : null);
}
} else {
return new XMLException(toCatch.getMessage(), toCatch);
}
}
public static Collection<Element> getChildElementList(Element element) {
List<Element> children = new ArrayList<>();
if (element != null) {
for (Node node = element.getFirstChild(); node != null; node = node.getNextSibling()) {
if (node.getNodeType() == Node.ELEMENT_NODE) {
children.add((Element) node);
}
}
}
return children;
}
}