blob: f6b2c9e4cd33bd82e541a151e1e233fe005ab5a6 [file] [log] [blame]
/*
* 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.solr.common.util;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.solr.common.EnumFieldValue;
import org.apache.solr.common.IteratorWriter;
import org.apache.solr.common.MapSerializable;
import org.apache.solr.common.MapWriter;
import org.apache.solr.common.PushWriter;
//Base interface for all text based writers
public interface TextWriter extends PushWriter {
default void writeVal(String name, Object val) throws IOException {
// if there get to be enough types, perhaps hashing on the type
// to get a handler might be faster (but types must be exact to do that...)
// (see a patch on LUCENE-3041 for inspiration)
// go in order of most common to least common, however some of the more general types like Map belong towards the end
if (val == null) {
writeNull(name);
} else if (val instanceof CharSequence) {
writeStr(name, val.toString(), true);
// micro-optimization... using toString() avoids a cast first
} else if (val instanceof Number) {
writeNumber(name, (Number) val);
} else if (val instanceof Boolean) {
writeBool(name, (Boolean) val);
} else if (val instanceof AtomicBoolean) {
writeBool(name, ((AtomicBoolean) val).get());
} else if (val instanceof Date) {
writeDate(name, (Date) val);
} else if (val instanceof NamedList) {
writeNamedList(name, (NamedList)val);
} else if (val instanceof Path) {
writeStr(name, ((Path) val).toAbsolutePath().toString(), true);
} else if (val instanceof IteratorWriter) {
writeIterator(name, (IteratorWriter) val);
} else if (val instanceof MapWriter) {
writeMap(name, (MapWriter) val);
} else if (val instanceof MapSerializable) {
//todo find a better way to reuse the map more efficiently
writeMap(name, ((MapSerializable) val).toMap(new LinkedHashMap<>()), false, true);
} else if (val instanceof Map) {
writeMap(name, (Map)val, false, true);
} else if (val instanceof Iterator) { // very generic; keep towards the end
writeArray(name, (Iterator) val);
} else if (val instanceof Iterable) { // very generic; keep towards the end
writeArray(name,((Iterable)val).iterator());
} else if (val instanceof Object[]) {
writeArray(name,(Object[])val);
} else if (val instanceof byte[]) {
byte[] arr = (byte[])val;
writeByteArr(name, arr, 0, arr.length);
} else if (val instanceof EnumFieldValue) {
writeStr(name, val.toString(), true);
} else if (val instanceof WriteableValue) {
((WriteableValue)val).write(name, this);
} else {
// default... for debugging only. Would be nice to "assert false" ?
writeStr(name, val.getClass().getName() + ':' + val.toString(), true);
}
}
void writeStr(String name, String val, boolean needsEscaping) throws IOException;
void writeMap(String name, @SuppressWarnings({"rawtypes"})Map val, boolean excludeOuter, boolean isFirstVal) throws IOException;
void writeArray(String name, @SuppressWarnings({"rawtypes"})Iterator val) throws IOException;
void writeNull(String name) throws IOException;
/** if this form of the method is called, val is the Java string form of an int */
void writeInt(String name, String val) throws IOException;
/** if this form of the method is called, val is the Java string form of a long */
void writeLong(String name, String val) throws IOException;
/** if this form of the method is called, val is the Java string form of a boolean */
void writeBool(String name, String val) throws IOException;
/** if this form of the method is called, val is the Java string form of a float */
void writeFloat(String name, String val) throws IOException;
/** if this form of the method is called, val is the Java string form of a double */
void writeDouble(String name, String val) throws IOException;
/** if this form of the method is called, val is the Solr ISO8601 based date format */
void writeDate(String name, String val) throws IOException;
void writeNamedList(String name, @SuppressWarnings({"rawtypes"})NamedList val) throws IOException;
Writer getWriter();
default void writeNumber(String name, Number val) throws IOException {
if (val instanceof Integer) {
writeInt(name, val.toString());
} else if (val instanceof Long) {
writeLong(name, val.toString());
} else if (val instanceof Float) {
// we pass the float instead of using toString() because
// it may need special formatting. same for double.
writeFloat(name, val.floatValue());
} else if (val instanceof Double) {
writeDouble(name, val.doubleValue());
} else if (val instanceof Short) {
writeInt(name, val.toString());
} else if (val instanceof Byte) {
writeInt(name, val.toString());
} else if (val instanceof AtomicInteger) {
writeInt(name, ((AtomicInteger) val).get());
} else if (val instanceof AtomicLong) {
writeLong(name, ((AtomicLong) val).get());
} else {
// default... for debugging only
writeStr(name, val.getClass().getName() + ':' + val.toString(), true);
}
}
default void writeArray(String name, Object[] val) throws IOException {
writeArray(name, Arrays.asList(val));
}
default void writeArray(String name, @SuppressWarnings({"rawtypes"})List l) throws IOException {
writeArray(name, l.iterator());
}
default void writeDate(String name, Date val) throws IOException {
writeDate(name, val.toInstant().toString());
}
default void writeByteArr(String name, byte[] buf, int offset, int len) throws IOException {
writeStr(name, Base64.byteArrayToBase64(buf, offset, len), false);
}
default void writeInt(String name, int val) throws IOException {
writeInt(name,Integer.toString(val));
}
default void writeLong(String name, long val) throws IOException {
writeLong(name,Long.toString(val));
}
default void writeBool(String name, boolean val) throws IOException {
writeBool(name,Boolean.toString(val));
}
default void writeFloat(String name, float val) throws IOException {
String s = Float.toString(val);
// If it's not a normal number, write the value as a string instead.
// The following test also handles NaN since comparisons are always false.
if (val > Float.NEGATIVE_INFINITY && val < Float.POSITIVE_INFINITY) {
writeFloat(name,s);
} else {
writeStr(name,s,false);
}
}
default void writeDouble(String name, double val) throws IOException {
String s = Double.toString(val);
// If it's not a normal number, write the value as a string instead.
// The following test also handles NaN since comparisons are always false.
if (val > Double.NEGATIVE_INFINITY && val < Double.POSITIVE_INFINITY) {
writeDouble(name,s);
} else {
writeStr(name,s,false);
}
}
default void writeBool(String name , Boolean val) throws IOException {
writeBool(name, val.toString());
}
@Override
default void writeMap(MapWriter mw) throws IOException {
//todo
}
default void writeMap(String name, MapWriter mw) throws IOException {
writeMap(mw);
}
@Override
default void writeIterator(IteratorWriter iw) throws IOException {
/*todo*/
}
default void writeIterator(String name, IteratorWriter iw) throws IOException {
writeIterator(iw);
}
default void indent() throws IOException {
if (doIndent()) indent(level());
}
int incLevel();
int decLevel();
TextWriter setIndent(boolean doIndent);
int level();
boolean doIndent();
default void indent(int lev) throws IOException {
getWriter().write(SolrJSONWriter.indentChars, 0, Math.min((lev<<1)+1, SolrJSONWriter.indentChars.length));
}
}