mirror of https://github.com/apache/poi.git
#63745 - Refactor EscherRecord.ToXml
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1868353 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
9982ec4e79
commit
69f493ee3a
|
@ -96,27 +96,7 @@ public class GenericRecordJsonWriter implements Closeable {
|
|||
}
|
||||
|
||||
public GenericRecordJsonWriter(Appendable buffer) {
|
||||
fw = new PrintWriter(new Writer(){
|
||||
@Override
|
||||
public void write(char[] cbuf, int off, int len) throws IOException {
|
||||
buffer.append(String.valueOf(cbuf), off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
if (buffer instanceof Flushable) {
|
||||
((Flushable)buffer).flush();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
flush();
|
||||
if (buffer instanceof Closeable) {
|
||||
((Closeable)buffer).close();
|
||||
}
|
||||
}
|
||||
});
|
||||
fw = new PrintWriter(new AppendableWriter(buffer));
|
||||
}
|
||||
|
||||
public static String marshal(GenericRecord record) {
|
||||
|
@ -267,10 +247,9 @@ public class GenericRecordJsonWriter implements Closeable {
|
|||
|
||||
private void printList(Object o) {
|
||||
fw.println('[');
|
||||
final int[] c = new int[1];
|
||||
//noinspection unchecked
|
||||
int oldChildIndex = childIndex;
|
||||
childIndex = 0;
|
||||
//noinspection unchecked
|
||||
((List)o).forEach(e -> { writeValue(e); childIndex++; });
|
||||
childIndex = oldChildIndex;
|
||||
fw.write(']');
|
||||
|
@ -430,14 +409,14 @@ public class GenericRecordJsonWriter implements Closeable {
|
|||
fw.write(']');
|
||||
}
|
||||
|
||||
private String trimHex(final long l, final int size) {
|
||||
static String trimHex(final long l, final int size) {
|
||||
final String b = Long.toHexString(l);
|
||||
int len = b.length();
|
||||
return ZEROS.substring(0, Math.max(0,size-len)) + b.substring(Math.max(0,len-size), len);
|
||||
}
|
||||
|
||||
private static class NullOutputStream extends OutputStream {
|
||||
private NullOutputStream() {
|
||||
static class NullOutputStream extends OutputStream {
|
||||
NullOutputStream() {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -452,4 +431,33 @@ public class GenericRecordJsonWriter implements Closeable {
|
|||
public void write(byte[] b) {
|
||||
}
|
||||
}
|
||||
|
||||
static class AppendableWriter extends Writer {
|
||||
private Appendable buffer;
|
||||
|
||||
AppendableWriter(Appendable buffer) {
|
||||
super(buffer);
|
||||
this.buffer = buffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(char[] cbuf, int off, int len) throws IOException {
|
||||
buffer.append(String.valueOf(cbuf), off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
if (buffer instanceof Flushable) {
|
||||
((Flushable)buffer).flush();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
flush();
|
||||
if (buffer instanceof Closeable) {
|
||||
((Closeable)buffer).close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,480 @@
|
|||
/*
|
||||
* ====================================================================
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* ====================================================================
|
||||
*/
|
||||
|
||||
package org.apache.poi.util;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Dimension2D;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.PathIterator;
|
||||
import java.awt.geom.Point2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.Array;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.AbstractMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
import org.apache.poi.common.usermodel.GenericRecord;
|
||||
import org.apache.poi.util.GenericRecordJsonWriter.AppendableWriter;
|
||||
import org.apache.poi.util.GenericRecordJsonWriter.NullOutputStream;
|
||||
|
||||
public class GenericRecordXmlWriter implements Closeable {
|
||||
private static final String TABS;
|
||||
private static final String ZEROS = "0000000000000000";
|
||||
private static final Pattern ESC_CHARS = Pattern.compile("[<>&'\"\\p{Cntrl}]");
|
||||
|
||||
private static final List<Map.Entry<Class, BiConsumer<GenericRecordXmlWriter,Object>>> handler = new ArrayList<>();
|
||||
|
||||
static {
|
||||
char[] t = new char[255];
|
||||
Arrays.fill(t, '\t');
|
||||
TABS = new String(t);
|
||||
handler(String.class, GenericRecordXmlWriter::printObject);
|
||||
handler(Number.class, GenericRecordXmlWriter::printNumber);
|
||||
handler(Boolean.class, GenericRecordXmlWriter::printBoolean);
|
||||
handler(List.class, GenericRecordXmlWriter::printList);
|
||||
// handler(GenericRecord.class, GenericRecordXmlWriter::printGenericRecord);
|
||||
handler(GenericRecordUtil.AnnotatedFlag.class, GenericRecordXmlWriter::printAnnotatedFlag);
|
||||
handler(byte[].class, GenericRecordXmlWriter::printBytes);
|
||||
handler(Point2D.class, GenericRecordXmlWriter::printPoint);
|
||||
handler(Dimension2D.class, GenericRecordXmlWriter::printDimension);
|
||||
handler(Rectangle2D.class, GenericRecordXmlWriter::printRectangle);
|
||||
handler(Path2D.class, GenericRecordXmlWriter::printPath);
|
||||
handler(AffineTransform.class, GenericRecordXmlWriter::printAffineTransform);
|
||||
handler(Color.class, GenericRecordXmlWriter::printColor);
|
||||
handler(BufferedImage.class, GenericRecordXmlWriter::printBufferedImage);
|
||||
handler(Array.class, GenericRecordXmlWriter::printArray);
|
||||
handler(Object.class, GenericRecordXmlWriter::printObject);
|
||||
}
|
||||
|
||||
private static void handler(Class c, BiConsumer<GenericRecordXmlWriter,Object> printer) {
|
||||
handler.add(new AbstractMap.SimpleEntry<>(c, printer));
|
||||
}
|
||||
|
||||
private final PrintWriter fw;
|
||||
private int indent = 0;
|
||||
private boolean withComments = true;
|
||||
private int childIndex = 0;
|
||||
private boolean attributePhase = true;
|
||||
|
||||
public GenericRecordXmlWriter(File fileName) throws IOException {
|
||||
OutputStream os = ("null".equals(fileName.getName())) ? new NullOutputStream() : new FileOutputStream(fileName);
|
||||
fw = new PrintWriter(new OutputStreamWriter(os, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public GenericRecordXmlWriter(Appendable buffer) {
|
||||
fw = new PrintWriter(new AppendableWriter(buffer));
|
||||
}
|
||||
|
||||
public static String marshal(GenericRecord record) {
|
||||
return marshal(record, true);
|
||||
}
|
||||
|
||||
public static String marshal(GenericRecord record, boolean withComments) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
try (GenericRecordXmlWriter w = new GenericRecordXmlWriter(sb)) {
|
||||
w.setWithComments(withComments);
|
||||
w.write(record);
|
||||
return sb.toString();
|
||||
} catch (IOException e) {
|
||||
return "<record/>";
|
||||
}
|
||||
}
|
||||
|
||||
public void setWithComments(boolean withComments) {
|
||||
this.withComments = withComments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
fw.close();
|
||||
}
|
||||
|
||||
private String tabs() {
|
||||
return TABS.substring(0, Math.min(indent, TABS.length()));
|
||||
}
|
||||
|
||||
public void write(GenericRecord record) {
|
||||
write(record, "record");
|
||||
}
|
||||
|
||||
private void write(GenericRecord record, final String name) {
|
||||
final String tabs = tabs();
|
||||
Enum type = record.getGenericRecordType();
|
||||
String recordName = (type != null) ? type.name() : record.getClass().getSimpleName();
|
||||
fw.append(tabs);
|
||||
fw.append("<"+name+" type=\"");
|
||||
fw.append(recordName);
|
||||
fw.append("\"");
|
||||
if (childIndex > 0) {
|
||||
fw.append(" index=\"");
|
||||
fw.print(childIndex);
|
||||
fw.append("\"");
|
||||
}
|
||||
|
||||
boolean hasChildren = false;
|
||||
|
||||
Map<String, Supplier<?>> prop = record.getGenericProperties();
|
||||
if (prop != null) {
|
||||
final int oldChildIndex = childIndex;
|
||||
childIndex = 0;
|
||||
attributePhase = true;
|
||||
List<Map.Entry<String,Supplier<?>>> complex = prop.entrySet().stream().flatMap(this::writeProp).collect(Collectors.toList());
|
||||
attributePhase = false;
|
||||
if (!complex.isEmpty()) {
|
||||
hasChildren = true;
|
||||
fw.println(">");
|
||||
indent++;
|
||||
complex.forEach(this::writeProp);
|
||||
indent--;
|
||||
}
|
||||
childIndex = oldChildIndex;
|
||||
} else {
|
||||
fw.print(">");
|
||||
}
|
||||
|
||||
attributePhase = false;
|
||||
|
||||
List<? extends GenericRecord> list = record.getGenericChildren();
|
||||
if (list != null && !list.isEmpty()) {
|
||||
hasChildren = true;
|
||||
indent++;
|
||||
fw.println();
|
||||
fw.append(tabs());
|
||||
fw.println("<children>");
|
||||
indent++;
|
||||
final int oldChildIndex = childIndex;
|
||||
childIndex = 0;
|
||||
list.forEach(l -> { writeValue("record", l); childIndex++; });
|
||||
childIndex = oldChildIndex;
|
||||
fw.println();
|
||||
indent--;
|
||||
fw.append(tabs());
|
||||
fw.println("</children>");
|
||||
indent--;
|
||||
}
|
||||
|
||||
if (hasChildren) {
|
||||
fw.append(tabs);
|
||||
fw.println("</" + name + ">");
|
||||
} else {
|
||||
fw.println("/>");
|
||||
}
|
||||
}
|
||||
|
||||
public void writeError(String errorMsg) {
|
||||
fw.append("<error>");
|
||||
printObject(errorMsg);
|
||||
fw.append("</error>");
|
||||
}
|
||||
|
||||
private Stream<Map.Entry<String,Supplier<?>>> writeProp(Map.Entry<String,Supplier<?>> me) {
|
||||
Object obj = me.getValue().get();
|
||||
if (obj == null) {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
final boolean isComplex = isComplex(obj);
|
||||
if (attributePhase == isComplex) {
|
||||
return isComplex ? Stream.of(new AbstractMap.SimpleEntry<>(me.getKey(), () -> obj)) : Stream.empty();
|
||||
}
|
||||
|
||||
final int oldChildIndex = childIndex;
|
||||
childIndex = 0;
|
||||
writeValue(me.getKey(), obj);
|
||||
childIndex = oldChildIndex;
|
||||
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
private static boolean isComplex(Object obj) {
|
||||
return !(
|
||||
obj instanceof Number ||
|
||||
obj instanceof Boolean ||
|
||||
obj instanceof Character ||
|
||||
obj instanceof String ||
|
||||
obj instanceof Color ||
|
||||
obj instanceof Enum);
|
||||
}
|
||||
|
||||
private void writeValue(String key, Object o) {
|
||||
assert(key != null);
|
||||
if (o instanceof GenericRecord) {
|
||||
printGenericRecord((GenericRecord)o, key);
|
||||
} else if (o != null) {
|
||||
if (key.endsWith(">")) {
|
||||
fw.print("\t");
|
||||
}
|
||||
|
||||
fw.print(attributePhase ? " " + key + "=\"" : tabs()+"<" + key);
|
||||
if (key.endsWith(">")) {
|
||||
fw.println();
|
||||
}
|
||||
|
||||
handler.stream().
|
||||
filter(h -> matchInstanceOrArray(h.getKey(), o)).
|
||||
findFirst().
|
||||
ifPresent(h -> h.getValue().accept(this, o));
|
||||
|
||||
if (attributePhase) {
|
||||
fw.append("\"");
|
||||
}
|
||||
|
||||
if (key.endsWith(">")) {
|
||||
fw.println(tabs()+"\t</"+key);
|
||||
} else if (o instanceof List || o.getClass().isArray()) {
|
||||
fw.println(tabs()+"</"+key+">");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean matchInstanceOrArray(Class key, Object instance) {
|
||||
return key.isInstance(instance) || (Array.class.equals(key) && instance.getClass().isArray());
|
||||
}
|
||||
private void printNumber(Object o) {
|
||||
assert(attributePhase);
|
||||
Number n = (Number)o;
|
||||
fw.print(n.toString());
|
||||
|
||||
if (attributePhase) {
|
||||
return;
|
||||
}
|
||||
|
||||
final int size;
|
||||
if (n instanceof Byte) {
|
||||
size = 2;
|
||||
} else if (n instanceof Short) {
|
||||
size = 4;
|
||||
} else if (n instanceof Integer) {
|
||||
size = 8;
|
||||
} else if (n instanceof Long) {
|
||||
size = 16;
|
||||
} else {
|
||||
size = -1;
|
||||
}
|
||||
|
||||
long l = n.longValue();
|
||||
if (withComments && size > 0 && (l < 0 || l > 9)) {
|
||||
fw.write(" /* 0x");
|
||||
fw.write(trimHex(l, size));
|
||||
fw.write(" */");
|
||||
}
|
||||
}
|
||||
|
||||
private void printBoolean(Object o) {
|
||||
fw.write(((Boolean)o).toString());
|
||||
}
|
||||
|
||||
private void printList(Object o) {
|
||||
assert (!attributePhase);
|
||||
fw.println(">");
|
||||
int oldChildIndex = childIndex;
|
||||
childIndex = 0;
|
||||
//noinspection unchecked
|
||||
((List)o).forEach(e -> { writeValue("item>", e); childIndex++; });
|
||||
childIndex = oldChildIndex;
|
||||
}
|
||||
|
||||
private void printArray(Object o) {
|
||||
assert (!attributePhase);
|
||||
fw.println(">");
|
||||
int length = Array.getLength(o);
|
||||
final int oldChildIndex = childIndex;
|
||||
for (childIndex=0; childIndex<length; childIndex++) {
|
||||
writeValue("item>", Array.get(o, childIndex));
|
||||
}
|
||||
childIndex = oldChildIndex;
|
||||
}
|
||||
|
||||
private void printGenericRecord(Object o, String name) {
|
||||
write((GenericRecord) o, name);
|
||||
}
|
||||
|
||||
private void printAnnotatedFlag(Object o) {
|
||||
assert (!attributePhase);
|
||||
GenericRecordUtil.AnnotatedFlag af = (GenericRecordUtil.AnnotatedFlag) o;
|
||||
Number n = af.getValue().get();
|
||||
int len;
|
||||
if (n instanceof Byte) {
|
||||
len = 2;
|
||||
} else if (n instanceof Short) {
|
||||
len = 4;
|
||||
} else if (n instanceof Integer) {
|
||||
len = 8;
|
||||
} else {
|
||||
len = 16;
|
||||
}
|
||||
|
||||
fw.print(" flag=\"0x");
|
||||
fw.print(trimHex(n.longValue(), len));
|
||||
fw.print('"');
|
||||
if (withComments) {
|
||||
fw.print(" description=\"");
|
||||
fw.print(af.getDescription());
|
||||
fw.print("\"");
|
||||
}
|
||||
fw.println("/>");
|
||||
}
|
||||
|
||||
private void printBytes(Object o) {
|
||||
assert (!attributePhase);
|
||||
fw.write(">");
|
||||
fw.write(DatatypeConverter.printBase64Binary((byte[]) o));
|
||||
}
|
||||
|
||||
private void printPoint(Object o) {
|
||||
assert (!attributePhase);
|
||||
Point2D p = (Point2D)o;
|
||||
fw.println(" x=\""+p.getX()+"\" y=\""+p.getY()+"\"/>");
|
||||
}
|
||||
|
||||
private void printDimension(Object o) {
|
||||
assert (!attributePhase);
|
||||
Dimension2D p = (Dimension2D)o;
|
||||
fw.println(" width=\""+p.getWidth()+"\" height=\""+p.getHeight()+"\"/>");
|
||||
}
|
||||
|
||||
private void printRectangle(Object o) {
|
||||
assert (!attributePhase);
|
||||
Rectangle2D p = (Rectangle2D)o;
|
||||
fw.println(" x=\""+p.getX()+"\" y=\""+p.getY()+"\" width=\""+p.getWidth()+"\" height=\""+p.getHeight()+"\"/>");
|
||||
}
|
||||
|
||||
private void printPath(Object o) {
|
||||
assert (!attributePhase);
|
||||
final PathIterator iter = ((Path2D)o).getPathIterator(null);
|
||||
final double[] pnts = new double[6];
|
||||
|
||||
indent += 2;
|
||||
String t = tabs();
|
||||
indent -= 2;
|
||||
|
||||
boolean isNext = false;
|
||||
while (!iter.isDone()) {
|
||||
fw.print(t);
|
||||
isNext = true;
|
||||
final int segType = iter.currentSegment(pnts);
|
||||
fw.print("<pathelement ");
|
||||
switch (segType) {
|
||||
case PathIterator.SEG_MOVETO:
|
||||
fw.print("type=\"move\" x=\""+pnts[0]+"\" y=\""+pnts[1]+"\"");
|
||||
break;
|
||||
case PathIterator.SEG_LINETO:
|
||||
fw.print("type=\"lineto\" x=\""+pnts[0]+"\" y=\""+pnts[1]+"\"");
|
||||
break;
|
||||
case PathIterator.SEG_QUADTO:
|
||||
fw.print("type=\"quad\" x1=\""+pnts[0]+"\" y1=\""+pnts[1]+"\" x2=\""+pnts[2]+"\" y2=\""+pnts[3]+"\"");
|
||||
break;
|
||||
case PathIterator.SEG_CUBICTO:
|
||||
fw.print("type=\"cubic\" x1=\""+pnts[0]+"\" y1=\""+pnts[1]+"\" x2=\""+pnts[2]+"\" y2=\""+pnts[3]+"\" x3=\""+pnts[4]+"\" y3=\""+pnts[5]+"\"");
|
||||
break;
|
||||
case PathIterator.SEG_CLOSE:
|
||||
fw.print("type=\"close\"");
|
||||
break;
|
||||
}
|
||||
fw.println("/>");
|
||||
iter.next();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void printObject(Object o) {
|
||||
final Matcher m = ESC_CHARS.matcher(o.toString());
|
||||
final StringBuffer sb = new StringBuffer();
|
||||
while (m.find()) {
|
||||
String repl;
|
||||
String match = m.group();
|
||||
switch (match) {
|
||||
case "<":
|
||||
repl = "<";
|
||||
break;
|
||||
case ">":
|
||||
repl = ">";
|
||||
break;
|
||||
case "&":
|
||||
repl = "&";
|
||||
break;
|
||||
case "\'":
|
||||
repl = "'";
|
||||
break;
|
||||
case "\"":
|
||||
repl = """;
|
||||
break;
|
||||
default:
|
||||
repl = "&#x" + Long.toHexString(match.codePointAt(0)) + ";";
|
||||
break;
|
||||
}
|
||||
m.appendReplacement(sb, repl);
|
||||
}
|
||||
m.appendTail(sb);
|
||||
fw.write(sb.toString());
|
||||
}
|
||||
|
||||
private void printAffineTransform(Object o) {
|
||||
assert (!attributePhase);
|
||||
AffineTransform xForm = (AffineTransform)o;
|
||||
fw.write(
|
||||
" scaleX=\""+xForm.getScaleX()+"\" "+
|
||||
"shearX=\""+xForm.getShearX()+"\" "+
|
||||
"transX=\""+xForm.getTranslateX()+"\" "+
|
||||
"scaleY=\""+xForm.getScaleY()+"\" "+
|
||||
"shearY=\""+xForm.getShearY()+"\" "+
|
||||
"transY=\""+xForm.getTranslateY()+"\"/>");
|
||||
}
|
||||
|
||||
private void printColor(Object o) {
|
||||
assert (attributePhase);
|
||||
final int rgb = ((Color)o).getRGB();
|
||||
fw.print("0x");
|
||||
fw.print(trimHex(rgb, 8));
|
||||
}
|
||||
|
||||
private void printBufferedImage(Object o) {
|
||||
assert (!attributePhase);
|
||||
BufferedImage bi = (BufferedImage)o;
|
||||
fw.println(" width=\""+bi.getWidth()+"\" height=\""+bi.getHeight()+"\" bands=\""+bi.getColorModel().getNumComponents()+"\"/>");
|
||||
}
|
||||
|
||||
private String trimHex(final long l, final int size) {
|
||||
final String b = Long.toHexString(l);
|
||||
int len = b.length();
|
||||
return ZEROS.substring(0, Math.max(0,size-len)) + b.substring(Math.max(0,len-size), len);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue