Move OsmAnd java core project back to android repository for maintainance reasons

This commit is contained in:
Victor Shcherb 2013-04-18 23:35:02 +02:00
parent 42a239c0f3
commit 7bc162a878
199 changed files with 89973 additions and 2 deletions

16
OsmAnd-java/.classpath Normal file
View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry excluding="com/OsMoDroid/" kind="src" path="android"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="libs/bsh-core-2.0b4.jar"/>
<classpathentry kind="lib" path="libs/bzip2-20090327.jar"/>
<classpathentry kind="lib" path="libs/commons-logging-1.1.1.jar"/>
<classpathentry kind="lib" path="libs/json-20090211.jar"/>
<classpathentry kind="lib" path="libs/junidecode-0.1.jar"/>
<classpathentry kind="lib" path="libs/kxml2-2.3.0.jar"/>
<classpathentry kind="lib" path="libs/tuprolog.jar"/>
<classpathentry kind="lib" path="libs/icu4j-49_1.jar"/>
<classpathentry kind="lib" path="libs/gnu-trove-osmand.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>

1
OsmAnd-java/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
bin

30
OsmAnd-java/.project Normal file
View file

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>OsmAnd-java</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
<linkedResources>
<link>
<name>android</name>
<type>2</type>
<locationURI>OSMAND_TRUNK/android/OsmAnd/gen</locationURI>
</link>
</linkedResources>
<variableList>
<variable>
<name>OSMAND_TRUNK</name>
<value>$%7BPARENT-2-PROJECT_LOC%7D</value>
</variable>
</variableList>
</projectDescription>

View file

@ -0,0 +1,11 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.6
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.6

77
OsmAnd-java/build.xml Normal file
View file

@ -0,0 +1,77 @@
<!-- build JAR libraty -->
<project name="OsmAnd-core" default="build" basedir=".">
<property file="local.properties" />
<property name="src.dir" value="java"/>
<property name="src.absolute.dir" location="${src.dir}" />
<property name="bin.dir" value="bin"/>
<property name="bin.absolute.dir" location="${bin.dir}"/>
<property name="lib.dir" value="libs"/>
<property name="lib.absolute.dir" location="${lib.dir}" />
<property name="java.encoding" value="UTF-8" />
<path id="build.path">
<fileset dir="${lib.absolute.dir}">
<include name="*.jar"/>
</fileset>
</path>
<target name="-dirs">
<mkdir dir="${bin.absolute.dir}"/>
</target>
<target name="clean">
<delete dir="${bin.absolute.dir}"/>
<delete file="OsmAnd-core.jar"/>
</target>
<target name="compile" depends="-dirs">
<copy todir="${bin.absolute.dir}">
<fileset dir="${src.absolute.dir}">
<exclude name="**/*.java" />
</fileset>
</copy>
<javac srcdir="${src.absolute.dir}"
destdir="${bin.absolute.dir}" encoding="${java.encoding}" classpathref="build.path" debug="on"
source="1.6">
<include name="**/*.java"/>
<exclude name="net/osmand/plus/**"/>
</javac>
</target>
<target name="native-libs-jar" depends="compile">
<exec command="bash collect_libs.sh" failonerror="true"></exec>
<antcall target="jar"/>
</target>
<target name="jar" depends="compile">
<manifestclasspath property="lib.list" jarfile="OsmAnd-core.jar">
<classpath refid="build.path"/>
</manifestclasspath>
<echo>list is ${lib.list}</echo>
<delete file="OsmAnd-core.jar" />
<delete file="MANIFEST.MF" />
<manifest file="MANIFEST.MF">
<attribute name="Built-By" value="${user.name}" />
<attribute name="Main-Class" value="net.osmand.swing.OsmExtractionUI" />
<attribute name="Class-Path" value="${lib.list}"/>
</manifest>
<jar destfile="OsmAnd-core.jar" manifest="MANIFEST.MF">
<fileset dir="${bin.absolute.dir}">
<include name="**/*" />
</fileset>
<fileset dir="${src.absolute.dir}">
<include name="**/*.java" />
</fileset>
</jar>
<delete file="MANIFEST.MF" />
</target>
<target name="build" depends="jar">
</target>
</project>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,714 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
/**
* A partial implementation of the {@link Message} interface which implements
* as many methods of that interface as possible in terms of other methods.
*
* @author kenton@google.com Kenton Varda
*/
public abstract class AbstractMessage extends AbstractMessageLite
implements Message {
@Override
@SuppressWarnings("unchecked")
public boolean isInitialized() {
// Check that all required fields are present.
for (final FieldDescriptor field : getDescriptorForType().getFields()) {
if (field.isRequired()) {
if (!hasField(field)) {
return false;
}
}
}
// Check that embedded messages are initialized.
for (final Map.Entry<FieldDescriptor, Object> entry :
getAllFields().entrySet()) {
final FieldDescriptor field = entry.getKey();
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
if (field.isRepeated()) {
for (final Message element : (List<Message>) entry.getValue()) {
if (!element.isInitialized()) {
return false;
}
}
} else {
if (!((Message) entry.getValue()).isInitialized()) {
return false;
}
}
}
}
return true;
}
@Override
public final String toString() {
return TextFormat.printToString(this);
}
@Override
public void writeTo(final CodedOutputStream output) throws IOException {
final boolean isMessageSet =
getDescriptorForType().getOptions().getMessageSetWireFormat();
for (final Map.Entry<FieldDescriptor, Object> entry :
getAllFields().entrySet()) {
final FieldDescriptor field = entry.getKey();
final Object value = entry.getValue();
if (isMessageSet && field.isExtension() &&
field.getType() == FieldDescriptor.Type.MESSAGE &&
!field.isRepeated()) {
output.writeMessageSetExtension(field.getNumber(), (Message) value);
} else {
FieldSet.writeField(field, value, output);
}
}
final UnknownFieldSet unknownFields = getUnknownFields();
if (isMessageSet) {
unknownFields.writeAsMessageSetTo(output);
} else {
unknownFields.writeTo(output);
}
}
private int memoizedSize = -1;
@Override
public int getSerializedSize() {
int size = memoizedSize;
if (size != -1) {
return size;
}
size = 0;
final boolean isMessageSet =
getDescriptorForType().getOptions().getMessageSetWireFormat();
for (final Map.Entry<FieldDescriptor, Object> entry :
getAllFields().entrySet()) {
final FieldDescriptor field = entry.getKey();
final Object value = entry.getValue();
if (isMessageSet && field.isExtension() &&
field.getType() == FieldDescriptor.Type.MESSAGE &&
!field.isRepeated()) {
size += CodedOutputStream.computeMessageSetExtensionSize(
field.getNumber(), (Message) value);
} else {
size += FieldSet.computeFieldSize(field, value);
}
}
final UnknownFieldSet unknownFields = getUnknownFields();
if (isMessageSet) {
size += unknownFields.getSerializedSizeAsMessageSet();
} else {
size += unknownFields.getSerializedSize();
}
memoizedSize = size;
return size;
}
@Override
public boolean equals(final Object other) {
if (other == this) {
return true;
}
if (!(other instanceof Message)) {
return false;
}
final Message otherMessage = (Message) other;
if (getDescriptorForType() != otherMessage.getDescriptorForType()) {
return false;
}
return getAllFields().equals(otherMessage.getAllFields()) &&
getUnknownFields().equals(otherMessage.getUnknownFields());
}
@Override
public int hashCode() {
int hash = 41;
hash = (19 * hash) + getDescriptorForType().hashCode();
hash = (53 * hash) + getAllFields().hashCode();
hash = (29 * hash) + getUnknownFields().hashCode();
return hash;
}
// =================================================================
/**
* A partial implementation of the {@link Message.Builder} interface which
* implements as many methods of that interface as possible in terms of
* other methods.
*/
@SuppressWarnings("unchecked")
public static abstract class Builder<BuilderType extends Builder>
extends AbstractMessageLite.Builder<BuilderType>
implements Message.Builder {
// The compiler produces an error if this is not declared explicitly.
@Override
public abstract BuilderType clone();
@Override
public BuilderType clear() {
for (final Map.Entry<FieldDescriptor, Object> entry :
getAllFields().entrySet()) {
clearField(entry.getKey());
}
return (BuilderType) this;
}
@Override
public BuilderType mergeFrom(final Message other) {
if (other.getDescriptorForType() != getDescriptorForType()) {
throw new IllegalArgumentException(
"mergeFrom(Message) can only merge messages of the same type.");
}
// Note: We don't attempt to verify that other's fields have valid
// types. Doing so would be a losing battle. We'd have to verify
// all sub-messages as well, and we'd have to make copies of all of
// them to insure that they don't change after verification (since
// the Message interface itself cannot enforce immutability of
// implementations).
// TODO(kenton): Provide a function somewhere called makeDeepCopy()
// which allows people to make secure deep copies of messages.
for (final Map.Entry<FieldDescriptor, Object> entry :
other.getAllFields().entrySet()) {
final FieldDescriptor field = entry.getKey();
if (field.isRepeated()) {
for (final Object element : (List)entry.getValue()) {
addRepeatedField(field, element);
}
} else if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
final Message existingValue = (Message)getField(field);
if (existingValue == existingValue.getDefaultInstanceForType()) {
setField(field, entry.getValue());
} else {
setField(field,
existingValue.newBuilderForType()
.mergeFrom(existingValue)
.mergeFrom((Message)entry.getValue())
.build());
}
} else {
setField(field, entry.getValue());
}
}
mergeUnknownFields(other.getUnknownFields());
return (BuilderType) this;
}
@Override
public BuilderType mergeFrom(final CodedInputStream input)
throws IOException {
return mergeFrom(input, ExtensionRegistry.getEmptyRegistry());
}
@Override
public BuilderType mergeFrom(
final CodedInputStream input,
final ExtensionRegistryLite extensionRegistry)
throws IOException {
final UnknownFieldSet.Builder unknownFields =
UnknownFieldSet.newBuilder(getUnknownFields());
while (true) {
final int tag = input.readTag();
if (tag == 0) {
break;
}
if (!mergeFieldFrom(input, unknownFields, extensionRegistry,
this, tag)) {
// end group tag
break;
}
}
setUnknownFields(unknownFields.build());
return (BuilderType) this;
}
/**
* Like {@link #mergeFrom(CodedInputStream, UnknownFieldSet.Builder,
* ExtensionRegistryLite, Message.Builder)}, but parses a single field.
* Package-private because it is used by GeneratedMessage.ExtendableMessage.
* @param tag The tag, which should have already been read.
* @return {@code true} unless the tag is an end-group tag.
*/
static boolean mergeFieldFrom(
final CodedInputStream input,
final UnknownFieldSet.Builder unknownFields,
final ExtensionRegistryLite extensionRegistry,
final Message.Builder builder,
final int tag) throws IOException {
final Descriptor type = builder.getDescriptorForType();
if (type.getOptions().getMessageSetWireFormat() &&
tag == WireFormat.MESSAGE_SET_ITEM_TAG) {
mergeMessageSetExtensionFromCodedStream(
input, unknownFields, extensionRegistry, builder);
return true;
}
final int wireType = WireFormat.getTagWireType(tag);
final int fieldNumber = WireFormat.getTagFieldNumber(tag);
final FieldDescriptor field;
Message defaultInstance = null;
if (type.isExtensionNumber(fieldNumber)) {
// extensionRegistry may be either ExtensionRegistry or
// ExtensionRegistryLite. Since the type we are parsing is a full
// message, only a full ExtensionRegistry could possibly contain
// extensions of it. Otherwise we will treat the registry as if it
// were empty.
if (extensionRegistry instanceof ExtensionRegistry) {
final ExtensionRegistry.ExtensionInfo extension =
((ExtensionRegistry) extensionRegistry)
.findExtensionByNumber(type, fieldNumber);
if (extension == null) {
field = null;
} else {
field = extension.descriptor;
defaultInstance = extension.defaultInstance;
if (defaultInstance == null &&
field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
throw new IllegalStateException(
"Message-typed extension lacked default instance: " +
field.getFullName());
}
}
} else {
field = null;
}
} else {
field = type.findFieldByNumber(fieldNumber);
}
boolean unknown = false;
boolean packed = false;
if (field == null) {
unknown = true; // Unknown field.
} else if (wireType == FieldSet.getWireFormatForFieldType(
field.getLiteType(),
false /* isPacked */)) {
packed = false;
} else if (field.isPackable() &&
wireType == FieldSet.getWireFormatForFieldType(
field.getLiteType(),
true /* isPacked */)) {
packed = true;
} else {
unknown = true; // Unknown wire type.
}
if (unknown) { // Unknown field or wrong wire type. Skip.
return unknownFields.mergeFieldFrom(tag, input);
}
if (packed) {
final int length = input.readRawVarint32();
final int limit = input.pushLimit(length);
if (field.getLiteType() == WireFormat.FieldType.ENUM) {
while (input.getBytesUntilLimit() > 0) {
final int rawValue = input.readEnum();
final Object value = field.getEnumType().findValueByNumber(rawValue);
if (value == null) {
// If the number isn't recognized as a valid value for this
// enum, drop it (don't even add it to unknownFields).
return true;
}
builder.addRepeatedField(field, value);
}
} else {
while (input.getBytesUntilLimit() > 0) {
final Object value =
FieldSet.readPrimitiveField(input, field.getLiteType());
builder.addRepeatedField(field, value);
}
}
input.popLimit(limit);
} else {
final Object value;
switch (field.getType()) {
case GROUP: {
final Message.Builder subBuilder;
if (defaultInstance != null) {
subBuilder = defaultInstance.newBuilderForType();
} else {
subBuilder = builder.newBuilderForField(field);
}
if (!field.isRepeated()) {
subBuilder.mergeFrom((Message) builder.getField(field));
}
input.readGroup(field.getNumber(), subBuilder, extensionRegistry);
value = subBuilder.build();
break;
}
case MESSAGE: {
final Message.Builder subBuilder;
if (defaultInstance != null) {
subBuilder = defaultInstance.newBuilderForType();
} else {
subBuilder = builder.newBuilderForField(field);
}
if (!field.isRepeated()) {
subBuilder.mergeFrom((Message) builder.getField(field));
}
input.readMessage(subBuilder, extensionRegistry);
value = subBuilder.build();
break;
}
case ENUM:
final int rawValue = input.readEnum();
value = field.getEnumType().findValueByNumber(rawValue);
// If the number isn't recognized as a valid value for this enum,
// drop it.
if (value == null) {
unknownFields.mergeVarintField(fieldNumber, rawValue);
return true;
}
break;
default:
value = FieldSet.readPrimitiveField(input, field.getLiteType());
break;
}
if (field.isRepeated()) {
builder.addRepeatedField(field, value);
} else {
builder.setField(field, value);
}
}
return true;
}
/** Called by {@code #mergeFieldFrom()} to parse a MessageSet extension. */
private static void mergeMessageSetExtensionFromCodedStream(
final CodedInputStream input,
final UnknownFieldSet.Builder unknownFields,
final ExtensionRegistryLite extensionRegistry,
final Message.Builder builder) throws IOException {
final Descriptor type = builder.getDescriptorForType();
// The wire format for MessageSet is:
// message MessageSet {
// repeated group Item = 1 {
// required int32 typeId = 2;
// required bytes message = 3;
// }
// }
// "typeId" is the extension's field number. The extension can only be
// a message type, where "message" contains the encoded bytes of that
// message.
//
// In practice, we will probably never see a MessageSet item in which
// the message appears before the type ID, or where either field does not
// appear exactly once. However, in theory such cases are valid, so we
// should be prepared to accept them.
int typeId = 0;
ByteString rawBytes = null; // If we encounter "message" before "typeId"
Message.Builder subBuilder = null;
FieldDescriptor field = null;
while (true) {
final int tag = input.readTag();
if (tag == 0) {
break;
}
if (tag == WireFormat.MESSAGE_SET_TYPE_ID_TAG) {
typeId = input.readUInt32();
// Zero is not a valid type ID.
if (typeId != 0) {
final ExtensionRegistry.ExtensionInfo extension;
// extensionRegistry may be either ExtensionRegistry or
// ExtensionRegistryLite. Since the type we are parsing is a full
// message, only a full ExtensionRegistry could possibly contain
// extensions of it. Otherwise we will treat the registry as if it
// were empty.
if (extensionRegistry instanceof ExtensionRegistry) {
extension = ((ExtensionRegistry) extensionRegistry)
.findExtensionByNumber(type, typeId);
} else {
extension = null;
}
if (extension != null) {
field = extension.descriptor;
subBuilder = extension.defaultInstance.newBuilderForType();
final Message originalMessage = (Message)builder.getField(field);
if (originalMessage != null) {
subBuilder.mergeFrom(originalMessage);
}
if (rawBytes != null) {
// We already encountered the message. Parse it now.
subBuilder.mergeFrom(
CodedInputStream.newInstance(rawBytes.newInput()));
rawBytes = null;
}
} else {
// Unknown extension number. If we already saw data, put it
// in rawBytes.
if (rawBytes != null) {
unknownFields.mergeField(typeId,
UnknownFieldSet.Field.newBuilder()
.addLengthDelimited(rawBytes)
.build());
rawBytes = null;
}
}
}
} else if (tag == WireFormat.MESSAGE_SET_MESSAGE_TAG) {
if (typeId == 0) {
// We haven't seen a type ID yet, so we have to store the raw bytes
// for now.
rawBytes = input.readBytes();
} else if (subBuilder == null) {
// We don't know how to parse this. Ignore it.
unknownFields.mergeField(typeId,
UnknownFieldSet.Field.newBuilder()
.addLengthDelimited(input.readBytes())
.build());
} else {
// We already know the type, so we can parse directly from the input
// with no copying. Hooray!
input.readMessage(subBuilder, extensionRegistry);
}
} else {
// Unknown tag. Skip it.
if (!input.skipField(tag)) {
break; // end of group
}
}
}
input.checkLastTagWas(WireFormat.MESSAGE_SET_ITEM_END_TAG);
if (subBuilder != null) {
builder.setField(field, subBuilder.build());
}
}
@Override
public BuilderType mergeUnknownFields(final UnknownFieldSet unknownFields) {
setUnknownFields(
UnknownFieldSet.newBuilder(getUnknownFields())
.mergeFrom(unknownFields)
.build());
return (BuilderType) this;
}
/**
* Construct an UninitializedMessageException reporting missing fields in
* the given message.
*/
protected static UninitializedMessageException
newUninitializedMessageException(Message message) {
return new UninitializedMessageException(findMissingFields(message));
}
/**
* Populates {@code this.missingFields} with the full "path" of each
* missing required field in the given message.
*/
private static List<String> findMissingFields(final Message message) {
final List<String> results = new ArrayList<String>();
findMissingFields(message, "", results);
return results;
}
/** Recursive helper implementing {@link #findMissingFields(Message)}. */
private static void findMissingFields(final Message message,
final String prefix,
final List<String> results) {
for (final FieldDescriptor field :
message.getDescriptorForType().getFields()) {
if (field.isRequired() && !message.hasField(field)) {
results.add(prefix + field.getName());
}
}
for (final Map.Entry<FieldDescriptor, Object> entry :
message.getAllFields().entrySet()) {
final FieldDescriptor field = entry.getKey();
final Object value = entry.getValue();
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
if (field.isRepeated()) {
int i = 0;
for (final Object element : (List) value) {
findMissingFields((Message) element,
subMessagePrefix(prefix, field, i++),
results);
}
} else {
if (message.hasField(field)) {
findMissingFields((Message) value,
subMessagePrefix(prefix, field, -1),
results);
}
}
}
}
}
private static String subMessagePrefix(final String prefix,
final FieldDescriptor field,
final int index) {
final StringBuilder result = new StringBuilder(prefix);
if (field.isExtension()) {
result.append('(')
.append(field.getFullName())
.append(')');
} else {
result.append(field.getName());
}
if (index != -1) {
result.append('[')
.append(index)
.append(']');
}
result.append('.');
return result.toString();
}
// ===============================================================
// The following definitions seem to be required in order to make javac
// not produce weird errors like:
//
// java/com/google/protobuf/DynamicMessage.java:203: types
// com.google.protobuf.AbstractMessage.Builder<
// com.google.protobuf.DynamicMessage.Builder> and
// com.google.protobuf.AbstractMessage.Builder<
// com.google.protobuf.DynamicMessage.Builder> are incompatible; both
// define mergeFrom(com.google.protobuf.ByteString), but with unrelated
// return types.
//
// Strangely, these lines are only needed if javac is invoked separately
// on AbstractMessage.java and AbstractMessageLite.java. If javac is
// invoked on both simultaneously, it works. (Or maybe the important
// point is whether or not DynamicMessage.java is compiled together with
// AbstractMessageLite.java -- not sure.) I suspect this is a compiler
// bug.
@Override
public BuilderType mergeFrom(final ByteString data)
throws InvalidProtocolBufferException {
return super.mergeFrom(data);
}
@Override
public BuilderType mergeFrom(
final ByteString data,
final ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return super.mergeFrom(data, extensionRegistry);
}
@Override
public BuilderType mergeFrom(final byte[] data)
throws InvalidProtocolBufferException {
return super.mergeFrom(data);
}
@Override
public BuilderType mergeFrom(
final byte[] data, final int off, final int len)
throws InvalidProtocolBufferException {
return super.mergeFrom(data, off, len);
}
@Override
public BuilderType mergeFrom(
final byte[] data,
final ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return super.mergeFrom(data, extensionRegistry);
}
@Override
public BuilderType mergeFrom(
final byte[] data, final int off, final int len,
final ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return super.mergeFrom(data, off, len, extensionRegistry);
}
@Override
public BuilderType mergeFrom(final InputStream input)
throws IOException {
return super.mergeFrom(input);
}
@Override
public BuilderType mergeFrom(
final InputStream input,
final ExtensionRegistryLite extensionRegistry)
throws IOException {
return super.mergeFrom(input, extensionRegistry);
}
@Override
public boolean mergeDelimitedFrom(final InputStream input)
throws IOException {
return super.mergeDelimitedFrom(input);
}
@Override
public boolean mergeDelimitedFrom(
final InputStream input,
final ExtensionRegistryLite extensionRegistry)
throws IOException {
return super.mergeDelimitedFrom(input, extensionRegistry);
}
}
}

View file

@ -0,0 +1,342 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
/**
* A partial implementation of the {@link MessageLite} interface which
* implements as many methods of that interface as possible in terms of other
* methods.
*
* @author kenton@google.com Kenton Varda
*/
public abstract class AbstractMessageLite implements MessageLite {
@Override
public ByteString toByteString() {
try {
final ByteString.CodedBuilder out =
ByteString.newCodedBuilder(getSerializedSize());
writeTo(out.getCodedOutput());
return out.build();
} catch (IOException e) {
throw new RuntimeException(
"Serializing to a ByteString threw an IOException (should " +
"never happen).", e);
}
}
@Override
public byte[] toByteArray() {
try {
final byte[] result = new byte[getSerializedSize()];
final CodedOutputStream output = CodedOutputStream.newInstance(result);
writeTo(output);
output.checkNoSpaceLeft();
return result;
} catch (IOException e) {
throw new RuntimeException(
"Serializing to a byte array threw an IOException " +
"(should never happen).", e);
}
}
@Override
public void writeTo(final OutputStream output) throws IOException {
final int bufferSize =
CodedOutputStream.computePreferredBufferSize(getSerializedSize());
final CodedOutputStream codedOutput =
CodedOutputStream.newInstance(output, bufferSize);
writeTo(codedOutput);
codedOutput.flush();
}
@Override
public void writeDelimitedTo(final OutputStream output) throws IOException {
final int serialized = getSerializedSize();
final int bufferSize = CodedOutputStream.computePreferredBufferSize(
CodedOutputStream.computeRawVarint32Size(serialized) + serialized);
final CodedOutputStream codedOutput =
CodedOutputStream.newInstance(output, bufferSize);
codedOutput.writeRawVarint32(serialized);
writeTo(codedOutput);
codedOutput.flush();
}
/**
* A partial implementation of the {@link Message.Builder} interface which
* implements as many methods of that interface as possible in terms of
* other methods.
*/
@SuppressWarnings("unchecked")
public static abstract class Builder<BuilderType extends Builder>
implements MessageLite.Builder {
// The compiler produces an error if this is not declared explicitly.
@Override
public abstract BuilderType clone();
@Override
public BuilderType mergeFrom(final CodedInputStream input)
throws IOException {
return mergeFrom(input, ExtensionRegistryLite.getEmptyRegistry());
}
// Re-defined here for return type covariance.
@Override
public abstract BuilderType mergeFrom(
final CodedInputStream input,
final ExtensionRegistryLite extensionRegistry)
throws IOException;
@Override
public BuilderType mergeFrom(final ByteString data)
throws InvalidProtocolBufferException {
try {
final CodedInputStream input = data.newCodedInput();
mergeFrom(input);
input.checkLastTagWas(0);
return (BuilderType) this;
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
throw new RuntimeException(
"Reading from a ByteString threw an IOException (should " +
"never happen).", e);
}
}
@Override
public BuilderType mergeFrom(
final ByteString data,
final ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
try {
final CodedInputStream input = data.newCodedInput();
mergeFrom(input, extensionRegistry);
input.checkLastTagWas(0);
return (BuilderType) this;
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
throw new RuntimeException(
"Reading from a ByteString threw an IOException (should " +
"never happen).", e);
}
}
@Override
public BuilderType mergeFrom(final byte[] data)
throws InvalidProtocolBufferException {
return mergeFrom(data, 0, data.length);
}
@Override
public BuilderType mergeFrom(final byte[] data, final int off,
final int len)
throws InvalidProtocolBufferException {
try {
final CodedInputStream input =
CodedInputStream.newInstance(data, off, len);
mergeFrom(input);
input.checkLastTagWas(0);
return (BuilderType) this;
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
throw new RuntimeException(
"Reading from a byte array threw an IOException (should " +
"never happen).", e);
}
}
@Override
public BuilderType mergeFrom(
final byte[] data,
final ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
return mergeFrom(data, 0, data.length, extensionRegistry);
}
@Override
public BuilderType mergeFrom(
final byte[] data, final int off, final int len,
final ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
try {
final CodedInputStream input =
CodedInputStream.newInstance(data, off, len);
mergeFrom(input, extensionRegistry);
input.checkLastTagWas(0);
return (BuilderType) this;
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
throw new RuntimeException(
"Reading from a byte array threw an IOException (should " +
"never happen).", e);
}
}
@Override
public BuilderType mergeFrom(final InputStream input) throws IOException {
final CodedInputStream codedInput = CodedInputStream.newInstance(input);
mergeFrom(codedInput);
codedInput.checkLastTagWas(0);
return (BuilderType) this;
}
@Override
public BuilderType mergeFrom(
final InputStream input,
final ExtensionRegistryLite extensionRegistry)
throws IOException {
final CodedInputStream codedInput = CodedInputStream.newInstance(input);
mergeFrom(codedInput, extensionRegistry);
codedInput.checkLastTagWas(0);
return (BuilderType) this;
}
/**
* An InputStream implementations which reads from some other InputStream
* but is limited to a particular number of bytes. Used by
* mergeDelimitedFrom(). This is intentionally package-private so that
* UnknownFieldSet can share it.
*/
static final class LimitedInputStream extends FilterInputStream {
private int limit;
LimitedInputStream(InputStream in, int limit) {
super(in);
this.limit = limit;
}
@Override
public int available() throws IOException {
return Math.min(super.available(), limit);
}
@Override
public int read() throws IOException {
if (limit <= 0) {
return -1;
}
final int result = super.read();
if (result >= 0) {
--limit;
}
return result;
}
@Override
public int read(final byte[] b, final int off, int len)
throws IOException {
if (limit <= 0) {
return -1;
}
len = Math.min(len, limit);
final int result = super.read(b, off, len);
if (result >= 0) {
limit -= result;
}
return result;
}
@Override
public long skip(final long n) throws IOException {
final long result = super.skip(Math.min(n, limit));
if (result >= 0) {
limit -= result;
}
return result;
}
}
@Override
public boolean mergeDelimitedFrom(
final InputStream input,
final ExtensionRegistryLite extensionRegistry)
throws IOException {
final int firstByte = input.read();
if (firstByte == -1) {
return false;
}
final int size = CodedInputStream.readRawVarint32(firstByte, input);
final InputStream limitedInput = new LimitedInputStream(input, size);
mergeFrom(limitedInput, extensionRegistry);
return true;
}
@Override
public boolean mergeDelimitedFrom(final InputStream input)
throws IOException {
return mergeDelimitedFrom(input,
ExtensionRegistryLite.getEmptyRegistry());
}
/**
* Construct an UninitializedMessageException reporting missing fields in
* the given message.
*/
protected static UninitializedMessageException
newUninitializedMessageException(MessageLite message) {
return new UninitializedMessageException(message);
}
/**
* Adds the {@code values} to the {@code list}. This is a helper method
* used by generated code. Users should ignore it.
*
* @throws NullPointerException if any of the elements of {@code values} is
* null.
*/
protected static <T> void addAll(final Iterable<T> values,
final Collection<? super T> list) {
for (final T value : values) {
if (value == null) {
throw new NullPointerException();
}
}
if (values instanceof Collection) {
final
Collection<T> collection = (Collection<T>) values;
list.addAll(collection);
} else {
for (final T value : values) {
list.add(value);
}
}
}
}
}

View file

@ -0,0 +1,51 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
/**
* <p>Abstract interface for a blocking RPC channel. {@code BlockingRpcChannel}
* is the blocking equivalent to {@link RpcChannel}.
*
* @author kenton@google.com Kenton Varda
* @author cpovirk@google.com Chris Povirk
*/
public interface BlockingRpcChannel {
/**
* Call the given method of the remote service and blocks until it returns.
* {@code callBlockingMethod()} is the blocking equivalent to
* {@link RpcChannel#callMethod}.
*/
Message callBlockingMethod(
Descriptors.MethodDescriptor method,
RpcController controller,
Message request,
Message responsePrototype) throws ServiceException;
}

View file

@ -0,0 +1,64 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
/**
* Blocking equivalent to {@link Service}.
*
* @author kenton@google.com Kenton Varda
* @author cpovirk@google.com Chris Povirk
*/
public interface BlockingService {
/**
* Equivalent to {@link Service#getDescriptorForType}.
*/
Descriptors.ServiceDescriptor getDescriptorForType();
/**
* Equivalent to {@link Service#callMethod}, except that
* {@code callBlockingMethod()} returns the result of the RPC or throws a
* {@link ServiceException} if there is a failure, rather than passing the
* information to a callback.
*/
Message callBlockingMethod(Descriptors.MethodDescriptor method,
RpcController controller,
Message request) throws ServiceException;
/**
* Equivalent to {@link Service#getRequestPrototype}.
*/
Message getRequestPrototype(Descriptors.MethodDescriptor method);
/**
* Equivalent to {@link Service#getResponsePrototype}.
*/
Message getResponsePrototype(Descriptors.MethodDescriptor method);
}

View file

@ -0,0 +1,391 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FilterOutputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.util.List;
/**
* Immutable array of bytes.
*
* @author crazybob@google.com Bob Lee
* @author kenton@google.com Kenton Varda
*/
public final class ByteString {
private final byte[] bytes;
private ByteString(final byte[] bytes) {
this.bytes = bytes;
}
/**
* Gets the byte at the given index.
*
* @throws ArrayIndexOutOfBoundsException {@code index} is < 0 or >= size
*/
public byte byteAt(final int index) {
return bytes[index];
}
/**
* Gets the number of bytes.
*/
public int size() {
return bytes.length;
}
/**
* Returns {@code true} if the size is {@code 0}, {@code false} otherwise.
*/
public boolean isEmpty() {
return bytes.length == 0;
}
// =================================================================
// byte[] -> ByteString
/**
* Empty ByteString.
*/
public static final ByteString EMPTY = new ByteString(new byte[0]);
/**
* Copies the given bytes into a {@code ByteString}.
*/
public static ByteString copyFrom(final byte[] bytes, final int offset,
final int size) {
final byte[] copy = new byte[size];
System.arraycopy(bytes, offset, copy, 0, size);
return new ByteString(copy);
}
/**
* Copies the given bytes into a {@code ByteString}.
*/
public static ByteString copyFrom(final byte[] bytes) {
return copyFrom(bytes, 0, bytes.length);
}
/**
* Copies {@code size} bytes from a {@code java.nio.ByteBuffer} into
* a {@code ByteString}.
*/
public static ByteString copyFrom(final ByteBuffer bytes, final int size) {
final byte[] copy = new byte[size];
bytes.get(copy);
return new ByteString(copy);
}
/**
* Copies the remaining bytes from a {@code java.nio.ByteBuffer} into
* a {@code ByteString}.
*/
public static ByteString copyFrom(final ByteBuffer bytes) {
return copyFrom(bytes, bytes.remaining());
}
/**
* Encodes {@code text} into a sequence of bytes using the named charset
* and returns the result as a {@code ByteString}.
*/
public static ByteString copyFrom(final String text, final String charsetName)
throws UnsupportedEncodingException {
return new ByteString(text.getBytes(charsetName));
}
/**
* Encodes {@code text} into a sequence of UTF-8 bytes and returns the
* result as a {@code ByteString}.
*/
public static ByteString copyFromUtf8(final String text) {
try {
return new ByteString(text.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("UTF-8 not supported?", e);
}
}
/**
* Concatenates all byte strings in the list and returns the result.
*
* <p>The returned {@code ByteString} is not necessarily a unique object.
* If the list is empty, the returned object is the singleton empty
* {@code ByteString}. If the list has only one element, that
* {@code ByteString} will be returned without copying.
*/
public static ByteString copyFrom(List<ByteString> list) {
if (list.size() == 0) {
return EMPTY;
} else if (list.size() == 1) {
return list.get(0);
}
int size = 0;
for (ByteString str : list) {
size += str.size();
}
byte[] bytes = new byte[size];
int pos = 0;
for (ByteString str : list) {
System.arraycopy(str.bytes, 0, bytes, pos, str.size());
pos += str.size();
}
return new ByteString(bytes);
}
// =================================================================
// ByteString -> byte[]
/**
* Copies bytes into a buffer at the given offset.
*
* @param target buffer to copy into
* @param offset in the target buffer
*/
public void copyTo(final byte[] target, final int offset) {
System.arraycopy(bytes, 0, target, offset, bytes.length);
}
/**
* Copies bytes into a buffer.
*
* @param target buffer to copy into
* @param sourceOffset offset within these bytes
* @param targetOffset offset within the target buffer
* @param size number of bytes to copy
*/
public void copyTo(final byte[] target, final int sourceOffset,
final int targetOffset,
final int size) {
System.arraycopy(bytes, sourceOffset, target, targetOffset, size);
}
/**
* Copies bytes to a {@code byte[]}.
*/
public byte[] toByteArray() {
final int size = bytes.length;
final byte[] copy = new byte[size];
System.arraycopy(bytes, 0, copy, 0, size);
return copy;
}
/**
* Constructs a new read-only {@code java.nio.ByteBuffer} with the
* same backing byte array.
*/
public ByteBuffer asReadOnlyByteBuffer() {
final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
return byteBuffer.asReadOnlyBuffer();
}
/**
* Constructs a new {@code String} by decoding the bytes using the
* specified charset.
*/
public String toString(final String charsetName)
throws UnsupportedEncodingException {
return new String(bytes, charsetName);
}
/**
* Constructs a new {@code String} by decoding the bytes as UTF-8.
*/
public String toStringUtf8() {
try {
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("UTF-8 not supported?", e);
}
}
// =================================================================
// equals() and hashCode()
@Override
public boolean equals(final Object o) {
if (o == this) {
return true;
}
if (!(o instanceof ByteString)) {
return false;
}
final ByteString other = (ByteString) o;
final int size = bytes.length;
if (size != other.bytes.length) {
return false;
}
final byte[] thisBytes = bytes;
final byte[] otherBytes = other.bytes;
for (int i = 0; i < size; i++) {
if (thisBytes[i] != otherBytes[i]) {
return false;
}
}
return true;
}
private volatile int hash = 0;
@Override
public int hashCode() {
int h = hash;
if (h == 0) {
final byte[] thisBytes = bytes;
final int size = bytes.length;
h = size;
for (int i = 0; i < size; i++) {
h = h * 31 + thisBytes[i];
}
if (h == 0) {
h = 1;
}
hash = h;
}
return h;
}
// =================================================================
// Input stream
/**
* Creates an {@code InputStream} which can be used to read the bytes.
*/
public InputStream newInput() {
return new ByteArrayInputStream(bytes);
}
/**
* Creates a {@link CodedInputStream} which can be used to read the bytes.
* Using this is more efficient than creating a {@link CodedInputStream}
* wrapping the result of {@link #newInput()}.
*/
public CodedInputStream newCodedInput() {
// We trust CodedInputStream not to modify the bytes, or to give anyone
// else access to them.
return CodedInputStream.newInstance(bytes);
}
// =================================================================
// Output stream
/**
* Creates a new {@link Output} with the given initial capacity.
*/
public static Output newOutput(final int initialCapacity) {
return new Output(new ByteArrayOutputStream(initialCapacity));
}
/**
* Creates a new {@link Output}.
*/
public static Output newOutput() {
return newOutput(32);
}
/**
* Outputs to a {@code ByteString} instance. Call {@link #toByteString()} to
* create the {@code ByteString} instance.
*/
public static final class Output extends FilterOutputStream {
private final ByteArrayOutputStream bout;
/**
* Constructs a new output with the given initial capacity.
*/
private Output(final ByteArrayOutputStream bout) {
super(bout);
this.bout = bout;
}
/**
* Creates a {@code ByteString} instance from this {@code Output}.
*/
public ByteString toByteString() {
final byte[] byteArray = bout.toByteArray();
return new ByteString(byteArray);
}
}
/**
* Constructs a new ByteString builder, which allows you to efficiently
* construct a {@code ByteString} by writing to a {@link CodedOutputStream}.
* Using this is much more efficient than calling {@code newOutput()} and
* wrapping that in a {@code CodedOutputStream}.
*
* <p>This is package-private because it's a somewhat confusing interface.
* Users can call {@link Message#toByteString()} instead of calling this
* directly.
*
* @param size The target byte size of the {@code ByteString}. You must
* write exactly this many bytes before building the result.
*/
static CodedBuilder newCodedBuilder(final int size) {
return new CodedBuilder(size);
}
/** See {@link ByteString#newCodedBuilder(int)}. */
static final class CodedBuilder {
private final CodedOutputStream output;
private final byte[] buffer;
private CodedBuilder(final int size) {
buffer = new byte[size];
output = CodedOutputStream.newInstance(buffer);
}
public ByteString build() {
output.checkNoSpaceLeft();
// We can be confident that the CodedOutputStream will not modify the
// underlying bytes anymore because it already wrote all of them. So,
// no need to make a copy.
return new ByteString(buffer);
}
public CodedOutputStream getCodedOutput() {
return output;
}
}
}

View file

@ -0,0 +1,924 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
/**
* Reads and decodes protocol message fields.
*
* This class contains two kinds of methods: methods that read specific
* protocol message constructs and field types (e.g. {@link #readTag()} and
* {@link #readInt32()}) and methods that read low-level values (e.g.
* {@link #readRawVarint32()} and {@link #readRawBytes}). If you are reading
* encoded protocol messages, you should use the former methods, but if you are
* reading some other format of your own design, use the latter.
*
* @author kenton@google.com Kenton Varda
*/
public final class CodedInputStream {
/**
* Create a new CodedInputStream wrapping the given InputStream.
*/
public static CodedInputStream newInstance(final InputStream input) {
return new CodedInputStream(input);
}
/**
* Create a new CodedInputStream wrapping the given byte array.
*/
public static CodedInputStream newInstance(final byte[] buf) {
return newInstance(buf, 0, buf.length);
}
/**
* Create a new CodedInputStream wrapping the given byte array slice.
*/
public static CodedInputStream newInstance(final byte[] buf, final int off,
final int len) {
return new CodedInputStream(buf, off, len);
}
// -----------------------------------------------------------------
// begin osmand change
public static CodedInputStream newInstance(RandomAccessFile raf) {
return new CodedInputStream(raf);
}
// end osmand change
/**
* Attempt to read a field tag, returning zero if we have reached EOF.
* Protocol message parsers use this to read tags, since a protocol message
* may legally end wherever a tag occurs, and zero is not a valid tag number.
*/
public int readTag() throws IOException {
if (isAtEnd()) {
lastTag = 0;
return 0;
}
lastTag = readRawVarint32();
if (WireFormat.getTagFieldNumber(lastTag) == 0) {
// If we actually read zero (or any tag number corresponding to field
// number zero), that's not a valid tag.
throw InvalidProtocolBufferException.invalidTag();
}
return lastTag;
}
/**
* Verifies that the last call to readTag() returned the given tag value.
* This is used to verify that a nested group ended with the correct
* end tag.
*
* @throws InvalidProtocolBufferException {@code value} does not match the
* last tag.
*/
public void checkLastTagWas(final int value)
throws InvalidProtocolBufferException {
if (lastTag != value) {
throw InvalidProtocolBufferException.invalidEndTag();
}
}
/**
* Reads and discards a single field, given its tag value.
*
* @return {@code false} if the tag is an endgroup tag, in which case
* nothing is skipped. Otherwise, returns {@code true}.
*/
public boolean skipField(final int tag) throws IOException {
switch (WireFormat.getTagWireType(tag)) {
case WireFormat.WIRETYPE_VARINT:
readInt32();
return true;
case WireFormat.WIRETYPE_FIXED64:
readRawLittleEndian64();
return true;
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
skipRawBytes(readRawVarint32());
return true;
case WireFormat.WIRETYPE_START_GROUP:
skipMessage();
checkLastTagWas(
WireFormat.makeTag(WireFormat.getTagFieldNumber(tag),
WireFormat.WIRETYPE_END_GROUP));
return true;
case WireFormat.WIRETYPE_END_GROUP:
return false;
case WireFormat.WIRETYPE_FIXED32:
readRawLittleEndian32();
return true;
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
/**
* Reads and discards an entire message. This will read either until EOF
* or until an endgroup tag, whichever comes first.
*/
public void skipMessage() throws IOException {
while (true) {
final int tag = readTag();
if (tag == 0 || !skipField(tag)) {
return;
}
}
}
// -----------------------------------------------------------------
/** Read a {@code double} field value from the stream. */
public double readDouble() throws IOException {
return Double.longBitsToDouble(readRawLittleEndian64());
}
/** Read a {@code float} field value from the stream. */
public float readFloat() throws IOException {
return Float.intBitsToFloat(readRawLittleEndian32());
}
/** Read a {@code uint64} field value from the stream. */
public long readUInt64() throws IOException {
return readRawVarint64();
}
/** Read an {@code int64} field value from the stream. */
public long readInt64() throws IOException {
return readRawVarint64();
}
/** Read an {@code int32} field value from the stream. */
public int readInt32() throws IOException {
return readRawVarint32();
}
/** Read a {@code fixed64} field value from the stream. */
public long readFixed64() throws IOException {
return readRawLittleEndian64();
}
/** Read a {@code fixed32} field value from the stream. */
public int readFixed32() throws IOException {
return readRawLittleEndian32();
}
/** Read a {@code bool} field value from the stream. */
public boolean readBool() throws IOException {
return readRawVarint32() != 0;
}
/** Read a {@code string} field value from the stream. */
public String readString() throws IOException {
final int size = readRawVarint32();
if (size <= (bufferSize - bufferPos) && size > 0) {
// Fast path: We already have the bytes in a contiguous buffer, so
// just copy directly from it.
final String result = new String(buffer, bufferPos, size, "UTF-8");
bufferPos += size;
return result;
} else {
// Slow path: Build a byte array first then copy it.
return new String(readRawBytes(size), "UTF-8");
}
}
/** Read a {@code group} field value from the stream. */
public void readGroup(final int fieldNumber,
final MessageLite.Builder builder,
final ExtensionRegistryLite extensionRegistry)
throws IOException {
if (recursionDepth >= recursionLimit) {
throw InvalidProtocolBufferException.recursionLimitExceeded();
}
++recursionDepth;
builder.mergeFrom(this, extensionRegistry);
checkLastTagWas(
WireFormat.makeTag(fieldNumber, WireFormat.WIRETYPE_END_GROUP));
--recursionDepth;
}
/**
* Reads a {@code group} field value from the stream and merges it into the
* given {@link UnknownFieldSet}.
*
* @deprecated UnknownFieldSet.Builder now implements MessageLite.Builder, so
* you can just call {@link #readGroup}.
*/
@Deprecated
public void readUnknownGroup(final int fieldNumber,
final MessageLite.Builder builder)
throws IOException {
// We know that UnknownFieldSet will ignore any ExtensionRegistry so it
// is safe to pass null here. (We can't call
// ExtensionRegistry.getEmptyRegistry() because that would make this
// class depend on ExtensionRegistry, which is not part of the lite
// library.)
readGroup(fieldNumber, builder, null);
}
/** Read an embedded message field value from the stream. */
public void readMessage(final MessageLite.Builder builder,
final ExtensionRegistryLite extensionRegistry)
throws IOException {
final int length = readRawVarint32();
if (recursionDepth >= recursionLimit) {
throw InvalidProtocolBufferException.recursionLimitExceeded();
}
final int oldLimit = pushLimit(length);
++recursionDepth;
builder.mergeFrom(this, extensionRegistry);
checkLastTagWas(0);
--recursionDepth;
popLimit(oldLimit);
}
/** Read a {@code bytes} field value from the stream. */
public ByteString readBytes() throws IOException {
final int size = readRawVarint32();
if (size <= (bufferSize - bufferPos) && size > 0) {
// Fast path: We already have the bytes in a contiguous buffer, so
// just copy directly from it.
final ByteString result = ByteString.copyFrom(buffer, bufferPos, size);
bufferPos += size;
return result;
} else {
// Slow path: Build a byte array first then copy it.
return ByteString.copyFrom(readRawBytes(size));
}
}
/** Read a {@code uint32} field value from the stream. */
public int readUInt32() throws IOException {
return readRawVarint32();
}
/**
* Read an enum field value from the stream. Caller is responsible
* for converting the numeric value to an actual enum.
*/
public int readEnum() throws IOException {
return readRawVarint32();
}
/** Read an {@code sfixed32} field value from the stream. */
public int readSFixed32() throws IOException {
return readRawLittleEndian32();
}
/** Read an {@code sfixed64} field value from the stream. */
public long readSFixed64() throws IOException {
return readRawLittleEndian64();
}
/** Read an {@code sint32} field value from the stream. */
public int readSInt32() throws IOException {
return decodeZigZag32(readRawVarint32());
}
/** Read an {@code sint64} field value from the stream. */
public long readSInt64() throws IOException {
return decodeZigZag64(readRawVarint64());
}
// =================================================================
/**
* Read a raw Varint from the stream. If larger than 32 bits, discard the
* upper bits.
*/
public int readRawVarint32() throws IOException {
byte tmp = readRawByte();
if (tmp >= 0) {
return tmp;
}
int result = tmp & 0x7f;
if ((tmp = readRawByte()) >= 0) {
result |= tmp << 7;
} else {
result |= (tmp & 0x7f) << 7;
if ((tmp = readRawByte()) >= 0) {
result |= tmp << 14;
} else {
result |= (tmp & 0x7f) << 14;
if ((tmp = readRawByte()) >= 0) {
result |= tmp << 21;
} else {
result |= (tmp & 0x7f) << 21;
result |= (tmp = readRawByte()) << 28;
if (tmp < 0) {
// Discard upper 32 bits.
for (int i = 0; i < 5; i++) {
if (readRawByte() >= 0) {
return result;
}
}
throw InvalidProtocolBufferException.malformedVarint();
}
}
}
}
return result;
}
/**
* Reads a varint from the input one byte at a time, so that it does not
* read any bytes after the end of the varint. If you simply wrapped the
* stream in a CodedInputStream and used {@link #readRawVarint32(InputStream)}
* then you would probably end up reading past the end of the varint since
* CodedInputStream buffers its input.
*/
static int readRawVarint32(final InputStream input) throws IOException {
final int firstByte = input.read();
if (firstByte == -1) {
throw InvalidProtocolBufferException.truncatedMessage();
}
return readRawVarint32(firstByte, input);
}
/**
* Like {@link #readRawVarint32(InputStream)}, but expects that the caller
* has already read one byte. This allows the caller to determine if EOF
* has been reached before attempting to read.
*/
static int readRawVarint32(final int firstByte,
final InputStream input) throws IOException {
if ((firstByte & 0x80) == 0) {
return firstByte;
}
int result = firstByte & 0x7f;
int offset = 7;
for (; offset < 32; offset += 7) {
final int b = input.read();
if (b == -1) {
throw InvalidProtocolBufferException.truncatedMessage();
}
result |= (b & 0x7f) << offset;
if ((b & 0x80) == 0) {
return result;
}
}
// Keep reading up to 64 bits.
for (; offset < 64; offset += 7) {
final int b = input.read();
if (b == -1) {
throw InvalidProtocolBufferException.truncatedMessage();
}
if ((b & 0x80) == 0) {
return result;
}
}
throw InvalidProtocolBufferException.malformedVarint();
}
/** Read a raw Varint from the stream. */
public long readRawVarint64() throws IOException {
int shift = 0;
long result = 0;
while (shift < 64) {
final byte b = readRawByte();
result |= (long)(b & 0x7F) << shift;
if ((b & 0x80) == 0) {
return result;
}
shift += 7;
}
throw InvalidProtocolBufferException.malformedVarint();
}
/** Read a 32-bit little-endian integer from the stream. */
public int readRawLittleEndian32() throws IOException {
final byte b1 = readRawByte();
final byte b2 = readRawByte();
final byte b3 = readRawByte();
final byte b4 = readRawByte();
return (((int)b1 & 0xff) ) |
(((int)b2 & 0xff) << 8) |
(((int)b3 & 0xff) << 16) |
(((int)b4 & 0xff) << 24);
}
/** Read a 64-bit little-endian integer from the stream. */
public long readRawLittleEndian64() throws IOException {
final byte b1 = readRawByte();
final byte b2 = readRawByte();
final byte b3 = readRawByte();
final byte b4 = readRawByte();
final byte b5 = readRawByte();
final byte b6 = readRawByte();
final byte b7 = readRawByte();
final byte b8 = readRawByte();
return (((long)b1 & 0xff) ) |
(((long)b2 & 0xff) << 8) |
(((long)b3 & 0xff) << 16) |
(((long)b4 & 0xff) << 24) |
(((long)b5 & 0xff) << 32) |
(((long)b6 & 0xff) << 40) |
(((long)b7 & 0xff) << 48) |
(((long)b8 & 0xff) << 56);
}
/**
* Decode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers
* into values that can be efficiently encoded with varint. (Otherwise,
* negative values must be sign-extended to 64 bits to be varint encoded,
* thus always taking 10 bytes on the wire.)
*
* @param n An unsigned 32-bit integer, stored in a signed int because
* Java has no explicit unsigned support.
* @return A signed 32-bit integer.
*/
public static int decodeZigZag32(final int n) {
return (n >>> 1) ^ -(n & 1);
}
/**
* Decode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers
* into values that can be efficiently encoded with varint. (Otherwise,
* negative values must be sign-extended to 64 bits to be varint encoded,
* thus always taking 10 bytes on the wire.)
*
* @param n An unsigned 64-bit integer, stored in a signed int because
* Java has no explicit unsigned support.
* @return A signed 64-bit integer.
*/
public static long decodeZigZag64(final long n) {
return (n >>> 1) ^ -(n & 1);
}
// -----------------------------------------------------------------
private final byte[] buffer;
private RandomAccessFile raf;
private int bufferSize;
private int bufferSizeAfterLimit;
private int bufferPos;
private final InputStream input;
private int lastTag;
/**
* The total number of bytes read before the current buffer. The total
* bytes read up to the current position can be computed as
* {@code totalBytesRetired + bufferPos}. This value may be negative if
* reading started in the middle of the current buffer (e.g. if the
* constructor that takes a byte array and an offset was used).
*/
private int totalBytesRetired;
/** The absolute position of the end of the current message. */
private int currentLimit = Integer.MAX_VALUE;
/** See setRecursionLimit() */
private int recursionDepth;
private int recursionLimit = DEFAULT_RECURSION_LIMIT;
/** See setSizeLimit() */
private int sizeLimit = DEFAULT_SIZE_LIMIT;
private static final int DEFAULT_RECURSION_LIMIT = 64;
private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB
private static final int BUFFER_SIZE = 5 * 1024;
private CodedInputStream(final byte[] buffer, final int off, final int len) {
this.buffer = buffer;
bufferSize = off + len;
bufferPos = off;
totalBytesRetired = -off;
input = null;
}
private CodedInputStream(final InputStream input) {
buffer = new byte[BUFFER_SIZE];
bufferSize = 0;
bufferPos = 0;
totalBytesRetired = 0;
this.input = input;
}
private CodedInputStream(final RandomAccessFile raf) {
buffer = new byte[BUFFER_SIZE];
this.bufferSize = 0;
bufferPos = 0;
totalBytesRetired = 0;
this.raf = raf;
input = null;
}
/**
* Set the maximum message recursion depth. In order to prevent malicious
* messages from causing stack overflows, {@code CodedInputStream} limits
* how deeply messages may be nested. The default limit is 64.
*
* @return the old limit.
*/
public int setRecursionLimit(final int limit) {
if (limit < 0) {
throw new IllegalArgumentException(
"Recursion limit cannot be negative: " + limit);
}
final int oldLimit = recursionLimit;
recursionLimit = limit;
return oldLimit;
}
/**
* Set the maximum message size. In order to prevent malicious
* messages from exhausting memory or causing integer overflows,
* {@code CodedInputStream} limits how large a message may be.
* The default limit is 64MB. You should set this limit as small
* as you can without harming your app's functionality. Note that
* size limits only apply when reading from an {@code InputStream}, not
* when constructed around a raw byte array (nor with
* {@link ByteString#newCodedInput}).
* <p>
* If you want to read several messages from a single CodedInputStream, you
* could call {@link #resetSizeCounter()} after each one to avoid hitting the
* size limit.
*
* @return the old limit.
*/
public int setSizeLimit(final int limit) {
if (limit < 0) {
throw new IllegalArgumentException(
"Size limit cannot be negative: " + limit);
}
final int oldLimit = sizeLimit;
sizeLimit = limit;
return oldLimit;
}
/**
* Resets the current size counter to zero (see {@link #setSizeLimit(int)}).
*/
public void resetSizeCounter() {
totalBytesRetired = -bufferPos;
}
/**
* Sets {@code currentLimit} to (current position) + {@code byteLimit}. This
* is called when descending into a length-delimited embedded message.
*
* <p>Note that {@code pushLimit()} does NOT affect how many bytes the
* {@code CodedInputStream} reads from an underlying {@code InputStream} when
* refreshing its buffer. If you need to prevent reading past a certain
* point in the underlying {@code InputStream} (e.g. because you expect it to
* contain more data after the end of the message which you need to handle
* differently) then you must place a wrapper around you {@code InputStream}
* which limits the amount of data that can be read from it.
*
* @return the old limit.
*/
public int pushLimit(int byteLimit) throws InvalidProtocolBufferException {
if (byteLimit < 0) {
throw InvalidProtocolBufferException.negativeSize();
}
byteLimit += totalBytesRetired + bufferPos;
final int oldLimit = currentLimit;
if (byteLimit > oldLimit) {
throw InvalidProtocolBufferException.truncatedMessage();
}
currentLimit = byteLimit;
recomputeBufferSizeAfterLimit();
return oldLimit;
}
private void recomputeBufferSizeAfterLimit() {
bufferSize += bufferSizeAfterLimit;
final int bufferEnd = totalBytesRetired + bufferSize;
if (bufferEnd > currentLimit) {
// Limit is in current buffer.
bufferSizeAfterLimit = bufferEnd - currentLimit;
bufferSize -= bufferSizeAfterLimit;
} else {
bufferSizeAfterLimit = 0;
}
}
/**
* Discards the current limit, returning to the previous limit.
*
* @param oldLimit The old limit, as returned by {@code pushLimit}.
*/
public void popLimit(final int oldLimit) {
currentLimit = oldLimit;
recomputeBufferSizeAfterLimit();
}
/**
* Returns the number of bytes to be read before the current limit.
* If no limit is set, returns -1.
*/
public int getBytesUntilLimit() {
if (currentLimit == Integer.MAX_VALUE) {
return -1;
}
final int currentAbsolutePosition = totalBytesRetired + bufferPos;
return currentLimit - currentAbsolutePosition;
}
/**
* Returns true if the stream has reached the end of the input. This is the
* case if either the end of the underlying input source has been reached or
* if the stream has reached a limit created using {@link #pushLimit(int)}.
*/
public boolean isAtEnd() throws IOException {
return bufferPos == bufferSize && !refillBuffer(false);
}
/**
* The total bytes read up to the current position. Calling
* {@link #resetSizeCounter()} resets this value to zero.
*/
public int getTotalBytesRead() {
return totalBytesRetired + bufferPos;
}
/**
* Called with {@code this.buffer} is empty to read more bytes from the
* input. If {@code mustSucceed} is true, refillBuffer() gurantees that
* either there will be at least one byte in the buffer when it returns
* or it will throw an exception. If {@code mustSucceed} is false,
* refillBuffer() returns false if no more bytes were available.
*/
private boolean refillBuffer(final boolean mustSucceed) throws IOException {
if (bufferPos < bufferSize) {
throw new IllegalStateException(
"refillBuffer() called when buffer wasn't empty.");
}
if (totalBytesRetired + bufferSize == currentLimit) {
// Oops, we hit a limit.
if (mustSucceed) {
throw InvalidProtocolBufferException.truncatedMessage();
} else {
return false;
}
}
totalBytesRetired += bufferSize;
bufferPos = 0;
if (raf != null) {
// osmand change
long remain = raf.length() - raf.getFilePointer();
bufferSize = (int) Math.min(remain, buffer.length);
if(bufferSize > 0) {
raf.readFully(buffer, 0, bufferSize);
} else {
bufferSize = -1;
}
} else {
bufferSize = (input == null) ? -1 : input.read(buffer);
}
if (bufferSize == 0 || bufferSize < -1) {
throw new IllegalStateException(
"InputStream#read(byte[]) returned invalid result: " + bufferSize +
"\nThe InputStream implementation is buggy.");
}
if (bufferSize == -1) {
bufferSize = 0;
if (mustSucceed) {
throw InvalidProtocolBufferException.truncatedMessage();
} else {
return false;
}
} else {
recomputeBufferSizeAfterLimit();
final int totalBytesRead =
totalBytesRetired + bufferSize + bufferSizeAfterLimit;
if (totalBytesRead > sizeLimit || totalBytesRead < 0) {
throw InvalidProtocolBufferException.sizeLimitExceeded();
}
return true;
}
}
/**
* Read one byte from the input.
*
* @throws InvalidProtocolBufferException The end of the stream or the current
* limit was reached.
*/
public byte readRawByte() throws IOException {
if (bufferPos == bufferSize) {
refillBuffer(true);
}
return buffer[bufferPos++];
}
/**
* Read a fixed size of bytes from the input.
*
* @throws InvalidProtocolBufferException The end of the stream or the current
* limit was reached.
*/
public byte[] readRawBytes(final int size) throws IOException {
if (size < 0) {
throw InvalidProtocolBufferException.negativeSize();
}
if (totalBytesRetired + bufferPos + size > currentLimit) {
// Read to the end of the stream anyway.
skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
// Then fail.
throw InvalidProtocolBufferException.truncatedMessage();
}
if (size <= bufferSize - bufferPos) {
// We have all the bytes we need already.
final byte[] bytes = new byte[size];
System.arraycopy(buffer, bufferPos, bytes, 0, size);
bufferPos += size;
return bytes;
} else if (size < BUFFER_SIZE) {
// Reading more bytes than are in the buffer, but not an excessive number
// of bytes. We can safely allocate the resulting array ahead of time.
// First copy what we have.
final byte[] bytes = new byte[size];
int pos = bufferSize - bufferPos;
System.arraycopy(buffer, bufferPos, bytes, 0, pos);
bufferPos = bufferSize;
// We want to use refillBuffer() and then copy from the buffer into our
// byte array rather than reading directly into our byte array because
// the input may be unbuffered.
refillBuffer(true);
while (size - pos > bufferSize) {
System.arraycopy(buffer, 0, bytes, pos, bufferSize);
pos += bufferSize;
bufferPos = bufferSize;
refillBuffer(true);
}
System.arraycopy(buffer, 0, bytes, pos, size - pos);
bufferPos = size - pos;
return bytes;
} else {
// The size is very large. For security reasons, we can't allocate the
// entire byte array yet. The size comes directly from the input, so a
// maliciously-crafted message could provide a bogus very large size in
// order to trick the app into allocating a lot of memory. We avoid this
// by allocating and reading only a small chunk at a time, so that the
// malicious message must actually *be* extremely large to cause
// problems. Meanwhile, we limit the allowed size of a message elsewhere.
// Remember the buffer markers since we'll have to copy the bytes out of
// it later.
final int originalBufferPos = bufferPos;
final int originalBufferSize = bufferSize;
// Mark the current buffer consumed.
totalBytesRetired += bufferSize;
bufferPos = 0;
bufferSize = 0;
// Read all the rest of the bytes we need.
int sizeLeft = size - (originalBufferSize - originalBufferPos);
final List<byte[]> chunks = new ArrayList<byte[]>();
while (sizeLeft > 0) {
final byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)];
int pos = 0;
while (pos < chunk.length) {
final int n;
// osmand change
if(raf != null) {
raf.readFully(chunk, pos, chunk.length - pos);
n = chunk.length - pos;
} else {
n = (input == null) ? -1 :
input.read(chunk, pos, chunk.length - pos);
}
if (n == -1) {
throw InvalidProtocolBufferException.truncatedMessage();
}
totalBytesRetired += n;
pos += n;
}
sizeLeft -= chunk.length;
chunks.add(chunk);
}
// OK, got everything. Now concatenate it all into one buffer.
final byte[] bytes = new byte[size];
// Start by copying the leftover bytes from this.buffer.
int pos = originalBufferSize - originalBufferPos;
System.arraycopy(buffer, originalBufferPos, bytes, 0, pos);
// And now all the chunks.
for (final byte[] chunk : chunks) {
System.arraycopy(chunk, 0, bytes, pos, chunk.length);
pos += chunk.length;
}
// Done.
return bytes;
}
}
/**
* Reads and discards {@code size} bytes.
*
* @throws InvalidProtocolBufferException The end of the stream or the current
* limit was reached.
*/
public void skipRawBytes(final int size) throws IOException {
if (size < 0) {
throw InvalidProtocolBufferException.negativeSize();
}
if (totalBytesRetired + bufferPos + size > currentLimit) {
// Read to the end of the stream anyway.
skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
// Then fail.
throw InvalidProtocolBufferException.truncatedMessage();
}
if (size <= bufferSize - bufferPos) {
// We have all the bytes we need already.
bufferPos += size;
} else {
// Skipping more bytes than are in the buffer. First skip what we have.
int pos = bufferSize - bufferPos;
totalBytesRetired += bufferSize; // ? pos incorrect
bufferPos = 0;
bufferSize = 0;
// Then skip directly from the InputStream for the rest.
while (pos < size) {
// osmand change
final int n ;
if(raf != null) {
n = raf.skipBytes(size - pos);
} else {
n = (input == null) ? -1 : (int) input.skip(size - pos);
}
if (n <= 0) {
throw InvalidProtocolBufferException.truncatedMessage();
}
pos += n;
totalBytesRetired += n;
}
}
}
// osmand change
public void seek(long pointer) throws IOException {
if (pointer - totalBytesRetired >= 0 && pointer - totalBytesRetired < bufferSize) {
if (pointer > currentLimit) {
// Then fail.
throw InvalidProtocolBufferException.truncatedMessage();
}
bufferPos = (int) (pointer - totalBytesRetired);
} else {
totalBytesRetired = (int) pointer;
bufferSizeAfterLimit = 0;
raf.seek(pointer);
bufferPos = 0;
bufferSize = 0;
}
}
}

View file

@ -0,0 +1,825 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.List;
/**
* Reads and decodes protocol message fields.
*
* This class contains two kinds of methods: methods that read specific
* protocol message constructs and field types (e.g. {@link #readTag()} and
* {@link #readInt32()}) and methods that read low-level values (e.g.
* {@link #readRawVarint32()} and {@link #readRawBytes}). If you are reading
* encoded protocol messages, you should use the former methods, but if you are
* reading some other format of your own design, use the latter.
*
* @author kenton@google.com Kenton Varda
* OSMAND change
*/
public final class CodedInputStreamRAF {
/**
* Create a new CodedInputStream wrapping the given InputStream.
*/
public static CodedInputStreamRAF newInstance(RandomAccessFile raf) {
return new CodedInputStreamRAF(raf, BUFFER_SIZE_DEF);
}
public static CodedInputStreamRAF newInstance(RandomAccessFile raf, int bufferSize) {
return new CodedInputStreamRAF(raf, bufferSize);
}
// -----------------------------------------------------------------
/**
* Attempt to read a field tag, returning zero if we have reached EOF.
* Protocol message parsers use this to read tags, since a protocol message
* may legally end wherever a tag occurs, and zero is not a valid tag number.
*/
public int readTag() throws IOException {
if (isAtEnd()) {
lastTag = 0;
return 0;
}
lastTag = readRawVarint32();
if (WireFormat.getTagFieldNumber(lastTag) == 0) {
// If we actually read zero (or any tag number corresponding to field
// number zero), that's not a valid tag.
throw InvalidProtocolBufferException.invalidTag();
}
return lastTag;
}
/**
* Verifies that the last call to readTag() returned the given tag value.
* This is used to verify that a nested group ended with the correct
* end tag.
*
* @throws InvalidProtocolBufferException {@code value} does not match the
* last tag.
*/
public void checkLastTagWas(final int value)
throws InvalidProtocolBufferException {
if (lastTag != value) {
throw InvalidProtocolBufferException.invalidEndTag();
}
}
/**
* Reads and discards a single field, given its tag value.
*
* @return {@code false} if the tag is an endgroup tag, in which case
* nothing is skipped. Otherwise, returns {@code true}.
*/
public boolean skipField(final int tag) throws IOException {
switch (WireFormat.getTagWireType(tag)) {
case WireFormat.WIRETYPE_VARINT:
readInt32();
return true;
case WireFormat.WIRETYPE_FIXED64:
readRawLittleEndian64();
return true;
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
skipRawBytes(readRawVarint32());
return true;
case WireFormat.WIRETYPE_START_GROUP:
skipMessage();
checkLastTagWas(
WireFormat.makeTag(WireFormat.getTagFieldNumber(tag),
WireFormat.WIRETYPE_END_GROUP));
return true;
case WireFormat.WIRETYPE_END_GROUP:
return false;
case WireFormat.WIRETYPE_FIXED32:
readRawLittleEndian32();
return true;
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
/**
* Reads and discards an entire message. This will read either until EOF
* or until an endgroup tag, whichever comes first.
*/
public void skipMessage() throws IOException {
while (true) {
final int tag = readTag();
if (tag == 0 || !skipField(tag)) {
return;
}
}
}
// -----------------------------------------------------------------
/** Read a {@code double} field value from the stream. */
public double readDouble() throws IOException {
return Double.longBitsToDouble(readRawLittleEndian64());
}
/** Read a {@code float} field value from the stream. */
public float readFloat() throws IOException {
return Float.intBitsToFloat(readRawLittleEndian32());
}
/** Read a {@code uint64} field value from the stream. */
public long readUInt64() throws IOException {
return readRawVarint64();
}
/** Read an {@code int64} field value from the stream. */
public long readInt64() throws IOException {
return readRawVarint64();
}
/** Read an {@code int32} field value from the stream. */
public int readInt32() throws IOException {
return readRawVarint32();
}
/** Read a {@code fixed64} field value from the stream. */
public long readFixed64() throws IOException {
return readRawLittleEndian64();
}
/** Read a {@code fixed32} field value from the stream. */
public int readFixed32() throws IOException {
return readRawLittleEndian32();
}
/** Read a {@code bool} field value from the stream. */
public boolean readBool() throws IOException {
return readRawVarint32() != 0;
}
/** Read a {@code string} field value from the stream. */
public String readString() throws IOException {
final int size = readRawVarint32();
if (size <= (bufferSize - bufferPos) && size > 0) {
// Fast path: We already have the bytes in a contiguous buffer, so
// just copy directly from it.
final String result = new String(buffer, bufferPos, size, "UTF-8");
bufferPos += size;
return result;
} else {
// Slow path: Build a byte array first then copy it.
return new String(readRawBytes(size), "UTF-8");
}
}
/** Read a {@code bytes} field value from the stream. */
public ByteString readBytes() throws IOException {
final int size = readRawVarint32();
if (size <= (bufferSize - bufferPos) && size > 0) {
// Fast path: We already have the bytes in a contiguous buffer, so
// just copy directly from it.
final ByteString result = ByteString.copyFrom(buffer, bufferPos, size);
bufferPos += size;
return result;
} else {
// Slow path: Build a byte array first then copy it.
return ByteString.copyFrom(readRawBytes(size));
}
}
/** Read a {@code uint32} field value from the stream. */
public int readUInt32() throws IOException {
return readRawVarint32();
}
/**
* Read an enum field value from the stream. Caller is responsible
* for converting the numeric value to an actual enum.
*/
public int readEnum() throws IOException {
return readRawVarint32();
}
/** Read an {@code sfixed32} field value from the stream. */
public int readSFixed32() throws IOException {
return readRawLittleEndian32();
}
/** Read an {@code sfixed64} field value from the stream. */
public long readSFixed64() throws IOException {
return readRawLittleEndian64();
}
/** Read an {@code sint32} field value from the stream. */
public int readSInt32() throws IOException {
return decodeZigZag32(readRawVarint32());
}
/** Read an {@code sint64} field value from the stream. */
public long readSInt64() throws IOException {
return decodeZigZag64(readRawVarint64());
}
// =================================================================
/**
* Read a raw Varint from the stream. If larger than 32 bits, discard the
* upper bits.
*/
public int readRawVarint32() throws IOException {
byte tmp = readRawByte();
if (tmp >= 0) {
return tmp;
}
int result = tmp & 0x7f;
if ((tmp = readRawByte()) >= 0) {
result |= tmp << 7;
} else {
result |= (tmp & 0x7f) << 7;
if ((tmp = readRawByte()) >= 0) {
result |= tmp << 14;
} else {
result |= (tmp & 0x7f) << 14;
if ((tmp = readRawByte()) >= 0) {
result |= tmp << 21;
} else {
result |= (tmp & 0x7f) << 21;
result |= (tmp = readRawByte()) << 28;
if (tmp < 0) {
// Discard upper 32 bits.
for (int i = 0; i < 5; i++) {
if (readRawByte() >= 0) {
return result;
}
}
throw InvalidProtocolBufferException.malformedVarint();
}
}
}
}
return result;
}
/**
* Reads a varint from the input one byte at a time, so that it does not
* read any bytes after the end of the varint. If you simply wrapped the
* stream in a CodedInputStream and used {@link #readRawVarint32(InputStream)}
* then you would probably end up reading past the end of the varint since
* CodedInputStream buffers its input.
*/
static int readRawVarint32(final InputStream input) throws IOException {
final int firstByte = input.read();
if (firstByte == -1) {
throw InvalidProtocolBufferException.truncatedMessage();
}
return readRawVarint32(firstByte, input);
}
/**
* Like {@link #readRawVarint32(InputStream)}, but expects that the caller
* has already read one byte. This allows the caller to determine if EOF
* has been reached before attempting to read.
*/
static int readRawVarint32(final int firstByte,
final InputStream input) throws IOException {
if ((firstByte & 0x80) == 0) {
return firstByte;
}
int result = firstByte & 0x7f;
int offset = 7;
for (; offset < 32; offset += 7) {
final int b = input.read();
if (b == -1) {
throw InvalidProtocolBufferException.truncatedMessage();
}
result |= (b & 0x7f) << offset;
if ((b & 0x80) == 0) {
return result;
}
}
// Keep reading up to 64 bits.
for (; offset < 64; offset += 7) {
final int b = input.read();
if (b == -1) {
throw InvalidProtocolBufferException.truncatedMessage();
}
if ((b & 0x80) == 0) {
return result;
}
}
throw InvalidProtocolBufferException.malformedVarint();
}
/** Read a raw Varint from the stream. */
public long readRawVarint64() throws IOException {
int shift = 0;
long result = 0;
while (shift < 64) {
final byte b = readRawByte();
result |= (long)(b & 0x7F) << shift;
if ((b & 0x80) == 0) {
return result;
}
shift += 7;
}
throw InvalidProtocolBufferException.malformedVarint();
}
/** Read a 32-bit little-endian integer from the stream. */
public int readRawLittleEndian32() throws IOException {
final byte b1 = readRawByte();
final byte b2 = readRawByte();
final byte b3 = readRawByte();
final byte b4 = readRawByte();
return (((int)b1 & 0xff) ) |
(((int)b2 & 0xff) << 8) |
(((int)b3 & 0xff) << 16) |
(((int)b4 & 0xff) << 24);
}
/** Read a 64-bit little-endian integer from the stream. */
public long readRawLittleEndian64() throws IOException {
final byte b1 = readRawByte();
final byte b2 = readRawByte();
final byte b3 = readRawByte();
final byte b4 = readRawByte();
final byte b5 = readRawByte();
final byte b6 = readRawByte();
final byte b7 = readRawByte();
final byte b8 = readRawByte();
return (((long)b1 & 0xff) ) |
(((long)b2 & 0xff) << 8) |
(((long)b3 & 0xff) << 16) |
(((long)b4 & 0xff) << 24) |
(((long)b5 & 0xff) << 32) |
(((long)b6 & 0xff) << 40) |
(((long)b7 & 0xff) << 48) |
(((long)b8 & 0xff) << 56);
}
/**
* Decode a ZigZag-encoded 32-bit value. ZigZag encodes signed integers
* into values that can be efficiently encoded with varint. (Otherwise,
* negative values must be sign-extended to 64 bits to be varint encoded,
* thus always taking 10 bytes on the wire.)
*
* @param n An unsigned 32-bit integer, stored in a signed int because
* Java has no explicit unsigned support.
* @return A signed 32-bit integer.
*/
public static int decodeZigZag32(final int n) {
return (n >>> 1) ^ -(n & 1);
}
/**
* Decode a ZigZag-encoded 64-bit value. ZigZag encodes signed integers
* into values that can be efficiently encoded with varint. (Otherwise,
* negative values must be sign-extended to 64 bits to be varint encoded,
* thus always taking 10 bytes on the wire.)
*
* @param n An unsigned 64-bit integer, stored in a signed int because
* Java has no explicit unsigned support.
* @return A signed 64-bit integer.
*/
public static long decodeZigZag64(final long n) {
return (n >>> 1) ^ -(n & 1);
}
// -----------------------------------------------------------------
private final byte[] buffer;
private int bufferSize;
private int bufferSizeAfterLimit;
private int bufferPos;
private final RandomAccessFile raf;
private int lastTag;
/**
* The total number of bytes read before the current buffer. The total
* bytes read up to the current position can be computed as
* {@code totalBytesRetired + bufferPos}. This value may be negative if
* reading started in the middle of the current buffer (e.g. if the
* constructor that takes a byte array and an offset was used).
*/
private int totalBytesRetired;
/** The absolute position of the end of the current message. */
private int currentLimit = Integer.MAX_VALUE;
/** See setRecursionLimit() */
private int recursionDepth;
private int recursionLimit = DEFAULT_RECURSION_LIMIT;
/** See setSizeLimit() */
private int sizeLimit = DEFAULT_SIZE_LIMIT;
private static final int DEFAULT_RECURSION_LIMIT = 64;
private static final int DEFAULT_SIZE_LIMIT = 64 << 20; // 64MB
private static final int BUFFER_SIZE_DEF = 4096;
private int BUFFER_SIZE = BUFFER_SIZE_DEF;
private CodedInputStreamRAF(final RandomAccessFile raf, int bufferSize) {
BUFFER_SIZE = bufferSize;
buffer = new byte[BUFFER_SIZE];
this.bufferSize = 0;
bufferPos = 0;
totalBytesRetired = 0;
this.raf = raf;
}
/**
* Set the maximum message recursion depth. In order to prevent malicious
* messages from causing stack overflows, {@code CodedInputStream} limits
* how deeply messages may be nested. The default limit is 64.
*
* @return the old limit.
*/
public int setRecursionLimit(final int limit) {
if (limit < 0) {
throw new IllegalArgumentException(
"Recursion limit cannot be negative: " + limit);
}
final int oldLimit = recursionLimit;
recursionLimit = limit;
return oldLimit;
}
/**
* Set the maximum message size. In order to prevent malicious
* messages from exhausting memory or causing integer overflows,
* {@code CodedInputStream} limits how large a message may be.
* The default limit is 64MB. You should set this limit as small
* as you can without harming your app's functionality. Note that
* size limits only apply when reading from an {@code InputStream}, not
* when constructed around a raw byte array (nor with
* {@link ByteString#newCodedInput}).
* <p>
* If you want to read several messages from a single CodedInputStream, you
* could call {@link #resetSizeCounter()} after each one to avoid hitting the
* size limit.
*
* @return the old limit.
*/
public int setSizeLimit(final int limit) {
if (limit < 0) {
throw new IllegalArgumentException(
"Size limit cannot be negative: " + limit);
}
final int oldLimit = sizeLimit;
sizeLimit = limit;
return oldLimit;
}
/**
* Resets the current size counter to zero (see {@link #setSizeLimit(int)}).
*/
public void resetSizeCounter() {
totalBytesRetired = -bufferPos;
}
/**
* Sets {@code currentLimit} to (current position) + {@code byteLimit}. This
* is called when descending into a length-delimited embedded message.
*
* <p>Note that {@code pushLimit()} does NOT affect how many bytes the
* {@code CodedInputStream} reads from an underlying {@code InputStream} when
* refreshing its buffer. If you need to prevent reading past a certain
* point in the underlying {@code InputStream} (e.g. because you expect it to
* contain more data after the end of the message which you need to handle
* differently) then you must place a wrapper around you {@code InputStream}
* which limits the amount of data that can be read from it.
*
* @return the old limit.
*/
public int pushLimit(int byteLimit) throws InvalidProtocolBufferException {
if (byteLimit < 0) {
throw InvalidProtocolBufferException.negativeSize();
}
byteLimit += totalBytesRetired + bufferPos;
final int oldLimit = currentLimit;
if (byteLimit > oldLimit) {
throw InvalidProtocolBufferException.truncatedMessage();
}
currentLimit = byteLimit;
recomputeBufferSizeAfterLimit();
return oldLimit;
}
private void recomputeBufferSizeAfterLimit() {
bufferSize += bufferSizeAfterLimit;
final int bufferEnd = totalBytesRetired + bufferSize;
if (bufferEnd > currentLimit) {
// Limit is in current buffer.
bufferSizeAfterLimit = bufferEnd - currentLimit;
bufferSize -= bufferSizeAfterLimit;
} else {
bufferSizeAfterLimit = 0;
}
}
/**
* Discards the current limit, returning to the previous limit.
*
* @param oldLimit The old limit, as returned by {@code pushLimit}.
*/
public void popLimit(final int oldLimit) {
currentLimit = oldLimit;
recomputeBufferSizeAfterLimit();
}
/**
* Returns the number of bytes to be read before the current limit.
* If no limit is set, returns -1.
*/
public int getBytesUntilLimit() {
if (currentLimit == Integer.MAX_VALUE) {
return -1;
}
final int currentAbsolutePosition = totalBytesRetired + bufferPos;
return currentLimit - currentAbsolutePosition;
}
/**
* Returns true if the stream has reached the end of the input. This is the
* case if either the end of the underlying input source has been reached or
* if the stream has reached a limit created using {@link #pushLimit(int)}.
*/
public boolean isAtEnd() throws IOException {
return bufferPos == bufferSize && !refillBuffer(false);
}
/**
* The total bytes read up to the current position. Calling
* {@link #resetSizeCounter()} resets this value to zero.
*/
public int getTotalBytesRead() {
return totalBytesRetired + bufferPos;
}
/**
* Called with {@code this.buffer} is empty to read more bytes from the
* input. If {@code mustSucceed} is true, refillBuffer() gurantees that
* either there will be at least one byte in the buffer when it returns
* or it will throw an exception. If {@code mustSucceed} is false,
* refillBuffer() returns false if no more bytes were available.
*/
private boolean refillBuffer(final boolean mustSucceed) throws IOException {
if (bufferPos < bufferSize) {
throw new IllegalStateException(
"refillBuffer() called when buffer wasn't empty.");
}
if (totalBytesRetired + bufferSize == currentLimit) {
// Oops, we hit a limit.
if (mustSucceed) {
throw InvalidProtocolBufferException.truncatedMessage();
} else {
return false;
}
}
totalBytesRetired += bufferSize;
bufferPos = 0;
// osmand change
bufferSize = raf.read(buffer);
if (bufferSize == 0 || bufferSize < -1) {
throw new IllegalStateException(
"InputStream#read(byte[]) returned invalid result: " + bufferSize +
"\nThe InputStream implementation is buggy.");
}
if (bufferSize == -1) {
bufferSize = 0;
if (mustSucceed) {
throw InvalidProtocolBufferException.truncatedMessage();
} else {
return false;
}
} else {
recomputeBufferSizeAfterLimit();
final int totalBytesRead =
totalBytesRetired + bufferSize + bufferSizeAfterLimit;
if (totalBytesRead > sizeLimit || totalBytesRead < 0) {
throw InvalidProtocolBufferException.sizeLimitExceeded();
}
return true;
}
}
/**
* Read one byte from the input.
*
* @throws InvalidProtocolBufferException The end of the stream or the current
* limit was reached.
*/
public byte readRawByte() throws IOException {
if (bufferPos == bufferSize) {
refillBuffer(true);
}
return buffer[bufferPos++];
}
/**
* Read a fixed size of bytes from the input.
*
* @throws InvalidProtocolBufferException The end of the stream or the current
* limit was reached.
*/
public byte[] readRawBytes(final int size) throws IOException {
if (size < 0) {
throw InvalidProtocolBufferException.negativeSize();
}
if (totalBytesRetired + bufferPos + size > currentLimit) {
// Read to the end of the stream anyway.
skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
// Then fail.
throw InvalidProtocolBufferException.truncatedMessage();
}
if (size <= bufferSize - bufferPos) {
// We have all the bytes we need already.
final byte[] bytes = new byte[size];
System.arraycopy(buffer, bufferPos, bytes, 0, size);
bufferPos += size;
return bytes;
} else if (size < BUFFER_SIZE) {
// Reading more bytes than are in the buffer, but not an excessive number
// of bytes. We can safely allocate the resulting array ahead of time.
// First copy what we have.
final byte[] bytes = new byte[size];
int pos = bufferSize - bufferPos;
System.arraycopy(buffer, bufferPos, bytes, 0, pos);
bufferPos = bufferSize;
// We want to use refillBuffer() and then copy from the buffer into our
// byte array rather than reading directly into our byte array because
// the input may be unbuffered.
refillBuffer(true);
while (size - pos > bufferSize) {
System.arraycopy(buffer, 0, bytes, pos, bufferSize);
pos += bufferSize;
bufferPos = bufferSize;
refillBuffer(true);
}
System.arraycopy(buffer, 0, bytes, pos, size - pos);
bufferPos = size - pos;
return bytes;
} else {
// The size is very large. For security reasons, we can't allocate the
// entire byte array yet. The size comes directly from the input, so a
// maliciously-crafted message could provide a bogus very large size in
// order to trick the app into allocating a lot of memory. We avoid this
// by allocating and reading only a small chunk at a time, so that the
// malicious message must actually *be* extremely large to cause
// problems. Meanwhile, we limit the allowed size of a message elsewhere.
// Remember the buffer markers since we'll have to copy the bytes out of
// it later.
final int originalBufferPos = bufferPos;
final int originalBufferSize = bufferSize;
// Mark the current buffer consumed.
totalBytesRetired += bufferSize;
bufferPos = 0;
bufferSize = 0;
// Read all the rest of the bytes we need.
int sizeLeft = size - (originalBufferSize - originalBufferPos);
final List<byte[]> chunks = new ArrayList<byte[]>();
while (sizeLeft > 0) {
final byte[] chunk = new byte[Math.min(sizeLeft, BUFFER_SIZE)];
int pos = 0;
while (pos < chunk.length) {
// osmand change
final int n = raf.read(chunk, pos, chunk.length - pos);
if (n == -1) {
throw InvalidProtocolBufferException.truncatedMessage();
}
totalBytesRetired += n;
pos += n;
}
sizeLeft -= chunk.length;
chunks.add(chunk);
}
// OK, got everything. Now concatenate it all into one buffer.
final byte[] bytes = new byte[size];
// Start by copying the leftover bytes from this.buffer.
int pos = originalBufferSize - originalBufferPos;
System.arraycopy(buffer, originalBufferPos, bytes, 0, pos);
// And now all the chunks.
for (final byte[] chunk : chunks) {
System.arraycopy(chunk, 0, bytes, pos, chunk.length);
pos += chunk.length;
}
// Done.
return bytes;
}
}
/**
* Reads and discards {@code size} bytes.
*
* @throws InvalidProtocolBufferException The end of the stream or the current
* limit was reached.
*/
public void skipRawBytes(final int size) throws IOException {
if (size < 0) {
throw InvalidProtocolBufferException.negativeSize();
}
if (totalBytesRetired + bufferPos + size > currentLimit) {
// Read to the end of the stream anyway.
skipRawBytes(currentLimit - totalBytesRetired - bufferPos);
// Then fail.
throw InvalidProtocolBufferException.truncatedMessage();
}
if (size <= bufferSize - bufferPos) {
// We have all the bytes we need already.
bufferPos += size;
} else {
// Skipping more bytes than are in the buffer. First skip what we have.
int pos = bufferSize - bufferPos;
totalBytesRetired += bufferSize;
bufferPos = 0;
bufferSize = 0;
// Then skip directly from the InputStream for the rest.
while (pos < size) {
// osmand change
final int n = raf.skipBytes(size - pos);
if (n <= 0) {
throw InvalidProtocolBufferException.truncatedMessage();
}
pos += n;
totalBytesRetired += n;
}
}
}
// osmand change
public void seek(long pointer) throws IOException{
if(pointer - totalBytesRetired >= 0 && pointer - totalBytesRetired < bufferSize){
if (pointer > currentLimit) {
// Then fail.
throw InvalidProtocolBufferException.truncatedMessage();
}
bufferPos = (int) (pointer - totalBytesRetired);
} else {
totalBytesRetired = (int) pointer;
bufferSizeAfterLimit = 0;
raf.seek(pointer);
bufferPos = 0;
bufferSize = 0;
}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,472 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
/**
* An implementation of {@link Message} that can represent arbitrary types,
* given a {@link Descriptors.Descriptor}.
*
* @author kenton@google.com Kenton Varda
*/
public final class DynamicMessage extends AbstractMessage {
private final Descriptor type;
private final FieldSet<FieldDescriptor> fields;
private final UnknownFieldSet unknownFields;
private int memoizedSize = -1;
/**
* Construct a {@code DynamicMessage} using the given {@code FieldSet}.
*/
private DynamicMessage(Descriptor type, FieldSet<FieldDescriptor> fields,
UnknownFieldSet unknownFields) {
this.type = type;
this.fields = fields;
this.unknownFields = unknownFields;
}
/**
* Get a {@code DynamicMessage} representing the default instance of the
* given type.
*/
public static DynamicMessage getDefaultInstance(Descriptor type) {
return new DynamicMessage(type, FieldSet.<FieldDescriptor>emptySet(),
UnknownFieldSet.getDefaultInstance());
}
/** Parse a message of the given type from the given input stream. */
public static DynamicMessage parseFrom(Descriptor type,
CodedInputStream input)
throws IOException {
return newBuilder(type).mergeFrom(input).buildParsed();
}
/** Parse a message of the given type from the given input stream. */
public static DynamicMessage parseFrom(
Descriptor type,
CodedInputStream input,
ExtensionRegistry extensionRegistry)
throws IOException {
return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed();
}
/** Parse {@code data} as a message of the given type and return it. */
public static DynamicMessage parseFrom(Descriptor type, ByteString data)
throws InvalidProtocolBufferException {
return newBuilder(type).mergeFrom(data).buildParsed();
}
/** Parse {@code data} as a message of the given type and return it. */
public static DynamicMessage parseFrom(Descriptor type, ByteString data,
ExtensionRegistry extensionRegistry)
throws InvalidProtocolBufferException {
return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed();
}
/** Parse {@code data} as a message of the given type and return it. */
public static DynamicMessage parseFrom(Descriptor type, byte[] data)
throws InvalidProtocolBufferException {
return newBuilder(type).mergeFrom(data).buildParsed();
}
/** Parse {@code data} as a message of the given type and return it. */
public static DynamicMessage parseFrom(Descriptor type, byte[] data,
ExtensionRegistry extensionRegistry)
throws InvalidProtocolBufferException {
return newBuilder(type).mergeFrom(data, extensionRegistry).buildParsed();
}
/** Parse a message of the given type from {@code input} and return it. */
public static DynamicMessage parseFrom(Descriptor type, InputStream input)
throws IOException {
return newBuilder(type).mergeFrom(input).buildParsed();
}
/** Parse a message of the given type from {@code input} and return it. */
public static DynamicMessage parseFrom(Descriptor type, InputStream input,
ExtensionRegistry extensionRegistry)
throws IOException {
return newBuilder(type).mergeFrom(input, extensionRegistry).buildParsed();
}
/** Construct a {@link Message.Builder} for the given type. */
public static Builder newBuilder(Descriptor type) {
return new Builder(type);
}
/**
* Construct a {@link Message.Builder} for a message of the same type as
* {@code prototype}, and initialize it with {@code prototype}'s contents.
*/
public static Builder newBuilder(Message prototype) {
return new Builder(prototype.getDescriptorForType()).mergeFrom(prototype);
}
// -----------------------------------------------------------------
// Implementation of Message interface.
@Override
public Descriptor getDescriptorForType() {
return type;
}
@Override
public DynamicMessage getDefaultInstanceForType() {
return getDefaultInstance(type);
}
@Override
public Map<FieldDescriptor, Object> getAllFields() {
return fields.getAllFields();
}
@Override
public boolean hasField(FieldDescriptor field) {
verifyContainingType(field);
return fields.hasField(field);
}
@Override
public Object getField(FieldDescriptor field) {
verifyContainingType(field);
Object result = fields.getField(field);
if (result == null) {
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
result = getDefaultInstance(field.getMessageType());
} else {
result = field.getDefaultValue();
}
}
return result;
}
@Override
public int getRepeatedFieldCount(FieldDescriptor field) {
verifyContainingType(field);
return fields.getRepeatedFieldCount(field);
}
@Override
public Object getRepeatedField(FieldDescriptor field, int index) {
verifyContainingType(field);
return fields.getRepeatedField(field, index);
}
@Override
public UnknownFieldSet getUnknownFields() {
return unknownFields;
}
private static boolean isInitialized(Descriptor type,
FieldSet<FieldDescriptor> fields) {
// Check that all required fields are present.
for (final FieldDescriptor field : type.getFields()) {
if (field.isRequired()) {
if (!fields.hasField(field)) {
return false;
}
}
}
// Check that embedded messages are initialized.
return fields.isInitialized();
}
@Override
public boolean isInitialized() {
return isInitialized(type, fields);
}
@Override
public void writeTo(CodedOutputStream output) throws IOException {
if (type.getOptions().getMessageSetWireFormat()) {
fields.writeMessageSetTo(output);
unknownFields.writeAsMessageSetTo(output);
} else {
fields.writeTo(output);
unknownFields.writeTo(output);
}
}
@Override
public int getSerializedSize() {
int size = memoizedSize;
if (size != -1) return size;
if (type.getOptions().getMessageSetWireFormat()) {
size = fields.getMessageSetSerializedSize();
size += unknownFields.getSerializedSizeAsMessageSet();
} else {
size = fields.getSerializedSize();
size += unknownFields.getSerializedSize();
}
memoizedSize = size;
return size;
}
@Override
public Builder newBuilderForType() {
return new Builder(type);
}
@Override
public Builder toBuilder() {
return newBuilderForType().mergeFrom(this);
}
/** Verifies that the field is a field of this message. */
private void verifyContainingType(FieldDescriptor field) {
if (field.getContainingType() != type) {
throw new IllegalArgumentException(
"FieldDescriptor does not match message type.");
}
}
// =================================================================
/**
* Builder for {@link DynamicMessage}s.
*/
public static final class Builder extends AbstractMessage.Builder<Builder> {
private final Descriptor type;
private FieldSet<FieldDescriptor> fields;
private UnknownFieldSet unknownFields;
/** Construct a {@code Builder} for the given type. */
private Builder(Descriptor type) {
this.type = type;
this.fields = FieldSet.newFieldSet();
this.unknownFields = UnknownFieldSet.getDefaultInstance();
}
// ---------------------------------------------------------------
// Implementation of Message.Builder interface.
@Override
public Builder clear() {
if (fields == null) {
throw new IllegalStateException("Cannot call clear() after build().");
}
fields.clear();
return this;
}
@Override
public Builder mergeFrom(Message other) {
if (other instanceof DynamicMessage) {
// This should be somewhat faster than calling super.mergeFrom().
DynamicMessage otherDynamicMessage = (DynamicMessage) other;
if (otherDynamicMessage.type != type) {
throw new IllegalArgumentException(
"mergeFrom(Message) can only merge messages of the same type.");
}
fields.mergeFrom(otherDynamicMessage.fields);
mergeUnknownFields(otherDynamicMessage.unknownFields);
return this;
} else {
return super.mergeFrom(other);
}
}
@Override
public DynamicMessage build() {
// If fields == null, we'll throw an appropriate exception later.
if (fields != null && !isInitialized()) {
throw newUninitializedMessageException(
new DynamicMessage(type, fields, unknownFields));
}
return buildPartial();
}
/**
* Helper for DynamicMessage.parseFrom() methods to call. Throws
* {@link InvalidProtocolBufferException} instead of
* {@link UninitializedMessageException}.
*/
private DynamicMessage buildParsed() throws InvalidProtocolBufferException {
if (!isInitialized()) {
throw newUninitializedMessageException(
new DynamicMessage(type, fields, unknownFields))
.asInvalidProtocolBufferException();
}
return buildPartial();
}
@Override
public DynamicMessage buildPartial() {
if (fields == null) {
throw new IllegalStateException(
"build() has already been called on this Builder.");
}
fields.makeImmutable();
DynamicMessage result =
new DynamicMessage(type, fields, unknownFields);
fields = null;
unknownFields = null;
return result;
}
@Override
public Builder clone() {
Builder result = new Builder(type);
result.fields.mergeFrom(fields);
return result;
}
@Override
public boolean isInitialized() {
return DynamicMessage.isInitialized(type, fields);
}
@Override
public Descriptor getDescriptorForType() {
return type;
}
@Override
public DynamicMessage getDefaultInstanceForType() {
return getDefaultInstance(type);
}
@Override
public Map<FieldDescriptor, Object> getAllFields() {
return fields.getAllFields();
}
@Override
public Builder newBuilderForField(FieldDescriptor field) {
verifyContainingType(field);
if (field.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
throw new IllegalArgumentException(
"newBuilderForField is only valid for fields with message type.");
}
return new Builder(field.getMessageType());
}
@Override
public boolean hasField(FieldDescriptor field) {
verifyContainingType(field);
return fields.hasField(field);
}
@Override
public Object getField(FieldDescriptor field) {
verifyContainingType(field);
Object result = fields.getField(field);
if (result == null) {
if (field.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
result = getDefaultInstance(field.getMessageType());
} else {
result = field.getDefaultValue();
}
}
return result;
}
@Override
public Builder setField(FieldDescriptor field, Object value) {
verifyContainingType(field);
fields.setField(field, value);
return this;
}
@Override
public Builder clearField(FieldDescriptor field) {
verifyContainingType(field);
fields.clearField(field);
return this;
}
@Override
public int getRepeatedFieldCount(FieldDescriptor field) {
verifyContainingType(field);
return fields.getRepeatedFieldCount(field);
}
@Override
public Object getRepeatedField(FieldDescriptor field, int index) {
verifyContainingType(field);
return fields.getRepeatedField(field, index);
}
@Override
public Builder setRepeatedField(FieldDescriptor field,
int index, Object value) {
verifyContainingType(field);
fields.setRepeatedField(field, index, value);
return this;
}
@Override
public Builder addRepeatedField(FieldDescriptor field, Object value) {
verifyContainingType(field);
fields.addRepeatedField(field, value);
return this;
}
@Override
public UnknownFieldSet getUnknownFields() {
return unknownFields;
}
@Override
public Builder setUnknownFields(UnknownFieldSet unknownFields) {
this.unknownFields = unknownFields;
return this;
}
@Override
public Builder mergeUnknownFields(UnknownFieldSet unknownFields) {
this.unknownFields =
UnknownFieldSet.newBuilder(this.unknownFields)
.mergeFrom(unknownFields)
.build();
return this;
}
/** Verifies that the field is a field of this message. */
private void verifyContainingType(FieldDescriptor field) {
if (field.getContainingType() != type) {
throw new IllegalArgumentException(
"FieldDescriptor does not match message type.");
}
}
}
}

View file

@ -0,0 +1,266 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
/**
* A table of known extensions, searchable by name or field number. When
* parsing a protocol message that might have extensions, you must provide
* an {@code ExtensionRegistry} in which you have registered any extensions
* that you want to be able to parse. Otherwise, those extensions will just
* be treated like unknown fields.
*
* <p>For example, if you had the {@code .proto} file:
*
* <pre>
* option java_class = "MyProto";
*
* message Foo {
* extensions 1000 to max;
* }
*
* extend Foo {
* optional int32 bar;
* }
* </pre>
*
* Then you might write code like:
*
* <pre>
* ExtensionRegistry registry = ExtensionRegistry.newInstance();
* registry.add(MyProto.bar);
* MyProto.Foo message = MyProto.Foo.parseFrom(input, registry);
* </pre>
*
* <p>Background:
*
* <p>You might wonder why this is necessary. Two alternatives might come to
* mind. First, you might imagine a system where generated extensions are
* automatically registered when their containing classes are loaded. This
* is a popular technique, but is bad design; among other things, it creates a
* situation where behavior can change depending on what classes happen to be
* loaded. It also introduces a security vulnerability, because an
* unprivileged class could cause its code to be called unexpectedly from a
* privileged class by registering itself as an extension of the right type.
*
* <p>Another option you might consider is lazy parsing: do not parse an
* extension until it is first requested, at which point the caller must
* provide a type to use. This introduces a different set of problems. First,
* it would require a mutex lock any time an extension was accessed, which
* would be slow. Second, corrupt data would not be detected until first
* access, at which point it would be much harder to deal with it. Third, it
* could violate the expectation that message objects are immutable, since the
* type provided could be any arbitrary message class. An unprivileged user
* could take advantage of this to inject a mutable object into a message
* belonging to privileged code and create mischief.
*
* @author kenton@google.com Kenton Varda
*/
public final class ExtensionRegistry extends ExtensionRegistryLite {
/** Construct a new, empty instance. */
public static ExtensionRegistry newInstance() {
return new ExtensionRegistry();
}
/** Get the unmodifiable singleton empty instance. */
public static ExtensionRegistry getEmptyRegistry() {
return EMPTY;
}
/** Returns an unmodifiable view of the registry. */
@Override
public ExtensionRegistry getUnmodifiable() {
return new ExtensionRegistry(this);
}
/** A (Descriptor, Message) pair, returned by lookup methods. */
public static final class ExtensionInfo {
/** The extension's descriptor. */
public final FieldDescriptor descriptor;
/**
* A default instance of the extension's type, if it has a message type.
* Otherwise, {@code null}.
*/
public final Message defaultInstance;
private ExtensionInfo(final FieldDescriptor descriptor) {
this.descriptor = descriptor;
defaultInstance = null;
}
private ExtensionInfo(final FieldDescriptor descriptor,
final Message defaultInstance) {
this.descriptor = descriptor;
this.defaultInstance = defaultInstance;
}
}
/**
* Find an extension by fully-qualified field name, in the proto namespace.
* I.e. {@code result.descriptor.fullName()} will match {@code fullName} if
* a match is found.
*
* @return Information about the extension if found, or {@code null}
* otherwise.
*/
public ExtensionInfo findExtensionByName(final String fullName) {
return extensionsByName.get(fullName);
}
/**
* Find an extension by containing type and field number.
*
* @return Information about the extension if found, or {@code null}
* otherwise.
*/
public ExtensionInfo findExtensionByNumber(final Descriptor containingType,
final int fieldNumber) {
return extensionsByNumber.get(
new DescriptorIntPair(containingType, fieldNumber));
}
/** Add an extension from a generated file to the registry. */
public void add(final GeneratedMessage.GeneratedExtension<?, ?> extension) {
if (extension.getDescriptor().getJavaType() ==
FieldDescriptor.JavaType.MESSAGE) {
if (extension.getMessageDefaultInstance() == null) {
throw new IllegalStateException(
"Registered message-type extension had null default instance: " +
extension.getDescriptor().getFullName());
}
add(new ExtensionInfo(extension.getDescriptor(),
extension.getMessageDefaultInstance()));
} else {
add(new ExtensionInfo(extension.getDescriptor(), null));
}
}
/** Add a non-message-type extension to the registry by descriptor. */
public void add(final FieldDescriptor type) {
if (type.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
throw new IllegalArgumentException(
"ExtensionRegistry.add() must be provided a default instance when " +
"adding an embedded message extension.");
}
add(new ExtensionInfo(type, null));
}
/** Add a message-type extension to the registry by descriptor. */
public void add(final FieldDescriptor type, final Message defaultInstance) {
if (type.getJavaType() != FieldDescriptor.JavaType.MESSAGE) {
throw new IllegalArgumentException(
"ExtensionRegistry.add() provided a default instance for a " +
"non-message extension.");
}
add(new ExtensionInfo(type, defaultInstance));
}
// =================================================================
// Private stuff.
private ExtensionRegistry() {
this.extensionsByName = new HashMap<String, ExtensionInfo>();
this.extensionsByNumber = new HashMap<DescriptorIntPair, ExtensionInfo>();
}
private ExtensionRegistry(ExtensionRegistry other) {
super(other);
this.extensionsByName = Collections.unmodifiableMap(other.extensionsByName);
this.extensionsByNumber =
Collections.unmodifiableMap(other.extensionsByNumber);
}
private final Map<String, ExtensionInfo> extensionsByName;
private final Map<DescriptorIntPair, ExtensionInfo> extensionsByNumber;
private ExtensionRegistry(boolean empty) {
super(ExtensionRegistryLite.getEmptyRegistry());
this.extensionsByName = Collections.<String, ExtensionInfo>emptyMap();
this.extensionsByNumber =
Collections.<DescriptorIntPair, ExtensionInfo>emptyMap();
}
private static final ExtensionRegistry EMPTY = new ExtensionRegistry(true);
private void add(final ExtensionInfo extension) {
if (!extension.descriptor.isExtension()) {
throw new IllegalArgumentException(
"ExtensionRegistry.add() was given a FieldDescriptor for a regular " +
"(non-extension) field.");
}
extensionsByName.put(extension.descriptor.getFullName(), extension);
extensionsByNumber.put(
new DescriptorIntPair(extension.descriptor.getContainingType(),
extension.descriptor.getNumber()),
extension);
final FieldDescriptor field = extension.descriptor;
if (field.getContainingType().getOptions().getMessageSetWireFormat() &&
field.getType() == FieldDescriptor.Type.MESSAGE &&
field.isOptional() &&
field.getExtensionScope() == field.getMessageType()) {
// This is an extension of a MessageSet type defined within the extension
// type's own scope. For backwards-compatibility, allow it to be looked
// up by type name.
extensionsByName.put(field.getMessageType().getFullName(), extension);
}
}
/** A (GenericDescriptor, int) pair, used as a map key. */
private static final class DescriptorIntPair {
private final Descriptor descriptor;
private final int number;
DescriptorIntPair(final Descriptor descriptor, final int number) {
this.descriptor = descriptor;
this.number = number;
}
@Override
public int hashCode() {
return descriptor.hashCode() * ((1 << 16) - 1) + number;
}
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof DescriptorIntPair)) {
return false;
}
final DescriptorIntPair other = (DescriptorIntPair)obj;
return descriptor == other.descriptor && number == other.number;
}
}
}

View file

@ -0,0 +1,169 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Equivalent to {@link ExtensionRegistry} but supports only "lite" types.
* <p>
* If all of your types are lite types, then you only need to use
* {@code ExtensionRegistryLite}. Similarly, if all your types are regular
* types, then you only need {@link ExtensionRegistry}. Typically it does not
* make sense to mix the two, since if you have any regular types in your
* program, you then require the full runtime and lose all the benefits of
* the lite runtime, so you might as well make all your types be regular types.
* However, in some cases (e.g. when depending on multiple third-patry libraries
* where one uses lite types and one uses regular), you may find yourself
* wanting to mix the two. In this case things get more complicated.
* <p>
* There are three factors to consider: Whether the type being extended is
* lite, whether the embedded type (in the case of a message-typed extension)
* is lite, and whether the extension itself is lite. Since all three are
* declared in different files, they could all be different. Here are all
* the combinations and which type of registry to use:
* <pre>
* Extended type Inner type Extension Use registry
* =======================================================================
* lite lite lite ExtensionRegistryLite
* lite regular lite ExtensionRegistry
* regular regular regular ExtensionRegistry
* all other combinations not supported
* </pre>
* <p>
* Note that just as regular types are not allowed to contain lite-type fields,
* they are also not allowed to contain lite-type extensions. This is because
* regular types must be fully accessible via reflection, which in turn means
* that all the inner messages must also support reflection. On the other hand,
* since regular types implement the entire lite interface, there is no problem
* with embedding regular types inside lite types.
*
* @author kenton@google.com Kenton Varda
*/
public class ExtensionRegistryLite {
/** Construct a new, empty instance. */
public static ExtensionRegistryLite newInstance() {
return new ExtensionRegistryLite();
}
/** Get the unmodifiable singleton empty instance. */
public static ExtensionRegistryLite getEmptyRegistry() {
return EMPTY;
}
/** Returns an unmodifiable view of the registry. */
public ExtensionRegistryLite getUnmodifiable() {
return new ExtensionRegistryLite(this);
}
/**
* Find an extension by containing type and field number.
*
* @return Information about the extension if found, or {@code null}
* otherwise.
*/
@SuppressWarnings("unchecked")
public <ContainingType extends MessageLite>
GeneratedMessageLite.GeneratedExtension<ContainingType, ?>
findLiteExtensionByNumber(
final ContainingType containingTypeDefaultInstance,
final int fieldNumber) {
return (GeneratedMessageLite.GeneratedExtension<ContainingType, ?>)
extensionsByNumber.get(
new ObjectIntPair(containingTypeDefaultInstance, fieldNumber));
}
/** Add an extension from a lite generated file to the registry. */
public final void add(
final GeneratedMessageLite.GeneratedExtension<?, ?> extension) {
extensionsByNumber.put(
new ObjectIntPair(extension.getContainingTypeDefaultInstance(),
extension.getNumber()),
extension);
}
// =================================================================
// Private stuff.
// Constructors are package-private so that ExtensionRegistry can subclass
// this.
ExtensionRegistryLite() {
this.extensionsByNumber =
new HashMap<ObjectIntPair,
GeneratedMessageLite.GeneratedExtension<?, ?>>();
}
ExtensionRegistryLite(ExtensionRegistryLite other) {
if (other == EMPTY) {
this.extensionsByNumber = Collections.emptyMap();
} else {
this.extensionsByNumber =
Collections.unmodifiableMap(other.extensionsByNumber);
}
}
private final Map<ObjectIntPair,
GeneratedMessageLite.GeneratedExtension<?, ?>>
extensionsByNumber;
private ExtensionRegistryLite(boolean empty) {
this.extensionsByNumber = Collections.emptyMap();
}
private static final ExtensionRegistryLite EMPTY =
new ExtensionRegistryLite(true);
/** A (Object, int) pair, used as a map key. */
private static final class ObjectIntPair {
private final Object object;
private final int number;
ObjectIntPair(final Object object, final int number) {
this.object = object;
this.number = number;
}
@Override
public int hashCode() {
return System.identityHashCode(object) * ((1 << 16) - 1) + number;
}
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof ObjectIntPair)) {
return false;
}
final ObjectIntPair other = (ObjectIntPair)obj;
return object == other.object && number == other.number;
}
}
}

View file

@ -0,0 +1,715 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* A class which represents an arbitrary set of fields of some message type.
* This is used to implement {@link DynamicMessage}, and also to represent
* extensions in {@link GeneratedMessage}. This class is package-private,
* since outside users should probably be using {@link DynamicMessage}.
*
* @author kenton@google.com Kenton Varda
*/
final class FieldSet<FieldDescriptorType extends
FieldSet.FieldDescriptorLite<FieldDescriptorType>> {
/**
* Interface for a FieldDescriptor or lite extension descriptor. This
* prevents FieldSet from depending on {@link Descriptors.FieldDescriptor}.
*/
public interface FieldDescriptorLite<T extends FieldDescriptorLite<T>>
extends Comparable<T> {
int getNumber();
WireFormat.FieldType getLiteType();
WireFormat.JavaType getLiteJavaType();
boolean isRepeated();
boolean isPacked();
Internal.EnumLiteMap<?> getEnumType();
// If getLiteJavaType() == MESSAGE, this merges a message object of the
// type into a builder of the type. Returns {@code to}.
MessageLite.Builder internalMergeFrom(
MessageLite.Builder to, MessageLite from);
}
private Map<FieldDescriptorType, Object> fields;
/** Construct a new FieldSet. */
private FieldSet() {
// Use a TreeMap because fields need to be in canonical order when
// serializing.
// TODO(kenton): Maybe use some sort of sparse array instead? It would
// even make sense to store the first 16 or so tags in a flat array
// to make DynamicMessage faster.
fields = new TreeMap<FieldDescriptorType, Object>();
}
/**
* Construct an empty FieldSet. This is only used to initialize
* DEFAULT_INSTANCE.
*/
private FieldSet(final boolean dummy) {
this.fields = Collections.emptyMap();
}
/** Construct a new FieldSet. */
public static <T extends FieldSet.FieldDescriptorLite<T>>
FieldSet<T> newFieldSet() {
return new FieldSet<T>();
}
/** Get an immutable empty FieldSet. */
@SuppressWarnings("unchecked")
public static <T extends FieldSet.FieldDescriptorLite<T>>
FieldSet<T> emptySet() {
return DEFAULT_INSTANCE;
}
@SuppressWarnings("unchecked")
private static final FieldSet DEFAULT_INSTANCE = new FieldSet(true);
/** Make this FieldSet immutable from this point forward. */
@SuppressWarnings("unchecked")
public void makeImmutable() {
for (final Map.Entry<FieldDescriptorType, Object> entry:
fields.entrySet()) {
if (entry.getKey().isRepeated()) {
final List value = (List)entry.getValue();
fields.put(entry.getKey(), Collections.unmodifiableList(value));
}
}
fields = Collections.unmodifiableMap(fields);
}
// =================================================================
/** See {@link Message.Builder#clear()}. */
public void clear() {
fields.clear();
}
/**
* Get a simple map containing all the fields.
*/
public Map<FieldDescriptorType, Object> getAllFields() {
return Collections.unmodifiableMap(fields);
}
/**
* Get an iterator to the field map. This iterator should not be leaked
* out of the protobuf library as it is not protected from mutation.
*/
public Iterator<Map.Entry<FieldDescriptorType, Object>> iterator() {
return fields.entrySet().iterator();
}
/**
* Useful for implementing
* {@link Message#hasField(Descriptors.FieldDescriptor)}.
*/
public boolean hasField(final FieldDescriptorType descriptor) {
if (descriptor.isRepeated()) {
throw new IllegalArgumentException(
"hasField() can only be called on non-repeated fields.");
}
return fields.get(descriptor) != null;
}
/**
* Useful for implementing
* {@link Message#getField(Descriptors.FieldDescriptor)}. This method
* returns {@code null} if the field is not set; in this case it is up
* to the caller to fetch the field's default value.
*/
public Object getField(final FieldDescriptorType descriptor) {
return fields.get(descriptor);
}
/**
* Useful for implementing
* {@link Message.Builder#setField(Descriptors.FieldDescriptor,Object)}.
*/
@SuppressWarnings("unchecked")
public void setField(final FieldDescriptorType descriptor,
Object value) {
if (descriptor.isRepeated()) {
if (!(value instanceof List)) {
throw new IllegalArgumentException(
"Wrong object type used with protocol message reflection.");
}
// Wrap the contents in a new list so that the caller cannot change
// the list's contents after setting it.
final List newList = new ArrayList();
newList.addAll((List)value);
for (final Object element : newList) {
verifyType(descriptor.getLiteType(), element);
}
value = newList;
} else {
verifyType(descriptor.getLiteType(), value);
}
fields.put(descriptor, value);
}
/**
* Useful for implementing
* {@link Message.Builder#clearField(Descriptors.FieldDescriptor)}.
*/
public void clearField(final FieldDescriptorType descriptor) {
fields.remove(descriptor);
}
/**
* Useful for implementing
* {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}.
*/
public int getRepeatedFieldCount(final FieldDescriptorType descriptor) {
if (!descriptor.isRepeated()) {
throw new IllegalArgumentException(
"getRepeatedField() can only be called on repeated fields.");
}
final Object value = fields.get(descriptor);
if (value == null) {
return 0;
} else {
return ((List<?>) value).size();
}
}
/**
* Useful for implementing
* {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}.
*/
@SuppressWarnings("unchecked")
public Object getRepeatedField(final FieldDescriptorType descriptor,
final int index) {
if (!descriptor.isRepeated()) {
throw new IllegalArgumentException(
"getRepeatedField() can only be called on repeated fields.");
}
final Object value = fields.get(descriptor);
if (value == null) {
throw new IndexOutOfBoundsException();
} else {
return ((List) value).get(index);
}
}
/**
* Useful for implementing
* {@link Message.Builder#setRepeatedField(Descriptors.FieldDescriptor,int,Object)}.
*/
@SuppressWarnings("unchecked")
public void setRepeatedField(final FieldDescriptorType descriptor,
final int index,
final Object value) {
if (!descriptor.isRepeated()) {
throw new IllegalArgumentException(
"getRepeatedField() can only be called on repeated fields.");
}
final Object list = fields.get(descriptor);
if (list == null) {
throw new IndexOutOfBoundsException();
}
verifyType(descriptor.getLiteType(), value);
((List) list).set(index, value);
}
/**
* Useful for implementing
* {@link Message.Builder#addRepeatedField(Descriptors.FieldDescriptor,Object)}.
*/
@SuppressWarnings("unchecked")
public void addRepeatedField(final FieldDescriptorType descriptor,
final Object value) {
if (!descriptor.isRepeated()) {
throw new IllegalArgumentException(
"addRepeatedField() can only be called on repeated fields.");
}
verifyType(descriptor.getLiteType(), value);
final Object existingValue = fields.get(descriptor);
List list;
if (existingValue == null) {
list = new ArrayList();
fields.put(descriptor, list);
} else {
list = (List) existingValue;
}
list.add(value);
}
/**
* Verifies that the given object is of the correct type to be a valid
* value for the given field. (For repeated fields, this checks if the
* object is the right type to be one element of the field.)
*
* @throws IllegalArgumentException The value is not of the right type.
*/
private static void verifyType(final WireFormat.FieldType type,
final Object value) {
if (value == null) {
throw new NullPointerException();
}
boolean isValid = false;
switch (type.getJavaType()) {
case INT: isValid = value instanceof Integer ; break;
case LONG: isValid = value instanceof Long ; break;
case FLOAT: isValid = value instanceof Float ; break;
case DOUBLE: isValid = value instanceof Double ; break;
case BOOLEAN: isValid = value instanceof Boolean ; break;
case STRING: isValid = value instanceof String ; break;
case BYTE_STRING: isValid = value instanceof ByteString; break;
case ENUM:
// TODO(kenton): Caller must do type checking here, I guess.
isValid = value instanceof Internal.EnumLite;
break;
case MESSAGE:
// TODO(kenton): Caller must do type checking here, I guess.
isValid = value instanceof MessageLite;
break;
}
if (!isValid) {
// TODO(kenton): When chaining calls to setField(), it can be hard to
// tell from the stack trace which exact call failed, since the whole
// chain is considered one line of code. It would be nice to print
// more information here, e.g. naming the field. We used to do that.
// But we can't now that FieldSet doesn't use descriptors. Maybe this
// isn't a big deal, though, since it would only really apply when using
// reflection and generally people don't chain reflection setters.
throw new IllegalArgumentException(
"Wrong object type used with protocol message reflection.");
}
}
// =================================================================
// Parsing and serialization
/**
* See {@link Message#isInitialized()}. Note: Since {@code FieldSet}
* itself does not have any way of knowing about required fields that
* aren't actually present in the set, it is up to the caller to check
* that all required fields are present.
*/
@SuppressWarnings("unchecked")
public boolean isInitialized() {
for (final Map.Entry<FieldDescriptorType, Object> entry:
fields.entrySet()) {
final FieldDescriptorType descriptor = entry.getKey();
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
if (descriptor.isRepeated()) {
for (final MessageLite element:
(List<MessageLite>) entry.getValue()) {
if (!element.isInitialized()) {
return false;
}
}
} else {
if (!((MessageLite) entry.getValue()).isInitialized()) {
return false;
}
}
}
}
return true;
}
/**
* Given a field type, return the wire type.
*
* @returns One of the {@code WIRETYPE_} constants defined in
* {@link WireFormat}.
*/
static int getWireFormatForFieldType(final WireFormat.FieldType type,
boolean isPacked) {
if (isPacked) {
return WireFormat.WIRETYPE_LENGTH_DELIMITED;
} else {
return type.getWireType();
}
}
/**
* Like {@link #mergeFrom(Message)}, but merges from another {@link FieldSet}.
*/
@SuppressWarnings("unchecked")
public void mergeFrom(final FieldSet<FieldDescriptorType> other) {
for (final Map.Entry<FieldDescriptorType, Object> entry:
other.fields.entrySet()) {
final FieldDescriptorType descriptor = entry.getKey();
final Object otherValue = entry.getValue();
if (descriptor.isRepeated()) {
Object value = fields.get(descriptor);
if (value == null) {
// Our list is empty, but we still need to make a defensive copy of
// the other list since we don't know if the other FieldSet is still
// mutable.
fields.put(descriptor, new ArrayList((List) otherValue));
} else {
// Concatenate the lists.
((List) value).addAll((List) otherValue);
}
} else if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE) {
Object value = fields.get(descriptor);
if (value == null) {
fields.put(descriptor, otherValue);
} else {
// Merge the messages.
fields.put(descriptor,
descriptor.internalMergeFrom(
((MessageLite) value).toBuilder(), (MessageLite) otherValue)
.build());
}
} else {
fields.put(descriptor, otherValue);
}
}
}
// TODO(kenton): Move static parsing and serialization methods into some
// other class. Probably WireFormat.
/**
* Read a field of any primitive type from a CodedInputStream. Enums,
* groups, and embedded messages are not handled by this method.
*
* @param input The stream from which to read.
* @param type Declared type of the field.
* @return An object representing the field's value, of the exact
* type which would be returned by
* {@link Message#getField(Descriptors.FieldDescriptor)} for
* this field.
*/
public static Object readPrimitiveField(
CodedInputStream input,
final WireFormat.FieldType type) throws IOException {
switch (type) {
case DOUBLE : return input.readDouble ();
case FLOAT : return input.readFloat ();
case INT64 : return input.readInt64 ();
case UINT64 : return input.readUInt64 ();
case INT32 : return input.readInt32 ();
case FIXED64 : return input.readFixed64 ();
case FIXED32 : return input.readFixed32 ();
case BOOL : return input.readBool ();
case STRING : return input.readString ();
case BYTES : return input.readBytes ();
case UINT32 : return input.readUInt32 ();
case SFIXED32: return input.readSFixed32();
case SFIXED64: return input.readSFixed64();
case SINT32 : return input.readSInt32 ();
case SINT64 : return input.readSInt64 ();
case GROUP:
throw new IllegalArgumentException(
"readPrimitiveField() cannot handle nested groups.");
case MESSAGE:
throw new IllegalArgumentException(
"readPrimitiveField() cannot handle embedded messages.");
case ENUM:
// We don't handle enums because we don't know what to do if the
// value is not recognized.
throw new IllegalArgumentException(
"readPrimitiveField() cannot handle enums.");
}
throw new RuntimeException(
"There is no way to get here, but the compiler thinks otherwise.");
}
/** See {@link Message#writeTo(CodedOutputStream)}. */
public void writeTo(final CodedOutputStream output)
throws IOException {
for (final Map.Entry<FieldDescriptorType, Object> entry:
fields.entrySet()) {
writeField(entry.getKey(), entry.getValue(), output);
}
}
/**
* Like {@link #writeTo} but uses MessageSet wire format.
*/
public void writeMessageSetTo(final CodedOutputStream output)
throws IOException {
for (final Map.Entry<FieldDescriptorType, Object> entry:
fields.entrySet()) {
final FieldDescriptorType descriptor = entry.getKey();
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
!descriptor.isRepeated() && !descriptor.isPacked()) {
output.writeMessageSetExtension(entry.getKey().getNumber(),
(MessageLite) entry.getValue());
} else {
writeField(descriptor, entry.getValue(), output);
}
}
}
/**
* Write a single tag-value pair to the stream.
*
* @param output The output stream.
* @param type The field's type.
* @param number The field's number.
* @param value Object representing the field's value. Must be of the exact
* type which would be returned by
* {@link Message#getField(Descriptors.FieldDescriptor)} for
* this field.
*/
private static void writeElement(final CodedOutputStream output,
final WireFormat.FieldType type,
final int number,
final Object value) throws IOException {
// Special case for groups, which need a start and end tag; other fields
// can just use writeTag() and writeFieldNoTag().
if (type == WireFormat.FieldType.GROUP) {
output.writeGroup(number, (MessageLite) value);
} else {
output.writeTag(number, getWireFormatForFieldType(type, false));
writeElementNoTag(output, type, value);
}
}
/**
* Write a field of arbitrary type, without its tag, to the stream.
*
* @param output The output stream.
* @param type The field's type.
* @param value Object representing the field's value. Must be of the exact
* type which would be returned by
* {@link Message#getField(Descriptors.FieldDescriptor)} for
* this field.
*/
private static void writeElementNoTag(
final CodedOutputStream output,
final WireFormat.FieldType type,
final Object value) throws IOException {
switch (type) {
case DOUBLE : output.writeDoubleNoTag ((Double ) value); break;
case FLOAT : output.writeFloatNoTag ((Float ) value); break;
case INT64 : output.writeInt64NoTag ((Long ) value); break;
case UINT64 : output.writeUInt64NoTag ((Long ) value); break;
case INT32 : output.writeInt32NoTag ((Integer ) value); break;
case FIXED64 : output.writeFixed64NoTag ((Long ) value); break;
case FIXED32 : output.writeFixed32NoTag ((Integer ) value); break;
case BOOL : output.writeBoolNoTag ((Boolean ) value); break;
case STRING : output.writeStringNoTag ((String ) value); break;
case GROUP : output.writeGroupNoTag ((MessageLite) value); break;
case MESSAGE : output.writeMessageNoTag ((MessageLite) value); break;
case BYTES : output.writeBytesNoTag ((ByteString ) value); break;
case UINT32 : output.writeUInt32NoTag ((Integer ) value); break;
case SFIXED32: output.writeSFixed32NoTag((Integer ) value); break;
case SFIXED64: output.writeSFixed64NoTag((Long ) value); break;
case SINT32 : output.writeSInt32NoTag ((Integer ) value); break;
case SINT64 : output.writeSInt64NoTag ((Long ) value); break;
case ENUM:
output.writeEnumNoTag(((Internal.EnumLite) value).getNumber());
break;
}
}
/** Write a single field. */
@SuppressWarnings("unchecked")
public static void writeField(final FieldDescriptorLite<?> descriptor,
final Object value,
final CodedOutputStream output)
throws IOException {
WireFormat.FieldType type = descriptor.getLiteType();
int number = descriptor.getNumber();
if (descriptor.isRepeated()) {
final List valueList = (List)value;
if (descriptor.isPacked()) {
output.writeTag(number, WireFormat.WIRETYPE_LENGTH_DELIMITED);
// Compute the total data size so the length can be written.
int dataSize = 0;
for (final Object element : valueList) {
dataSize += computeElementSizeNoTag(type, element);
}
output.writeRawVarint32(dataSize);
// Write the data itself, without any tags.
for (final Object element : valueList) {
writeElementNoTag(output, type, element);
}
} else {
for (final Object element : valueList) {
writeElement(output, type, number, element);
}
}
} else {
writeElement(output, type, number, value);
}
}
/**
* See {@link Message#getSerializedSize()}. It's up to the caller to cache
* the resulting size if desired.
*/
public int getSerializedSize() {
int size = 0;
for (final Map.Entry<FieldDescriptorType, Object> entry:
fields.entrySet()) {
size += computeFieldSize(entry.getKey(), entry.getValue());
}
return size;
}
/**
* Like {@link #getSerializedSize} but uses MessageSet wire format.
*/
public int getMessageSetSerializedSize() {
int size = 0;
for (final Map.Entry<FieldDescriptorType, Object> entry:
fields.entrySet()) {
final FieldDescriptorType descriptor = entry.getKey();
if (descriptor.getLiteJavaType() == WireFormat.JavaType.MESSAGE &&
!descriptor.isRepeated() && !descriptor.isPacked()) {
size += CodedOutputStream.computeMessageSetExtensionSize(
entry.getKey().getNumber(), (MessageLite) entry.getValue());
} else {
size += computeFieldSize(descriptor, entry.getValue());
}
}
return size;
}
/**
* Compute the number of bytes that would be needed to encode a
* single tag/value pair of arbitrary type.
*
* @param type The field's type.
* @param number The field's number.
* @param value Object representing the field's value. Must be of the exact
* type which would be returned by
* {@link Message#getField(Descriptors.FieldDescriptor)} for
* this field.
*/
private static int computeElementSize(
final WireFormat.FieldType type,
final int number, final Object value) {
int tagSize = CodedOutputStream.computeTagSize(number);
if (type == WireFormat.FieldType.GROUP) {
tagSize *= 2;
}
return tagSize + computeElementSizeNoTag(type, value);
}
/**
* Compute the number of bytes that would be needed to encode a
* particular value of arbitrary type, excluding tag.
*
* @param type The field's type.
* @param value Object representing the field's value. Must be of the exact
* type which would be returned by
* {@link Message#getField(Descriptors.FieldDescriptor)} for
* this field.
*/
private static int computeElementSizeNoTag(
final WireFormat.FieldType type, final Object value) {
switch (type) {
// Note: Minor violation of 80-char limit rule here because this would
// actually be harder to read if we wrapped the lines.
case DOUBLE : return CodedOutputStream.computeDoubleSizeNoTag ((Double )value);
case FLOAT : return CodedOutputStream.computeFloatSizeNoTag ((Float )value);
case INT64 : return CodedOutputStream.computeInt64SizeNoTag ((Long )value);
case UINT64 : return CodedOutputStream.computeUInt64SizeNoTag ((Long )value);
case INT32 : return CodedOutputStream.computeInt32SizeNoTag ((Integer )value);
case FIXED64 : return CodedOutputStream.computeFixed64SizeNoTag ((Long )value);
case FIXED32 : return CodedOutputStream.computeFixed32SizeNoTag ((Integer )value);
case BOOL : return CodedOutputStream.computeBoolSizeNoTag ((Boolean )value);
case STRING : return CodedOutputStream.computeStringSizeNoTag ((String )value);
case GROUP : return CodedOutputStream.computeGroupSizeNoTag ((MessageLite)value);
case MESSAGE : return CodedOutputStream.computeMessageSizeNoTag ((MessageLite)value);
case BYTES : return CodedOutputStream.computeBytesSizeNoTag ((ByteString )value);
case UINT32 : return CodedOutputStream.computeUInt32SizeNoTag ((Integer )value);
case SFIXED32: return CodedOutputStream.computeSFixed32SizeNoTag((Integer )value);
case SFIXED64: return CodedOutputStream.computeSFixed64SizeNoTag((Long )value);
case SINT32 : return CodedOutputStream.computeSInt32SizeNoTag ((Integer )value);
case SINT64 : return CodedOutputStream.computeSInt64SizeNoTag ((Long )value);
case ENUM:
return CodedOutputStream.computeEnumSizeNoTag(
((Internal.EnumLite) value).getNumber());
}
throw new RuntimeException(
"There is no way to get here, but the compiler thinks otherwise.");
}
/**
* Compute the number of bytes needed to encode a particular field.
*/
@SuppressWarnings("unchecked")
public static int computeFieldSize(final FieldDescriptorLite<?> descriptor,
final Object value) {
WireFormat.FieldType type = descriptor.getLiteType();
int number = descriptor.getNumber();
if (descriptor.isRepeated()) {
if (descriptor.isPacked()) {
int dataSize = 0;
for (final Object element : (List)value) {
dataSize += computeElementSizeNoTag(type, element);
}
return dataSize +
CodedOutputStream.computeTagSize(number) +
CodedOutputStream.computeRawVarint32Size(dataSize);
} else {
int size = 0;
for (final Object element : (List)value) {
size += computeElementSize(type, number, element);
}
return size;
}
} else {
return computeElementSize(type, number, value);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,576 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import java.io.IOException;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
/**
* Lite version of {@link GeneratedMessage}.
*
* @author kenton@google.com Kenton Varda
*/
public abstract class GeneratedMessageLite extends AbstractMessageLite {
protected GeneratedMessageLite() {}
@SuppressWarnings("unchecked")
public abstract static class Builder<MessageType extends GeneratedMessageLite,
BuilderType extends Builder>
extends AbstractMessageLite.Builder<BuilderType> {
protected Builder() {}
// This is implemented here only to work around an apparent bug in the
// Java compiler and/or build system. See bug #1898463. The mere presence
// of this dummy clone() implementation makes it go away.
@Override
public BuilderType clone() {
throw new UnsupportedOperationException(
"This is supposed to be overridden by subclasses.");
}
/** All subclasses implement this. */
public abstract BuilderType mergeFrom(MessageType message);
// Defined here for return type covariance.
@Override
public abstract MessageType getDefaultInstanceForType();
/**
* Get the message being built. We don't just pass this to the
* constructor because it becomes null when build() is called.
*/
protected abstract MessageType internalGetResult();
/**
* Called by subclasses to parse an unknown field.
* @return {@code true} unless the tag is an end-group tag.
*/
protected boolean parseUnknownField(
final CodedInputStream input,
final ExtensionRegistryLite extensionRegistry,
final int tag) throws IOException {
return input.skipField(tag);
}
}
// =================================================================
// Extensions-related stuff
/**
* Lite equivalent of {@link GeneratedMessage.ExtendableMessage}.
*/
public abstract static class ExtendableMessage<
MessageType extends ExtendableMessage<MessageType>>
extends GeneratedMessageLite {
protected ExtendableMessage() {}
private final FieldSet<ExtensionDescriptor> extensions =
FieldSet.newFieldSet();
private void verifyExtensionContainingType(
final GeneratedExtension<MessageType, ?> extension) {
if (extension.getContainingTypeDefaultInstance() !=
getDefaultInstanceForType()) {
// This can only happen if someone uses unchecked operations.
throw new IllegalArgumentException(
"This extension is for a different message type. Please make " +
"sure that you are not suppressing any generics type warnings.");
}
}
/** Check if a singular extension is present. */
public final boolean hasExtension(
final GeneratedExtension<MessageType, ?> extension) {
verifyExtensionContainingType(extension);
return extensions.hasField(extension.descriptor);
}
/** Get the number of elements in a repeated extension. */
public final <Type> int getExtensionCount(
final GeneratedExtension<MessageType, List<Type>> extension) {
verifyExtensionContainingType(extension);
return extensions.getRepeatedFieldCount(extension.descriptor);
}
/** Get the value of an extension. */
@SuppressWarnings("unchecked")
public final <Type> Type getExtension(
final GeneratedExtension<MessageType, Type> extension) {
verifyExtensionContainingType(extension);
final Object value = extensions.getField(extension.descriptor);
if (value == null) {
return extension.defaultValue;
} else {
return (Type) value;
}
}
/** Get one element of a repeated extension. */
@SuppressWarnings("unchecked")
public final <Type> Type getExtension(
final GeneratedExtension<MessageType, List<Type>> extension,
final int index) {
verifyExtensionContainingType(extension);
return (Type) extensions.getRepeatedField(extension.descriptor, index);
}
/** Called by subclasses to check if all extensions are initialized. */
protected boolean extensionsAreInitialized() {
return extensions.isInitialized();
}
/**
* Used by subclasses to serialize extensions. Extension ranges may be
* interleaved with field numbers, but we must write them in canonical
* (sorted by field number) order. ExtensionWriter helps us write
* individual ranges of extensions at once.
*/
protected class ExtensionWriter {
// Imagine how much simpler this code would be if Java iterators had
// a way to get the next element without advancing the iterator.
private final Iterator<Map.Entry<ExtensionDescriptor, Object>> iter =
extensions.iterator();
private Map.Entry<ExtensionDescriptor, Object> next;
private final boolean messageSetWireFormat;
private ExtensionWriter(boolean messageSetWireFormat) {
if (iter.hasNext()) {
next = iter.next();
}
this.messageSetWireFormat = messageSetWireFormat;
}
public void writeUntil(final int end, final CodedOutputStream output)
throws IOException {
while (next != null && next.getKey().getNumber() < end) {
ExtensionDescriptor extension = next.getKey();
if (messageSetWireFormat && extension.getLiteJavaType() ==
WireFormat.JavaType.MESSAGE &&
!extension.isRepeated()) {
output.writeMessageSetExtension(extension.getNumber(),
(MessageLite) next.getValue());
} else {
FieldSet.writeField(extension, next.getValue(), output);
}
if (iter.hasNext()) {
next = iter.next();
} else {
next = null;
}
}
}
}
protected ExtensionWriter newExtensionWriter() {
return new ExtensionWriter(false);
}
protected ExtensionWriter newMessageSetExtensionWriter() {
return new ExtensionWriter(true);
}
/** Called by subclasses to compute the size of extensions. */
protected int extensionsSerializedSize() {
return extensions.getSerializedSize();
}
protected int extensionsSerializedSizeAsMessageSet() {
return extensions.getMessageSetSerializedSize();
}
}
/**
* Lite equivalent of {@link GeneratedMessage.ExtendableBuilder}.
*/
@SuppressWarnings("unchecked")
public abstract static class ExtendableBuilder<
MessageType extends ExtendableMessage<MessageType>,
BuilderType extends ExtendableBuilder<MessageType, BuilderType>>
extends Builder<MessageType, BuilderType> {
protected ExtendableBuilder() {}
// This is implemented here only to work around an apparent bug in the
// Java compiler and/or build system. See bug #1898463. The mere presence
// of this dummy clone() implementation makes it go away.
@Override
public BuilderType clone() {
throw new UnsupportedOperationException(
"This is supposed to be overridden by subclasses.");
}
@Override
protected abstract MessageType internalGetResult();
/** Check if a singular extension is present. */
public final boolean hasExtension(
final GeneratedExtension<MessageType, ?> extension) {
return internalGetResult().hasExtension(extension);
}
/** Get the number of elements in a repeated extension. */
public final <Type> int getExtensionCount(
final GeneratedExtension<MessageType, List<Type>> extension) {
return internalGetResult().getExtensionCount(extension);
}
/** Get the value of an extension. */
public final <Type> Type getExtension(
final GeneratedExtension<MessageType, Type> extension) {
return internalGetResult().getExtension(extension);
}
/** Get one element of a repeated extension. */
public final <Type> Type getExtension(
final GeneratedExtension<MessageType, List<Type>> extension,
final int index) {
return internalGetResult().getExtension(extension, index);
}
/** Set the value of an extension. */
public final <Type> BuilderType setExtension(
final GeneratedExtension<MessageType, Type> extension,
final Type value) {
final ExtendableMessage<MessageType> message = internalGetResult();
message.verifyExtensionContainingType(extension);
message.extensions.setField(extension.descriptor, value);
return (BuilderType) this;
}
/** Set the value of one element of a repeated extension. */
public final <Type> BuilderType setExtension(
final GeneratedExtension<MessageType, List<Type>> extension,
final int index, final Type value) {
final ExtendableMessage<MessageType> message = internalGetResult();
message.verifyExtensionContainingType(extension);
message.extensions.setRepeatedField(extension.descriptor, index, value);
return (BuilderType) this;
}
/** Append a value to a repeated extension. */
public final <Type> BuilderType addExtension(
final GeneratedExtension<MessageType, List<Type>> extension,
final Type value) {
final ExtendableMessage<MessageType> message = internalGetResult();
message.verifyExtensionContainingType(extension);
message.extensions.addRepeatedField(extension.descriptor, value);
return (BuilderType) this;
}
/** Clear an extension. */
public final <Type> BuilderType clearExtension(
final GeneratedExtension<MessageType, ?> extension) {
final ExtendableMessage<MessageType> message = internalGetResult();
message.verifyExtensionContainingType(extension);
message.extensions.clearField(extension.descriptor);
return (BuilderType) this;
}
/**
* Called by subclasses to parse an unknown field or an extension.
* @return {@code true} unless the tag is an end-group tag.
*/
@Override
protected boolean parseUnknownField(
final CodedInputStream input,
final ExtensionRegistryLite extensionRegistry,
final int tag) throws IOException {
final FieldSet<ExtensionDescriptor> extensions =
((ExtendableMessage) internalGetResult()).extensions;
final int wireType = WireFormat.getTagWireType(tag);
final int fieldNumber = WireFormat.getTagFieldNumber(tag);
final GeneratedExtension<MessageType, ?> extension =
extensionRegistry.findLiteExtensionByNumber(
getDefaultInstanceForType(), fieldNumber);
boolean unknown = false;
boolean packed = false;
if (extension == null) {
unknown = true; // Unknown field.
} else if (wireType == FieldSet.getWireFormatForFieldType(
extension.descriptor.getLiteType(),
false /* isPacked */)) {
packed = false; // Normal, unpacked value.
} else if (extension.descriptor.isRepeated &&
extension.descriptor.type.isPackable() &&
wireType == FieldSet.getWireFormatForFieldType(
extension.descriptor.getLiteType(),
true /* isPacked */)) {
packed = true; // Packed value.
} else {
unknown = true; // Wrong wire type.
}
if (unknown) { // Unknown field or wrong wire type. Skip.
return input.skipField(tag);
}
if (packed) {
final int length = input.readRawVarint32();
final int limit = input.pushLimit(length);
if (extension.descriptor.getLiteType() == WireFormat.FieldType.ENUM) {
while (input.getBytesUntilLimit() > 0) {
final int rawValue = input.readEnum();
final Object value =
extension.descriptor.getEnumType().findValueByNumber(rawValue);
if (value == null) {
// If the number isn't recognized as a valid value for this
// enum, drop it (don't even add it to unknownFields).
return true;
}
extensions.addRepeatedField(extension.descriptor, value);
}
} else {
while (input.getBytesUntilLimit() > 0) {
final Object value =
FieldSet.readPrimitiveField(input,
extension.descriptor.getLiteType());
extensions.addRepeatedField(extension.descriptor, value);
}
}
input.popLimit(limit);
} else {
final Object value;
switch (extension.descriptor.getLiteJavaType()) {
case MESSAGE: {
MessageLite.Builder subBuilder = null;
if (!extension.descriptor.isRepeated()) {
MessageLite existingValue =
(MessageLite) extensions.getField(extension.descriptor);
if (existingValue != null) {
subBuilder = existingValue.toBuilder();
}
}
if (subBuilder == null) {
subBuilder = extension.messageDefaultInstance.newBuilderForType();
}
if (extension.descriptor.getLiteType() ==
WireFormat.FieldType.GROUP) {
input.readGroup(extension.getNumber(),
subBuilder, extensionRegistry);
} else {
input.readMessage(subBuilder, extensionRegistry);
}
value = subBuilder.build();
break;
}
case ENUM:
final int rawValue = input.readEnum();
value = extension.descriptor.getEnumType()
.findValueByNumber(rawValue);
// If the number isn't recognized as a valid value for this enum,
// drop it.
if (value == null) {
return true;
}
break;
default:
value = FieldSet.readPrimitiveField(input,
extension.descriptor.getLiteType());
break;
}
if (extension.descriptor.isRepeated()) {
extensions.addRepeatedField(extension.descriptor, value);
} else {
extensions.setField(extension.descriptor, value);
}
}
return true;
}
protected final void mergeExtensionFields(final MessageType other) {
((ExtendableMessage) internalGetResult()).extensions.mergeFrom(
((ExtendableMessage) other).extensions);
}
}
// -----------------------------------------------------------------
/** For use by generated code only. */
public static <ContainingType extends MessageLite, Type>
GeneratedExtension<ContainingType, Type>
newGeneratedExtension() {
return new GeneratedExtension<ContainingType, Type>();
}
private static final class ExtensionDescriptor
implements FieldSet.FieldDescriptorLite<
ExtensionDescriptor> {
private ExtensionDescriptor(
final Internal.EnumLiteMap<?> enumTypeMap,
final int number,
final WireFormat.FieldType type,
final boolean isRepeated,
final boolean isPacked) {
this.enumTypeMap = enumTypeMap;
this.number = number;
this.type = type;
this.isRepeated = isRepeated;
this.isPacked = isPacked;
}
private final Internal.EnumLiteMap<?> enumTypeMap;
private final int number;
private final WireFormat.FieldType type;
private final boolean isRepeated;
private final boolean isPacked;
@Override
public int getNumber() {
return number;
}
@Override
public WireFormat.FieldType getLiteType() {
return type;
}
@Override
public WireFormat.JavaType getLiteJavaType() {
return type.getJavaType();
}
@Override
public boolean isRepeated() {
return isRepeated;
}
@Override
public boolean isPacked() {
return isPacked;
}
@Override
public Internal.EnumLiteMap<?> getEnumType() {
return enumTypeMap;
}
@Override
@SuppressWarnings("unchecked")
public MessageLite.Builder internalMergeFrom(
MessageLite.Builder to, MessageLite from) {
return ((Builder) to).mergeFrom((GeneratedMessageLite) from);
}
@Override
public int compareTo(ExtensionDescriptor other) {
return number - other.number;
}
}
/**
* Lite equivalent to {@link GeneratedMessage.GeneratedExtension}.
*
* Users should ignore the contents of this class and only use objects of
* this type as parameters to extension accessors and ExtensionRegistry.add().
*/
public static final class GeneratedExtension<
ContainingType extends MessageLite, Type> {
// We can't always initialize a GeneratedExtension when we first construct
// it due to initialization order difficulties (namely, the default
// instances may not have been constructed yet). So, we construct an
// uninitialized GeneratedExtension once, then call internalInit() on it
// later. Generated code will always call internalInit() on all extensions
// as part of the static initialization code, and internalInit() throws an
// exception if called more than once, so this method is useless to users.
private GeneratedExtension() {}
private void internalInit(
final ContainingType containingTypeDefaultInstance,
final Type defaultValue,
final MessageLite messageDefaultInstance,
final ExtensionDescriptor descriptor) {
this.containingTypeDefaultInstance = containingTypeDefaultInstance;
this.defaultValue = defaultValue;
this.messageDefaultInstance = messageDefaultInstance;
this.descriptor = descriptor;
}
/** For use by generated code only. */
public void internalInitSingular(
final ContainingType containingTypeDefaultInstance,
final Type defaultValue,
final MessageLite messageDefaultInstance,
final Internal.EnumLiteMap<?> enumTypeMap,
final int number,
final WireFormat.FieldType type) {
internalInit(
containingTypeDefaultInstance, defaultValue, messageDefaultInstance,
new ExtensionDescriptor(enumTypeMap, number, type,
false /* isRepeated */, false /* isPacked */));
}
/** For use by generated code only. */
@SuppressWarnings("unchecked")
public void internalInitRepeated(
final ContainingType containingTypeDefaultInstance,
final MessageLite messageDefaultInstance,
final Internal.EnumLiteMap<?> enumTypeMap,
final int number,
final WireFormat.FieldType type,
final boolean isPacked) {
internalInit(
containingTypeDefaultInstance, (Type) Collections.emptyList(),
messageDefaultInstance,
new ExtensionDescriptor(
enumTypeMap, number, type, true /* isRepeated */, isPacked));
}
private ContainingType containingTypeDefaultInstance;
private Type defaultValue;
private MessageLite messageDefaultInstance;
private ExtensionDescriptor descriptor;
/**
* Default instance of the type being extended, used to identify that type.
*/
public ContainingType getContainingTypeDefaultInstance() {
return containingTypeDefaultInstance;
}
/** Get the field number. */
public int getNumber() {
return descriptor.getNumber();
}
/**
* If the extension is an embedded message, this is the default instance of
* that type.
*/
public MessageLite getMessageDefaultInstance() {
return messageDefaultInstance;
}
}
}

View file

@ -0,0 +1,121 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import java.io.UnsupportedEncodingException;
/**
* The classes contained within are used internally by the Protocol Buffer
* library and generated message implementations. They are public only because
* those generated messages do not reside in the {@code protobuf} package.
* Others should not use this class directly.
*
* @author kenton@google.com (Kenton Varda)
*/
public class Internal {
/**
* Helper called by generated code to construct default values for string
* fields.
* <p>
* The protocol compiler does not actually contain a UTF-8 decoder -- it
* just pushes UTF-8-encoded text around without touching it. The one place
* where this presents a problem is when generating Java string literals.
* Unicode characters in the string literal would normally need to be encoded
* using a Unicode escape sequence, which would require decoding them.
* To get around this, protoc instead embeds the UTF-8 bytes into the
* generated code and leaves it to the runtime library to decode them.
* <p>
* It gets worse, though. If protoc just generated a byte array, like:
* new byte[] {0x12, 0x34, 0x56, 0x78}
* Java actually generates *code* which allocates an array and then fills
* in each value. This is much less efficient than just embedding the bytes
* directly into the bytecode. To get around this, we need another
* work-around. String literals are embedded directly, so protoc actually
* generates a string literal corresponding to the bytes. The easiest way
* to do this is to use the ISO-8859-1 character set, which corresponds to
* the first 256 characters of the Unicode range. Protoc can then use
* good old CEscape to generate the string.
* <p>
* So we have a string literal which represents a set of bytes which
* represents another string. This function -- stringDefaultValue --
* converts from the generated string to the string we actually want. The
* generated code calls this automatically.
*/
public static String stringDefaultValue(String bytes) {
try {
return new String(bytes.getBytes("ISO-8859-1"), "UTF-8");
} catch (UnsupportedEncodingException e) {
// This should never happen since all JVMs are required to implement
// both of the above character sets.
throw new IllegalStateException(
"Java VM does not support a standard character set.", e);
}
}
/**
* Helper called by generated code to construct default values for bytes
* fields.
* <p>
* This is a lot like {@link #stringDefaultValue}, but for bytes fields.
* In this case we only need the second of the two hacks -- allowing us to
* embed raw bytes as a string literal with ISO-8859-1 encoding.
*/
public static ByteString bytesDefaultValue(String bytes) {
try {
return ByteString.copyFrom(bytes.getBytes("ISO-8859-1"));
} catch (UnsupportedEncodingException e) {
// This should never happen since all JVMs are required to implement
// ISO-8859-1.
throw new IllegalStateException(
"Java VM does not support a standard character set.", e);
}
}
/**
* Interface for an enum value or value descriptor, to be used in FieldSet.
* The lite library stores enum values directly in FieldSets but the full
* library stores EnumValueDescriptors in order to better support reflection.
*/
public interface EnumLite {
int getNumber();
}
/**
* Interface for an object which maps integers to {@link EnumLite}s.
* {@link Descriptors.EnumDescriptor} implements this interface by mapping
* numbers to {@link Descriptors.EnumValueDescriptor}s. Additionally,
* every generated enum type has a static method internalGetValueMap() which
* returns an implementation of this type that maps numbers to enum values.
*/
public interface EnumLiteMap<T extends EnumLite> {
T findValueByNumber(int number);
}
}

View file

@ -0,0 +1,93 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import java.io.IOException;
/**
* Thrown when a protocol message being parsed is invalid in some way,
* e.g. it contains a malformed varint or a negative byte length.
*
* @author kenton@google.com Kenton Varda
*/
public class InvalidProtocolBufferException extends IOException {
private static final long serialVersionUID = -1616151763072450476L;
public InvalidProtocolBufferException(final String description) {
super(description);
}
static InvalidProtocolBufferException truncatedMessage() {
return new InvalidProtocolBufferException(
"While parsing a protocol message, the input ended unexpectedly " +
"in the middle of a field. This could mean either than the " +
"input has been truncated or that an embedded message " +
"misreported its own length.");
}
static InvalidProtocolBufferException negativeSize() {
return new InvalidProtocolBufferException(
"CodedInputStream encountered an embedded string or message " +
"which claimed to have negative size.");
}
static InvalidProtocolBufferException malformedVarint() {
return new InvalidProtocolBufferException(
"CodedInputStream encountered a malformed varint.");
}
static InvalidProtocolBufferException invalidTag() {
return new InvalidProtocolBufferException(
"Protocol message contained an invalid tag (zero).");
}
static InvalidProtocolBufferException invalidEndTag() {
return new InvalidProtocolBufferException(
"Protocol message end-group tag did not match expected tag.");
}
static InvalidProtocolBufferException invalidWireType() {
return new InvalidProtocolBufferException(
"Protocol message tag had invalid wire type.");
}
static InvalidProtocolBufferException recursionLimitExceeded() {
return new InvalidProtocolBufferException(
"Protocol message had too many levels of nesting. May be malicious. " +
"Use CodedInputStream.setRecursionLimit() to increase the depth limit.");
}
static InvalidProtocolBufferException sizeLimitExceeded() {
return new InvalidProtocolBufferException(
"Protocol message was too large. May be malicious. " +
"Use CodedInputStream.setSizeLimit() to increase the size limit.");
}
}

View file

@ -0,0 +1,325 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// TODO(kenton): Use generics? E.g. Builder<BuilderType extends Builder>, then
// mergeFrom*() could return BuilderType for better type-safety.
package com.google.protobuf;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
/**
* Abstract interface implemented by Protocol Message objects.
* <p>
* See also {@link MessageLite}, which defines most of the methods that typical
* users care about. {@link Message} adds to it methods that are not available
* in the "lite" runtime. The biggest added features are introspection and
* reflection -- i.e., getting descriptors for the message type and accessing
* the field values dynamically.
*
* @author kenton@google.com Kenton Varda
*/
public interface Message extends MessageLite {
/**
* Get the message's type's descriptor. This differs from the
* {@code getDescriptor()} method of generated message classes in that
* this method is an abstract method of the {@code Message} interface
* whereas {@code getDescriptor()} is a static method of a specific class.
* They return the same thing.
*/
Descriptors.Descriptor getDescriptorForType();
// (From MessageLite, re-declared here only for return type covariance.)
@Override
Message getDefaultInstanceForType();
/**
* Returns a collection of all the fields in this message which are set
* and their corresponding values. A singular ("required" or "optional")
* field is set iff hasField() returns true for that field. A "repeated"
* field is set iff getRepeatedFieldSize() is greater than zero. The
* values are exactly what would be returned by calling
* {@link #getField(Descriptors.FieldDescriptor)} for each field. The map
* is guaranteed to be a sorted map, so iterating over it will return fields
* in order by field number.
*/
Map<Descriptors.FieldDescriptor, Object> getAllFields();
/**
* Returns true if the given field is set. This is exactly equivalent to
* calling the generated "has" accessor method corresponding to the field.
* @throws IllegalArgumentException The field is a repeated field, or
* {@code field.getContainingType() != getDescriptorForType()}.
*/
boolean hasField(Descriptors.FieldDescriptor field);
/**
* Obtains the value of the given field, or the default value if it is
* not set. For primitive fields, the boxed primitive value is returned.
* For enum fields, the EnumValueDescriptor for the value is returend. For
* embedded message fields, the sub-message is returned. For repeated
* fields, a java.util.List is returned.
*/
Object getField(Descriptors.FieldDescriptor field);
/**
* Gets the number of elements of a repeated field. This is exactly
* equivalent to calling the generated "Count" accessor method corresponding
* to the field.
* @throws IllegalArgumentException The field is not a repeated field, or
* {@code field.getContainingType() != getDescriptorForType()}.
*/
int getRepeatedFieldCount(Descriptors.FieldDescriptor field);
/**
* Gets an element of a repeated field. For primitive fields, the boxed
* primitive value is returned. For enum fields, the EnumValueDescriptor
* for the value is returend. For embedded message fields, the sub-message
* is returned.
* @throws IllegalArgumentException The field is not a repeated field, or
* {@code field.getContainingType() != getDescriptorForType()}.
*/
Object getRepeatedField(Descriptors.FieldDescriptor field, int index);
/** Get the {@link UnknownFieldSet} for this message. */
UnknownFieldSet getUnknownFields();
// -----------------------------------------------------------------
// Comparison and hashing
/**
* Compares the specified object with this message for equality. Returns
* <tt>true</tt> if the given object is a message of the same type (as
* defined by {@code getDescriptorForType()}) and has identical values for
* all of its fields.
*
* @param other object to be compared for equality with this message
* @return <tt>true</tt> if the specified object is equal to this message
*/
@Override
boolean equals(Object other);
/**
* Returns the hash code value for this message. The hash code of a message
* is defined to be <tt>getDescriptor().hashCode() ^ map.hashCode()</tt>,
* where <tt>map</tt> is a map of field numbers to field values.
*
* @return the hash code value for this message
* @see Map#hashCode()
*/
@Override
int hashCode();
// -----------------------------------------------------------------
// Convenience methods.
/**
* Converts the message to a string in protocol buffer text format. This is
* just a trivial wrapper around {@link TextFormat#printToString(Message)}.
*/
@Override
String toString();
// =================================================================
// Builders
// (From MessageLite, re-declared here only for return type covariance.)
@Override
Builder newBuilderForType();
@Override
Builder toBuilder();
/**
* Abstract interface implemented by Protocol Message builders.
*/
interface Builder extends MessageLite.Builder {
// (From MessageLite.Builder, re-declared here only for return type
// covariance.)
@Override
Builder clear();
/**
* Merge {@code other} into the message being built. {@code other} must
* have the exact same type as {@code this} (i.e.
* {@code getDescriptorForType() == other.getDescriptorForType()}).
*
* Merging occurs as follows. For each field:<br>
* * For singular primitive fields, if the field is set in {@code other},
* then {@code other}'s value overwrites the value in this message.<br>
* * For singular message fields, if the field is set in {@code other},
* it is merged into the corresponding sub-message of this message
* using the same merging rules.<br>
* * For repeated fields, the elements in {@code other} are concatenated
* with the elements in this message.
*
* This is equivalent to the {@code Message::MergeFrom} method in C++.
*/
Builder mergeFrom(Message other);
// (From MessageLite.Builder, re-declared here only for return type
// covariance.)
@Override
Message build();
@Override
Message buildPartial();
@Override
Builder clone();
@Override
Builder mergeFrom(CodedInputStream input) throws IOException;
@Override
Builder mergeFrom(CodedInputStream input,
ExtensionRegistryLite extensionRegistry)
throws IOException;
/**
* Get the message's type's descriptor.
* See {@link Message#getDescriptorForType()}.
*/
Descriptors.Descriptor getDescriptorForType();
// (From MessageLite.Builder, re-declared here only for return type
// covariance.)
@Override
Message getDefaultInstanceForType();
/**
* Like {@link Message#getAllFields()}. The returned map may or may not
* reflect future changes to the builder. Either way, the returned map is
* itself unmodifiable.
*/
Map<Descriptors.FieldDescriptor, Object> getAllFields();
/**
* Create a Builder for messages of the appropriate type for the given
* field. Messages built with this can then be passed to setField(),
* setRepeatedField(), or addRepeatedField().
*/
Builder newBuilderForField(Descriptors.FieldDescriptor field);
/** Like {@link Message#hasField(Descriptors.FieldDescriptor)} */
boolean hasField(Descriptors.FieldDescriptor field);
/** Like {@link Message#getField(Descriptors.FieldDescriptor)} */
Object getField(Descriptors.FieldDescriptor field);
/**
* Sets a field to the given value. The value must be of the correct type
* for this field, i.e. the same type that
* {@link Message#getField(Descriptors.FieldDescriptor)} would return.
*/
Builder setField(Descriptors.FieldDescriptor field, Object value);
/**
* Clears the field. This is exactly equivalent to calling the generated
* "clear" accessor method corresponding to the field.
*/
Builder clearField(Descriptors.FieldDescriptor field);
/**
* Like {@link Message#getRepeatedFieldCount(Descriptors.FieldDescriptor)}
*/
int getRepeatedFieldCount(Descriptors.FieldDescriptor field);
/**
* Like {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)}
*/
Object getRepeatedField(Descriptors.FieldDescriptor field, int index);
/**
* Sets an element of a repeated field to the given value. The value must
* be of the correct type for this field, i.e. the same type that
* {@link Message#getRepeatedField(Descriptors.FieldDescriptor,int)} would
* return.
* @throws IllegalArgumentException The field is not a repeated field, or
* {@code field.getContainingType() != getDescriptorForType()}.
*/
Builder setRepeatedField(Descriptors.FieldDescriptor field,
int index, Object value);
/**
* Like {@code setRepeatedField}, but appends the value as a new element.
* @throws IllegalArgumentException The field is not a repeated field, or
* {@code field.getContainingType() != getDescriptorForType()}.
*/
Builder addRepeatedField(Descriptors.FieldDescriptor field, Object value);
/** Get the {@link UnknownFieldSet} for this message. */
UnknownFieldSet getUnknownFields();
/** Set the {@link UnknownFieldSet} for this message. */
Builder setUnknownFields(UnknownFieldSet unknownFields);
/**
* Merge some unknown fields into the {@link UnknownFieldSet} for this
* message.
*/
Builder mergeUnknownFields(UnknownFieldSet unknownFields);
// ---------------------------------------------------------------
// Convenience methods.
// (From MessageLite.Builder, re-declared here only for return type
// covariance.)
@Override
Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException;
@Override
Builder mergeFrom(ByteString data,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException;
@Override
Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException;
@Override
Builder mergeFrom(byte[] data, int off, int len)
throws InvalidProtocolBufferException;
@Override
Builder mergeFrom(byte[] data,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException;
@Override
Builder mergeFrom(byte[] data, int off, int len,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException;
@Override
Builder mergeFrom(InputStream input) throws IOException;
@Override
Builder mergeFrom(InputStream input,
ExtensionRegistryLite extensionRegistry)
throws IOException;
@Override
boolean mergeDelimitedFrom(InputStream input)
throws IOException;
@Override
boolean mergeDelimitedFrom(InputStream input,
ExtensionRegistryLite extensionRegistry)
throws IOException;
}
}

View file

@ -0,0 +1,335 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// TODO(kenton): Use generics? E.g. Builder<BuilderType extends Builder>, then
// mergeFrom*() could return BuilderType for better type-safety.
package com.google.protobuf;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Abstract interface implemented by Protocol Message objects.
*
* <p>This interface is implemented by all protocol message objects. Non-lite
* messages additionally implement the Message interface, which is a subclass
* of MessageLite. Use MessageLite instead when you only need the subset of
* features which it supports -- namely, nothing that uses descriptors or
* reflection. You can instruct the protocol compiler to generate classes
* which implement only MessageLite, not the full Message interface, by adding
* the follow line to the .proto file:
* <pre>
* option optimize_for = LITE_RUNTIME;
* </pre>
*
* <p>This is particularly useful on resource-constrained systems where the
* full protocol buffers runtime library is too big.
*
* <p>Note that on non-constrained systems (e.g. servers) when you need to link
* in lots of protocol definitions, a better way to reduce total code footprint
* is to use {@code optimize_for = CODE_SIZE}. This will make the generated
* code smaller while still supporting all the same features (at the expense of
* speed). {@code optimize_for = LITE_RUNTIME} is best when you only have a
* small number of message types linked into your binary, in which case the
* size of the protocol buffers runtime itself is the biggest problem.
*
* @author kenton@google.com Kenton Varda
*/
public interface MessageLite {
/**
* Get an instance of the type with all fields set to their default values.
* This may or may not be a singleton. This differs from the
* {@code getDefaultInstance()} method of generated message classes in that
* this method is an abstract method of the {@code MessageLite} interface
* whereas {@code getDefaultInstance()} is a static method of a specific
* class. They return the same thing.
*/
MessageLite getDefaultInstanceForType();
/**
* Returns true if all required fields in the message and all embedded
* messages are set, false otherwise.
*/
boolean isInitialized();
/**
* Serializes the message and writes it to {@code output}. This does not
* flush or close the stream.
*/
void writeTo(CodedOutputStream output) throws IOException;
/**
* Get the number of bytes required to encode this message. The result
* is only computed on the first call and memoized after that.
*/
int getSerializedSize();
// -----------------------------------------------------------------
// Convenience methods.
/**
* Serializes the message to a {@code ByteString} and returns it. This is
* just a trivial wrapper around
* {@link #writeTo(CodedOutputStream)}.
*/
ByteString toByteString();
/**
* Serializes the message to a {@code byte} array and returns it. This is
* just a trivial wrapper around
* {@link #writeTo(CodedOutputStream)}.
*/
byte[] toByteArray();
/**
* Serializes the message and writes it to {@code output}. This is just a
* trivial wrapper around {@link #writeTo(CodedOutputStream)}. This does
* not flush or close the stream.
* <p>
* NOTE: Protocol Buffers are not self-delimiting. Therefore, if you write
* any more data to the stream after the message, you must somehow ensure
* that the parser on the receiving end does not interpret this as being
* part of the protocol message. This can be done e.g. by writing the size
* of the message before the data, then making sure to limit the input to
* that size on the receiving end (e.g. by wrapping the InputStream in one
* which limits the input). Alternatively, just use
* {@link #writeDelimitedTo(OutputStream)}.
*/
void writeTo(OutputStream output) throws IOException;
/**
* Like {@link #writeTo(OutputStream)}, but writes the size of the message
* as a varint before writing the data. This allows more data to be written
* to the stream after the message without the need to delimit the message
* data yourself. Use {@link Builder#mergeDelimitedFrom(InputStream)} (or
* the static method {@code YourMessageType.parseDelimitedFrom(InputStream)})
* to parse messages written by this method.
*/
void writeDelimitedTo(OutputStream output) throws IOException;
// =================================================================
// Builders
/**
* Constructs a new builder for a message of the same type as this message.
*/
Builder newBuilderForType();
/**
* Constructs a builder initialized with the current message. Use this to
* derive a new message from the current one.
*/
Builder toBuilder();
/**
* Abstract interface implemented by Protocol Message builders.
*/
interface Builder extends Cloneable {
/** Resets all fields to their default values. */
Builder clear();
/**
* Construct the final message. Once this is called, the Builder is no
* longer valid, and calling any other method will result in undefined
* behavior and may throw a NullPointerException. If you need to continue
* working with the builder after calling {@code build()}, {@code clone()}
* it first.
* @throws UninitializedMessageException The message is missing one or more
* required fields (i.e. {@link #isInitialized()} returns false).
* Use {@link #buildPartial()} to bypass this check.
*/
MessageLite build();
/**
* Like {@link #build()}, but does not throw an exception if the message
* is missing required fields. Instead, a partial message is returned.
* Once this is called, the Builder is no longer valid, and calling any
* will result in undefined behavior and may throw a NullPointerException.
*
* If you need to continue working with the builder after calling
* {@code buildPartial()}, {@code clone()} it first.
*/
MessageLite buildPartial();
/**
* Clones the Builder.
* @see Object#clone()
*/
Builder clone();
/**
* Returns true if all required fields in the message and all embedded
* messages are set, false otherwise.
*/
boolean isInitialized();
/**
* Parses a message of this type from the input and merges it with this
* message, as if using {@link Builder#mergeFrom(MessageLite)}.
*
* <p>Warning: This does not verify that all required fields are present in
* the input message. If you call {@link #build()} without setting all
* required fields, it will throw an {@link UninitializedMessageException},
* which is a {@code RuntimeException} and thus might not be caught. There
* are a few good ways to deal with this:
* <ul>
* <li>Call {@link #isInitialized()} to verify that all required fields
* are set before building.
* <li>Parse the message separately using one of the static
* {@code parseFrom} methods, then use {@link #mergeFrom(MessageLite)}
* to merge it with this one. {@code parseFrom} will throw an
* {@link InvalidProtocolBufferException} (an {@code IOException})
* if some required fields are missing.
* <li>Use {@code buildPartial()} to build, which ignores missing
* required fields.
* </ul>
*
* <p>Note: The caller should call
* {@link CodedInputStream#checkLastTagWas(int)} after calling this to
* verify that the last tag seen was the appropriate end-group tag,
* or zero for EOF.
*/
Builder mergeFrom(CodedInputStream input) throws IOException;
/**
* Like {@link Builder#mergeFrom(CodedInputStream)}, but also
* parses extensions. The extensions that you want to be able to parse
* must be registered in {@code extensionRegistry}. Extensions not in
* the registry will be treated as unknown fields.
*/
Builder mergeFrom(CodedInputStream input,
ExtensionRegistryLite extensionRegistry)
throws IOException;
/**
* Get the message's type's default instance.
* See {@link MessageLite#getDefaultInstanceForType()}.
*/
MessageLite getDefaultInstanceForType();
// ---------------------------------------------------------------
// Convenience methods.
/**
* Parse {@code data} as a message of this type and merge it with the
* message being built. This is just a small wrapper around
* {@link #mergeFrom(CodedInputStream)}.
*/
Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException;
/**
* Parse {@code data} as a message of this type and merge it with the
* message being built. This is just a small wrapper around
* {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}.
*/
Builder mergeFrom(ByteString data,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException;
/**
* Parse {@code data} as a message of this type and merge it with the
* message being built. This is just a small wrapper around
* {@link #mergeFrom(CodedInputStream)}.
*/
Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException;
/**
* Parse {@code data} as a message of this type and merge it with the
* message being built. This is just a small wrapper around
* {@link #mergeFrom(CodedInputStream)}.
*/
Builder mergeFrom(byte[] data, int off, int len)
throws InvalidProtocolBufferException;
/**
* Parse {@code data} as a message of this type and merge it with the
* message being built. This is just a small wrapper around
* {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}.
*/
Builder mergeFrom(byte[] data,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException;
/**
* Parse {@code data} as a message of this type and merge it with the
* message being built. This is just a small wrapper around
* {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}.
*/
Builder mergeFrom(byte[] data, int off, int len,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException;
/**
* Parse a message of this type from {@code input} and merge it with the
* message being built. This is just a small wrapper around
* {@link #mergeFrom(CodedInputStream)}. Note that this method always
* reads the <i>entire</i> input (unless it throws an exception). If you
* want it to stop earlier, you will need to wrap your input in some
* wrapper stream that limits reading. Or, use
* {@link MessageLite#writeDelimitedTo(OutputStream)} to write your message
* and {@link #mergeDelimitedFrom(InputStream)} to read it.
* <p>
* Despite usually reading the entire input, this does not close the stream.
*/
Builder mergeFrom(InputStream input) throws IOException;
/**
* Parse a message of this type from {@code input} and merge it with the
* message being built. This is just a small wrapper around
* {@link #mergeFrom(CodedInputStream,ExtensionRegistry)}.
*/
Builder mergeFrom(InputStream input,
ExtensionRegistryLite extensionRegistry)
throws IOException;
/**
* Like {@link #mergeFrom(InputStream)}, but does not read until EOF.
* Instead, the size of the message (encoded as a varint) is read first,
* then the message data. Use
* {@link MessageLite#writeDelimitedTo(OutputStream)} to write messages in
* this format.
*
* @returns True if successful, or false if the stream is at EOF when the
* method starts. Any other error (including reaching EOF during
* parsing) will cause an exception to be thrown.
*/
boolean mergeDelimitedFrom(InputStream input)
throws IOException;
/**
* Like {@link #mergeDelimitedFrom(InputStream)} but supporting extensions.
*/
boolean mergeDelimitedFrom(InputStream input,
ExtensionRegistryLite extensionRegistry)
throws IOException;
}
}

View file

@ -0,0 +1,59 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import com.google.protobuf.Descriptors.EnumDescriptor;
import com.google.protobuf.Descriptors.EnumValueDescriptor;
/**
* Interface of useful methods added to all enums generated by the protocol
* compiler.
*/
public interface ProtocolMessageEnum extends Internal.EnumLite {
/**
* Return the value's numeric value as defined in the .proto file.
*/
@Override
int getNumber();
/**
* Return the value's descriptor, which contains information such as
* value name, number, and type.
*/
EnumValueDescriptor getValueDescriptor();
/**
* Return the enum type's descriptor, which contains information
* about each defined value, etc.
*/
EnumDescriptor getDescriptorForType();
}

View file

@ -0,0 +1,47 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
/**
* Interface for an RPC callback, normally called when an RPC completes.
* {@code ParameterType} is normally the method's response message type.
*
* <p>Starting with version 2.3.0, RPC implementations should not try to build
* on this, but should instead provide code generator plugins which generate
* code specific to the particular RPC implementation. This way the generated
* code can be more appropriate for the implementation in use and can avoid
* unnecessary layers of indirection.
*
* @author kenton@google.com Kenton Varda
*/
public interface RpcCallback<ParameterType> {
void run(ParameterType parameter);
}

View file

@ -0,0 +1,71 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
/**
* <p>Abstract interface for an RPC channel. An {@code RpcChannel} represents a
* communication line to a {@link Service} which can be used to call that
* {@link Service}'s methods. The {@link Service} may be running on another
* machine. Normally, you should not call an {@code RpcChannel} directly, but
* instead construct a stub {@link Service} wrapping it. Example:
*
* <pre>
* RpcChannel channel = rpcImpl.newChannel("remotehost.example.com:1234");
* RpcController controller = rpcImpl.newController();
* MyService service = MyService.newStub(channel);
* service.myMethod(controller, request, callback);
* </pre>
*
* <p>Starting with version 2.3.0, RPC implementations should not try to build
* on this, but should instead provide code generator plugins which generate
* code specific to the particular RPC implementation. This way the generated
* code can be more appropriate for the implementation in use and can avoid
* unnecessary layers of indirection.
*
* @author kenton@google.com Kenton Varda
*/
public interface RpcChannel {
/**
* Call the given method of the remote service. This method is similar to
* {@code Service.callMethod()} with one important difference: the caller
* decides the types of the {@code Message} objects, not the callee. The
* request may be of any type as long as
* {@code request.getDescriptor() == method.getInputType()}.
* The response passed to the callback will be of the same type as
* {@code responsePrototype} (which must have
* {@code getDescriptor() == method.getOutputType()}).
*/
void callMethod(Descriptors.MethodDescriptor method,
RpcController controller,
Message request,
Message responsePrototype,
RpcCallback<Message> done);
}

View file

@ -0,0 +1,118 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
/**
* <p>An {@code RpcController} mediates a single method call. The primary
* purpose of the controller is to provide a way to manipulate settings
* specific to the RPC implementation and to find out about RPC-level errors.
*
* <p>Starting with version 2.3.0, RPC implementations should not try to build
* on this, but should instead provide code generator plugins which generate
* code specific to the particular RPC implementation. This way the generated
* code can be more appropriate for the implementation in use and can avoid
* unnecessary layers of indirection.
*
* <p>The methods provided by the {@code RpcController} interface are intended
* to be a "least common denominator" set of features which we expect all
* implementations to support. Specific implementations may provide more
* advanced features (e.g. deadline propagation).
*
* @author kenton@google.com Kenton Varda
*/
public interface RpcController {
// -----------------------------------------------------------------
// These calls may be made from the client side only. Their results
// are undefined on the server side (may throw RuntimeExceptions).
/**
* Resets the RpcController to its initial state so that it may be reused in
* a new call. This can be called from the client side only. It must not
* be called while an RPC is in progress.
*/
void reset();
/**
* After a call has finished, returns true if the call failed. The possible
* reasons for failure depend on the RPC implementation. {@code failed()}
* most only be called on the client side, and must not be called before a
* call has finished.
*/
boolean failed();
/**
* If {@code failed()} is {@code true}, returns a human-readable description
* of the error.
*/
String errorText();
/**
* Advises the RPC system that the caller desires that the RPC call be
* canceled. The RPC system may cancel it immediately, may wait awhile and
* then cancel it, or may not even cancel the call at all. If the call is
* canceled, the "done" callback will still be called and the RpcController
* will indicate that the call failed at that time.
*/
void startCancel();
// -----------------------------------------------------------------
// These calls may be made from the server side only. Their results
// are undefined on the client side (may throw RuntimeExceptions).
/**
* Causes {@code failed()} to return true on the client side. {@code reason}
* will be incorporated into the message returned by {@code errorText()}.
* If you find you need to return machine-readable information about
* failures, you should incorporate it into your response protocol buffer
* and should NOT call {@code setFailed()}.
*/
void setFailed(String reason);
/**
* If {@code true}, indicates that the client canceled the RPC, so the server
* may as well give up on replying to it. This method must be called on the
* server side only. The server should still call the final "done" callback.
*/
boolean isCanceled();
/**
* Asks that the given callback be called when the RPC is canceled. The
* parameter passed to the callback will always be {@code null}. The
* callback will always be called exactly once. If the RPC completes without
* being canceled, the callback will be called after completion. If the RPC
* has already been canceled when NotifyOnCancel() is called, the callback
* will be called immediately.
*
* <p>{@code notifyOnCancel()} must be called no more than once per request.
* It must be called on the server side only.
*/
void notifyOnCancel(RpcCallback<Object> callback);
}

View file

@ -0,0 +1,137 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
/**
* Grab-bag of utility functions useful when dealing with RPCs.
*
* @author kenton@google.com Kenton Varda
*/
public final class RpcUtil {
private RpcUtil() {}
/**
* Take an {@code RpcCallback<Message>} and convert it to an
* {@code RpcCallback} accepting a specific message type. This is always
* type-safe (parameter type contravariance).
*/
@SuppressWarnings("unchecked")
public static <Type extends Message> RpcCallback<Type>
specializeCallback(final RpcCallback<Message> originalCallback) {
return (RpcCallback<Type>)originalCallback;
// The above cast works, but only due to technical details of the Java
// implementation. A more theoretically correct -- but less efficient --
// implementation would be as follows:
// return new RpcCallback<Type>() {
// public void run(Type parameter) {
// originalCallback.run(parameter);
// }
// };
}
/**
* Take an {@code RpcCallback} accepting a specific message type and convert
* it to an {@code RpcCallback<Message>}. The generalized callback will
* accept any message object which has the same descriptor, and will convert
* it to the correct class before calling the original callback. However,
* if the generalized callback is given a message with a different descriptor,
* an exception will be thrown.
*/
public static <Type extends Message>
RpcCallback<Message> generalizeCallback(
final RpcCallback<Type> originalCallback,
final Class<Type> originalClass,
final Type defaultInstance) {
return new RpcCallback<Message>() {
@Override
public void run(final Message parameter) {
Type typedParameter;
try {
typedParameter = originalClass.cast(parameter);
} catch (ClassCastException ignored) {
typedParameter = copyAsType(defaultInstance, parameter);
}
originalCallback.run(typedParameter);
}
};
}
/**
* Creates a new message of type "Type" which is a copy of "source". "source"
* must have the same descriptor but may be a different class (e.g.
* DynamicMessage).
*/
@SuppressWarnings("unchecked")
private static <Type extends Message> Type copyAsType(
final Type typeDefaultInstance, final Message source) {
return (Type)typeDefaultInstance.newBuilderForType()
.mergeFrom(source)
.build();
}
/**
* Creates a callback which can only be called once. This may be useful for
* security, when passing a callback to untrusted code: most callbacks do
* not expect to be called more than once, so doing so may expose bugs if it
* is not prevented.
*/
public static <ParameterType>
RpcCallback<ParameterType> newOneTimeCallback(
final RpcCallback<ParameterType> originalCallback) {
return new RpcCallback<ParameterType>() {
private boolean alreadyCalled = false;
@Override
public void run(final ParameterType parameter) {
synchronized(this) {
if (alreadyCalled) {
throw new AlreadyCalledException();
}
alreadyCalled = true;
}
originalCallback.run(parameter);
}
};
}
/**
* Exception thrown when a one-time callback is called more than once.
*/
public static final class AlreadyCalledException extends RuntimeException {
private static final long serialVersionUID = 5469741279507848266L;
public AlreadyCalledException() {
super("This RpcCallback was already called and cannot be called " +
"multiple times.");
}
}
}

View file

@ -0,0 +1,117 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
/**
* Abstract base interface for protocol-buffer-based RPC services. Services
* themselves are abstract classes (implemented either by servers or as
* stubs), but they subclass this base interface. The methods of this
* interface can be used to call the methods of the service without knowing
* its exact type at compile time (analogous to the Message interface).
*
* <p>Starting with version 2.3.0, RPC implementations should not try to build
* on this, but should instead provide code generator plugins which generate
* code specific to the particular RPC implementation. This way the generated
* code can be more appropriate for the implementation in use and can avoid
* unnecessary layers of indirection.
*
* @author kenton@google.com Kenton Varda
*/
public interface Service {
/**
* Get the {@code ServiceDescriptor} describing this service and its methods.
*/
Descriptors.ServiceDescriptor getDescriptorForType();
/**
* <p>Call a method of the service specified by MethodDescriptor. This is
* normally implemented as a simple {@code switch()} that calls the standard
* definitions of the service's methods.
*
* <p>Preconditions:
* <ul>
* <li>{@code method.getService() == getDescriptorForType()}
* <li>{@code request} is of the exact same class as the object returned by
* {@code getRequestPrototype(method)}.
* <li>{@code controller} is of the correct type for the RPC implementation
* being used by this Service. For stubs, the "correct type" depends
* on the RpcChannel which the stub is using. Server-side Service
* implementations are expected to accept whatever type of
* {@code RpcController} the server-side RPC implementation uses.
* </ul>
*
* <p>Postconditions:
* <ul>
* <li>{@code done} will be called when the method is complete. This may be
* before {@code callMethod()} returns or it may be at some point in
* the future.
* <li>The parameter to {@code done} is the response. It must be of the
* exact same type as would be returned by
* {@code getResponsePrototype(method)}.
* <li>If the RPC failed, the parameter to {@code done} will be
* {@code null}. Further details about the failure can be found by
* querying {@code controller}.
* </ul>
*/
void callMethod(Descriptors.MethodDescriptor method,
RpcController controller,
Message request,
RpcCallback<Message> done);
/**
* <p>{@code callMethod()} requires that the request passed in is of a
* particular subclass of {@code Message}. {@code getRequestPrototype()}
* gets the default instances of this type for a given method. You can then
* call {@code Message.newBuilderForType()} on this instance to
* construct a builder to build an object which you can then pass to
* {@code callMethod()}.
*
* <p>Example:
* <pre>
* MethodDescriptor method =
* service.getDescriptorForType().findMethodByName("Foo");
* Message request =
* stub.getRequestPrototype(method).newBuilderForType()
* .mergeFrom(input).build();
* service.callMethod(method, request, callback);
* </pre>
*/
Message getRequestPrototype(Descriptors.MethodDescriptor method);
/**
* Like {@code getRequestPrototype()}, but gets a prototype of the response
* message. {@code getResponsePrototype()} is generally not needed because
* the {@code Service} implementation constructs the response message itself,
* but it may be useful in some cases to know ahead of time what type of
* object will be returned.
*/
Message getResponsePrototype(Descriptors.MethodDescriptor method);
}

View file

@ -0,0 +1,44 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
/**
* Thrown by blocking RPC methods when a failure occurs.
*
* @author cpovirk@google.com (Chris Povirk)
*/
public final class ServiceException extends Exception {
private static final long serialVersionUID = -1219262335729891920L;
public ServiceException(final String message) {
super(message);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,99 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import java.util.Collections;
import java.util.List;
/**
* Thrown when attempting to build a protocol message that is missing required
* fields. This is a {@code RuntimeException} because it normally represents
* a programming error: it happens when some code which constructs a message
* fails to set all the fields. {@code parseFrom()} methods <b>do not</b>
* throw this; they throw an {@link InvalidProtocolBufferException} if
* required fields are missing, because it is not a programming error to
* receive an incomplete message. In other words,
* {@code UninitializedMessageException} should never be thrown by correct
* code, but {@code InvalidProtocolBufferException} might be.
*
* @author kenton@google.com Kenton Varda
*/
public class UninitializedMessageException extends RuntimeException {
private static final long serialVersionUID = -7466929953374883507L;
public UninitializedMessageException(final MessageLite message) {
super("Message was missing required fields. (Lite runtime could not " +
"determine which fields were missing).");
missingFields = null;
}
public UninitializedMessageException(final List<String> missingFields) {
super(buildDescription(missingFields));
this.missingFields = missingFields;
}
private final List<String> missingFields;
/**
* Get a list of human-readable names of required fields missing from this
* message. Each name is a full path to a field, e.g. "foo.bar[5].baz".
* Returns null if the lite runtime was used, since it lacks the ability to
* find missing fields.
*/
public List<String> getMissingFields() {
return Collections.unmodifiableList(missingFields);
}
/**
* Converts this exception to an {@link InvalidProtocolBufferException}.
* When a parsed message is missing required fields, this should be thrown
* instead of {@code UninitializedMessageException}.
*/
public InvalidProtocolBufferException asInvalidProtocolBufferException() {
return new InvalidProtocolBufferException(getMessage());
}
/** Construct the description string for this exception. */
private static String buildDescription(final List<String> missingFields) {
final StringBuilder description =
new StringBuilder("Message missing required fields: ");
boolean first = true;
for (final String field : missingFields) {
if (first) {
first = false;
} else {
description.append(", ");
}
description.append(field);
}
return description.toString();
}
}

View file

@ -0,0 +1,980 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.google.protobuf.AbstractMessageLite.Builder.LimitedInputStream;
/**
* {@code UnknownFieldSet} is used to keep track of fields which were seen when
* parsing a protocol message but whose field numbers or types are unrecognized.
* This most frequently occurs when new fields are added to a message type
* and then messages containing those feilds are read by old software that was
* compiled before the new types were added.
*
* <p>Every {@link Message} contains an {@code UnknownFieldSet} (and every
* {@link Message.Builder} contains an {@link Builder}).
*
* <p>Most users will never need to use this class.
*
* @author kenton@google.com Kenton Varda
*/
public final class UnknownFieldSet implements MessageLite {
private UnknownFieldSet() {}
/** Create a new {@link Builder}. */
public static Builder newBuilder() {
return Builder.create();
}
/**
* Create a new {@link Builder} and initialize it to be a copy
* of {@code copyFrom}.
*/
public static Builder newBuilder(final UnknownFieldSet copyFrom) {
return newBuilder().mergeFrom(copyFrom);
}
/** Get an empty {@code UnknownFieldSet}. */
public static UnknownFieldSet getDefaultInstance() {
return defaultInstance;
}
@Override
public UnknownFieldSet getDefaultInstanceForType() {
return defaultInstance;
}
private static final UnknownFieldSet defaultInstance =
new UnknownFieldSet(Collections.<Integer, Field>emptyMap());
/**
* Construct an {@code UnknownFieldSet} around the given map. The map is
* expected to be immutable.
*/
private UnknownFieldSet(final Map<Integer, Field> fields) {
this.fields = fields;
}
private Map<Integer, Field> fields;
@Override
public boolean equals(final Object other) {
if (this == other) {
return true;
}
return (other instanceof UnknownFieldSet) &&
fields.equals(((UnknownFieldSet) other).fields);
}
@Override
public int hashCode() {
return fields.hashCode();
}
/** Get a map of fields in the set by number. */
public Map<Integer, Field> asMap() {
return fields;
}
/** Check if the given field number is present in the set. */
public boolean hasField(final int number) {
return fields.containsKey(number);
}
/**
* Get a field by number. Returns an empty field if not present. Never
* returns {@code null}.
*/
public Field getField(final int number) {
final Field result = fields.get(number);
return (result == null) ? Field.getDefaultInstance() : result;
}
/** Serializes the set and writes it to {@code output}. */
@Override
public void writeTo(final CodedOutputStream output) throws IOException {
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
entry.getValue().writeTo(entry.getKey(), output);
}
}
/**
* Converts the set to a string in protocol buffer text format. This is
* just a trivial wrapper around
* {@link TextFormat#printToString(UnknownFieldSet)}.
*/
@Override
public String toString() {
return TextFormat.printToString(this);
}
/**
* Serializes the message to a {@code ByteString} and returns it. This is
* just a trivial wrapper around {@link #writeTo(CodedOutputStream)}.
*/
@Override
public ByteString toByteString() {
try {
final ByteString.CodedBuilder out =
ByteString.newCodedBuilder(getSerializedSize());
writeTo(out.getCodedOutput());
return out.build();
} catch (final IOException e) {
throw new RuntimeException(
"Serializing to a ByteString threw an IOException (should " +
"never happen).", e);
}
}
/**
* Serializes the message to a {@code byte} array and returns it. This is
* just a trivial wrapper around {@link #writeTo(CodedOutputStream)}.
*/
@Override
public byte[] toByteArray() {
try {
final byte[] result = new byte[getSerializedSize()];
final CodedOutputStream output = CodedOutputStream.newInstance(result);
writeTo(output);
output.checkNoSpaceLeft();
return result;
} catch (final IOException e) {
throw new RuntimeException(
"Serializing to a byte array threw an IOException " +
"(should never happen).", e);
}
}
/**
* Serializes the message and writes it to {@code output}. This is just a
* trivial wrapper around {@link #writeTo(CodedOutputStream)}.
*/
@Override
public void writeTo(final OutputStream output) throws IOException {
final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output);
writeTo(codedOutput);
codedOutput.flush();
}
@Override
public void writeDelimitedTo(OutputStream output) throws IOException {
final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output);
codedOutput.writeRawVarint32(getSerializedSize());
writeTo(codedOutput);
codedOutput.flush();
}
/** Get the number of bytes required to encode this set. */
@Override
public int getSerializedSize() {
int result = 0;
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
result += entry.getValue().getSerializedSize(entry.getKey());
}
return result;
}
/**
* Serializes the set and writes it to {@code output} using
* {@code MessageSet} wire format.
*/
public void writeAsMessageSetTo(final CodedOutputStream output)
throws IOException {
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
entry.getValue().writeAsMessageSetExtensionTo(
entry.getKey(), output);
}
}
/**
* Get the number of bytes required to encode this set using
* {@code MessageSet} wire format.
*/
public int getSerializedSizeAsMessageSet() {
int result = 0;
for (final Map.Entry<Integer, Field> entry : fields.entrySet()) {
result += entry.getValue().getSerializedSizeAsMessageSetExtension(
entry.getKey());
}
return result;
}
@Override
public boolean isInitialized() {
// UnknownFieldSets do not have required fields, so they are always
// initialized.
return true;
}
/** Parse an {@code UnknownFieldSet} from the given input stream. */
public static UnknownFieldSet parseFrom(final CodedInputStream input)
throws IOException {
return newBuilder().mergeFrom(input).build();
}
/** Parse {@code data} as an {@code UnknownFieldSet} and return it. */
public static UnknownFieldSet parseFrom(final ByteString data)
throws InvalidProtocolBufferException {
return newBuilder().mergeFrom(data).build();
}
/** Parse {@code data} as an {@code UnknownFieldSet} and return it. */
public static UnknownFieldSet parseFrom(final byte[] data)
throws InvalidProtocolBufferException {
return newBuilder().mergeFrom(data).build();
}
/** Parse an {@code UnknownFieldSet} from {@code input} and return it. */
public static UnknownFieldSet parseFrom(final InputStream input)
throws IOException {
return newBuilder().mergeFrom(input).build();
}
@Override
public Builder newBuilderForType() {
return newBuilder();
}
@Override
public Builder toBuilder() {
return newBuilder().mergeFrom(this);
}
/**
* Builder for {@link UnknownFieldSet}s.
*
* <p>Note that this class maintains {@link Field.Builder}s for all fields
* in the set. Thus, adding one element to an existing {@link Field} does not
* require making a copy. This is important for efficient parsing of
* unknown repeated fields. However, it implies that {@link Field}s cannot
* be constructed independently, nor can two {@link UnknownFieldSet}s share
* the same {@code Field} object.
*
* <p>Use {@link UnknownFieldSet#newBuilder()} to construct a {@code Builder}.
*/
public static final class Builder implements MessageLite.Builder {
// This constructor should never be called directly (except from 'create').
private Builder() {}
private Map<Integer, Field> fields;
// Optimization: We keep around a builder for the last field that was
// modified so that we can efficiently add to it multiple times in a
// row (important when parsing an unknown repeated field).
private int lastFieldNumber;
private Field.Builder lastField;
private static Builder create() {
Builder builder = new Builder();
builder.reinitialize();
return builder;
}
/**
* Get a field builder for the given field number which includes any
* values that already exist.
*/
private Field.Builder getFieldBuilder(final int number) {
if (lastField != null) {
if (number == lastFieldNumber) {
return lastField;
}
// Note: addField() will reset lastField and lastFieldNumber.
addField(lastFieldNumber, lastField.build());
}
if (number == 0) {
return null;
} else {
final Field existing = fields.get(number);
lastFieldNumber = number;
lastField = Field.newBuilder();
if (existing != null) {
lastField.mergeFrom(existing);
}
return lastField;
}
}
/**
* Build the {@link UnknownFieldSet} and return it.
*
* <p>Once {@code build()} has been called, the {@code Builder} will no
* longer be usable. Calling any method after {@code build()} will result
* in undefined behavior and can cause a {@code NullPointerException} to be
* thrown.
*/
@Override
public UnknownFieldSet build() {
getFieldBuilder(0); // Force lastField to be built.
final UnknownFieldSet result;
if (fields.isEmpty()) {
result = getDefaultInstance();
} else {
result = new UnknownFieldSet(Collections.unmodifiableMap(fields));
}
fields = null;
return result;
}
@Override
public UnknownFieldSet buildPartial() {
// No required fields, so this is the same as build().
return build();
}
@Override
public Builder clone() {
getFieldBuilder(0); // Force lastField to be built.
return UnknownFieldSet.newBuilder().mergeFrom(
new UnknownFieldSet(fields));
}
@Override
public UnknownFieldSet getDefaultInstanceForType() {
return UnknownFieldSet.getDefaultInstance();
}
private void reinitialize() {
fields = Collections.emptyMap();
lastFieldNumber = 0;
lastField = null;
}
/** Reset the builder to an empty set. */
@Override
public Builder clear() {
reinitialize();
return this;
}
/**
* Merge the fields from {@code other} into this set. If a field number
* exists in both sets, {@code other}'s values for that field will be
* appended to the values in this set.
*/
public Builder mergeFrom(final UnknownFieldSet other) {
if (other != getDefaultInstance()) {
for (final Map.Entry<Integer, Field> entry : other.fields.entrySet()) {
mergeField(entry.getKey(), entry.getValue());
}
}
return this;
}
/**
* Add a field to the {@code UnknownFieldSet}. If a field with the same
* number already exists, the two are merged.
*/
public Builder mergeField(final int number, final Field field) {
if (number == 0) {
throw new IllegalArgumentException("Zero is not a valid field number.");
}
if (hasField(number)) {
getFieldBuilder(number).mergeFrom(field);
} else {
// Optimization: We could call getFieldBuilder(number).mergeFrom(field)
// in this case, but that would create a copy of the Field object.
// We'd rather reuse the one passed to us, so call addField() instead.
addField(number, field);
}
return this;
}
/**
* Convenience method for merging a new field containing a single varint
* value. This is used in particular when an unknown enum value is
* encountered.
*/
public Builder mergeVarintField(final int number, final int value) {
if (number == 0) {
throw new IllegalArgumentException("Zero is not a valid field number.");
}
getFieldBuilder(number).addVarint(value);
return this;
}
/** Check if the given field number is present in the set. */
public boolean hasField(final int number) {
if (number == 0) {
throw new IllegalArgumentException("Zero is not a valid field number.");
}
return number == lastFieldNumber || fields.containsKey(number);
}
/**
* Add a field to the {@code UnknownFieldSet}. If a field with the same
* number already exists, it is removed.
*/
public Builder addField(final int number, final Field field) {
if (number == 0) {
throw new IllegalArgumentException("Zero is not a valid field number.");
}
if (lastField != null && lastFieldNumber == number) {
// Discard this.
lastField = null;
lastFieldNumber = 0;
}
if (fields.isEmpty()) {
fields = new TreeMap<Integer,Field>();
}
fields.put(number, field);
return this;
}
/**
* Get all present {@code Field}s as an immutable {@code Map}. If more
* fields are added, the changes may or may not be reflected in this map.
*/
public Map<Integer, Field> asMap() {
getFieldBuilder(0); // Force lastField to be built.
return Collections.unmodifiableMap(fields);
}
/**
* Parse an entire message from {@code input} and merge its fields into
* this set.
*/
@Override
public Builder mergeFrom(final CodedInputStream input) throws IOException {
while (true) {
final int tag = input.readTag();
if (tag == 0 || !mergeFieldFrom(tag, input)) {
break;
}
}
return this;
}
/**
* Parse a single field from {@code input} and merge it into this set.
* @param tag The field's tag number, which was already parsed.
* @return {@code false} if the tag is an engroup tag.
*/
public boolean mergeFieldFrom(final int tag, final CodedInputStream input)
throws IOException {
final int number = WireFormat.getTagFieldNumber(tag);
switch (WireFormat.getTagWireType(tag)) {
case WireFormat.WIRETYPE_VARINT:
getFieldBuilder(number).addVarint(input.readInt64());
return true;
case WireFormat.WIRETYPE_FIXED64:
getFieldBuilder(number).addFixed64(input.readFixed64());
return true;
case WireFormat.WIRETYPE_LENGTH_DELIMITED:
getFieldBuilder(number).addLengthDelimited(input.readBytes());
return true;
case WireFormat.WIRETYPE_START_GROUP:
final Builder subBuilder = newBuilder();
input.readGroup(number, subBuilder,
ExtensionRegistry.getEmptyRegistry());
getFieldBuilder(number).addGroup(subBuilder.build());
return true;
case WireFormat.WIRETYPE_END_GROUP:
return false;
case WireFormat.WIRETYPE_FIXED32:
getFieldBuilder(number).addFixed32(input.readFixed32());
return true;
default:
throw InvalidProtocolBufferException.invalidWireType();
}
}
/**
* Parse {@code data} as an {@code UnknownFieldSet} and merge it with the
* set being built. This is just a small wrapper around
* {@link #mergeFrom(CodedInputStream)}.
*/
@Override
public Builder mergeFrom(final ByteString data)
throws InvalidProtocolBufferException {
try {
final CodedInputStream input = data.newCodedInput();
mergeFrom(input);
input.checkLastTagWas(0);
return this;
} catch (final InvalidProtocolBufferException e) {
throw e;
} catch (final IOException e) {
throw new RuntimeException(
"Reading from a ByteString threw an IOException (should " +
"never happen).", e);
}
}
/**
* Parse {@code data} as an {@code UnknownFieldSet} and merge it with the
* set being built. This is just a small wrapper around
* {@link #mergeFrom(CodedInputStream)}.
*/
@Override
public Builder mergeFrom(final byte[] data)
throws InvalidProtocolBufferException {
try {
final CodedInputStream input = CodedInputStream.newInstance(data);
mergeFrom(input);
input.checkLastTagWas(0);
return this;
} catch (final InvalidProtocolBufferException e) {
throw e;
} catch (final IOException e) {
throw new RuntimeException(
"Reading from a byte array threw an IOException (should " +
"never happen).", e);
}
}
/**
* Parse an {@code UnknownFieldSet} from {@code input} and merge it with the
* set being built. This is just a small wrapper around
* {@link #mergeFrom(CodedInputStream)}.
*/
@Override
public Builder mergeFrom(final InputStream input) throws IOException {
final CodedInputStream codedInput = CodedInputStream.newInstance(input);
mergeFrom(codedInput);
codedInput.checkLastTagWas(0);
return this;
}
@Override
public boolean mergeDelimitedFrom(InputStream input)
throws IOException {
final int firstByte = input.read();
if (firstByte == -1) {
return false;
}
final int size = CodedInputStream.readRawVarint32(firstByte, input);
final InputStream limitedInput = new LimitedInputStream(input, size);
mergeFrom(limitedInput);
return true;
}
@Override
public boolean mergeDelimitedFrom(
InputStream input,
ExtensionRegistryLite extensionRegistry) throws IOException {
// UnknownFieldSet has no extensions.
return mergeDelimitedFrom(input);
}
@Override
public Builder mergeFrom(
CodedInputStream input,
ExtensionRegistryLite extensionRegistry) throws IOException {
// UnknownFieldSet has no extensions.
return mergeFrom(input);
}
@Override
public Builder mergeFrom(
ByteString data,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
// UnknownFieldSet has no extensions.
return mergeFrom(data);
}
@Override
public Builder mergeFrom(byte[] data, int off, int len)
throws InvalidProtocolBufferException {
try {
final CodedInputStream input =
CodedInputStream.newInstance(data, off, len);
mergeFrom(input);
input.checkLastTagWas(0);
return this;
} catch (InvalidProtocolBufferException e) {
throw e;
} catch (IOException e) {
throw new RuntimeException(
"Reading from a byte array threw an IOException (should " +
"never happen).", e);
}
}
@Override
public Builder mergeFrom(
byte[] data,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
// UnknownFieldSet has no extensions.
return mergeFrom(data);
}
@Override
public Builder mergeFrom(
byte[] data, int off, int len,
ExtensionRegistryLite extensionRegistry)
throws InvalidProtocolBufferException {
// UnknownFieldSet has no extensions.
return mergeFrom(data, off, len);
}
@Override
public Builder mergeFrom(
InputStream input,
ExtensionRegistryLite extensionRegistry) throws IOException {
// UnknownFieldSet has no extensions.
return mergeFrom(input);
}
@Override
public boolean isInitialized() {
// UnknownFieldSets do not have required fields, so they are always
// initialized.
return true;
}
}
/**
* Represents a single field in an {@code UnknownFieldSet}.
*
* <p>A {@code Field} consists of five lists of values. The lists correspond
* to the five "wire types" used in the protocol buffer binary format.
* The wire type of each field can be determined from the encoded form alone,
* without knowing the field's declared type. So, we are able to parse
* unknown values at least this far and separate them. Normally, only one
* of the five lists will contain any values, since it is impossible to
* define a valid message type that declares two different types for the
* same field number. However, the code is designed to allow for the case
* where the same unknown field number is encountered using multiple different
* wire types.
*
* <p>{@code Field} is an immutable class. To construct one, you must use a
* {@link Builder}.
*
* @see UnknownFieldSet
*/
public static final class Field {
private Field() {}
/** Construct a new {@link Builder}. */
public static Builder newBuilder() {
return Builder.create();
}
/**
* Construct a new {@link Builder} and initialize it to a copy of
* {@code copyFrom}.
*/
public static Builder newBuilder(final Field copyFrom) {
return newBuilder().mergeFrom(copyFrom);
}
/** Get an empty {@code Field}. */
public static Field getDefaultInstance() {
return fieldDefaultInstance;
}
private static final Field fieldDefaultInstance = newBuilder().build();
/** Get the list of varint values for this field. */
public List<Long> getVarintList() { return varint; }
/** Get the list of fixed32 values for this field. */
public List<Integer> getFixed32List() { return fixed32; }
/** Get the list of fixed64 values for this field. */
public List<Long> getFixed64List() { return fixed64; }
/** Get the list of length-delimited values for this field. */
public List<ByteString> getLengthDelimitedList() { return lengthDelimited; }
/**
* Get the list of embedded group values for this field. These are
* represented using {@link UnknownFieldSet}s rather than {@link Message}s
* since the group's type is presumably unknown.
*/
public List<UnknownFieldSet> getGroupList() { return group; }
@Override
public boolean equals(final Object other) {
if (this == other) {
return true;
}
if (!(other instanceof Field)) {
return false;
}
return Arrays.equals(getIdentityArray(),
((Field) other).getIdentityArray());
}
@Override
public int hashCode() {
return Arrays.hashCode(getIdentityArray());
}
/**
* Returns the array of objects to be used to uniquely identify this
* {@link Field} instance.
*/
private Object[] getIdentityArray() {
return new Object[] {
varint,
fixed32,
fixed64,
lengthDelimited,
group};
}
/**
* Serializes the field, including field number, and writes it to
* {@code output}.
*/
public void writeTo(final int fieldNumber, final CodedOutputStream output)
throws IOException {
for (final long value : varint) {
output.writeUInt64(fieldNumber, value);
}
for (final int value : fixed32) {
output.writeFixed32(fieldNumber, value);
}
for (final long value : fixed64) {
output.writeFixed64(fieldNumber, value);
}
for (final ByteString value : lengthDelimited) {
output.writeBytes(fieldNumber, value);
}
for (final UnknownFieldSet value : group) {
output.writeGroup(fieldNumber, value);
}
}
/**
* Get the number of bytes required to encode this field, including field
* number.
*/
public int getSerializedSize(final int fieldNumber) {
int result = 0;
for (final long value : varint) {
result += CodedOutputStream.computeUInt64Size(fieldNumber, value);
}
for (final int value : fixed32) {
result += CodedOutputStream.computeFixed32Size(fieldNumber, value);
}
for (final long value : fixed64) {
result += CodedOutputStream.computeFixed64Size(fieldNumber, value);
}
for (final ByteString value : lengthDelimited) {
result += CodedOutputStream.computeBytesSize(fieldNumber, value);
}
for (final UnknownFieldSet value : group) {
result += CodedOutputStream.computeGroupSize(fieldNumber, value);
}
return result;
}
/**
* Serializes the field, including field number, and writes it to
* {@code output}, using {@code MessageSet} wire format.
*/
public void writeAsMessageSetExtensionTo(
final int fieldNumber,
final CodedOutputStream output)
throws IOException {
for (final ByteString value : lengthDelimited) {
output.writeRawMessageSetExtension(fieldNumber, value);
}
}
/**
* Get the number of bytes required to encode this field, including field
* number, using {@code MessageSet} wire format.
*/
public int getSerializedSizeAsMessageSetExtension(final int fieldNumber) {
int result = 0;
for (final ByteString value : lengthDelimited) {
result += CodedOutputStream.computeRawMessageSetExtensionSize(
fieldNumber, value);
}
return result;
}
private List<Long> varint;
private List<Integer> fixed32;
private List<Long> fixed64;
private List<ByteString> lengthDelimited;
private List<UnknownFieldSet> group;
/**
* Used to build a {@link Field} within an {@link UnknownFieldSet}.
*
* <p>Use {@link Field#newBuilder()} to construct a {@code Builder}.
*/
public static final class Builder {
// This constructor should never be called directly (except from 'create').
private Builder() {}
private static Builder create() {
Builder builder = new Builder();
builder.result = new Field();
return builder;
}
private Field result;
/**
* Build the field. After {@code build()} has been called, the
* {@code Builder} is no longer usable. Calling any other method will
* result in undefined behavior and can cause a
* {@code NullPointerException} to be thrown.
*/
public Field build() {
if (result.varint == null) {
result.varint = Collections.emptyList();
} else {
result.varint = Collections.unmodifiableList(result.varint);
}
if (result.fixed32 == null) {
result.fixed32 = Collections.emptyList();
} else {
result.fixed32 = Collections.unmodifiableList(result.fixed32);
}
if (result.fixed64 == null) {
result.fixed64 = Collections.emptyList();
} else {
result.fixed64 = Collections.unmodifiableList(result.fixed64);
}
if (result.lengthDelimited == null) {
result.lengthDelimited = Collections.emptyList();
} else {
result.lengthDelimited =
Collections.unmodifiableList(result.lengthDelimited);
}
if (result.group == null) {
result.group = Collections.emptyList();
} else {
result.group = Collections.unmodifiableList(result.group);
}
final Field returnMe = result;
result = null;
return returnMe;
}
/** Discard the field's contents. */
public Builder clear() {
result = new Field();
return this;
}
/**
* Merge the values in {@code other} into this field. For each list
* of values, {@code other}'s values are append to the ones in this
* field.
*/
public Builder mergeFrom(final Field other) {
if (!other.varint.isEmpty()) {
if (result.varint == null) {
result.varint = new ArrayList<Long>();
}
result.varint.addAll(other.varint);
}
if (!other.fixed32.isEmpty()) {
if (result.fixed32 == null) {
result.fixed32 = new ArrayList<Integer>();
}
result.fixed32.addAll(other.fixed32);
}
if (!other.fixed64.isEmpty()) {
if (result.fixed64 == null) {
result.fixed64 = new ArrayList<Long>();
}
result.fixed64.addAll(other.fixed64);
}
if (!other.lengthDelimited.isEmpty()) {
if (result.lengthDelimited == null) {
result.lengthDelimited = new ArrayList<ByteString>();
}
result.lengthDelimited.addAll(other.lengthDelimited);
}
if (!other.group.isEmpty()) {
if (result.group == null) {
result.group = new ArrayList<UnknownFieldSet>();
}
result.group.addAll(other.group);
}
return this;
}
/** Add a varint value. */
public Builder addVarint(final long value) {
if (result.varint == null) {
result.varint = new ArrayList<Long>();
}
result.varint.add(value);
return this;
}
/** Add a fixed32 value. */
public Builder addFixed32(final int value) {
if (result.fixed32 == null) {
result.fixed32 = new ArrayList<Integer>();
}
result.fixed32.add(value);
return this;
}
/** Add a fixed64 value. */
public Builder addFixed64(final long value) {
if (result.fixed64 == null) {
result.fixed64 = new ArrayList<Long>();
}
result.fixed64.add(value);
return this;
}
/** Add a length-delimited value. */
public Builder addLengthDelimited(final ByteString value) {
if (result.lengthDelimited == null) {
result.lengthDelimited = new ArrayList<ByteString>();
}
result.lengthDelimited.add(value);
return this;
}
/** Add an embedded group. */
public Builder addGroup(final UnknownFieldSet value) {
if (result.group == null) {
result.group = new ArrayList<UnknownFieldSet>();
}
result.group.add(value);
return this;
}
}
}
}

View file

@ -0,0 +1,166 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// http://code.google.com/p/protobuf/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package com.google.protobuf;
/**
* This class is used internally by the Protocol Buffer library and generated
* message implementations. It is public only because those generated messages
* do not reside in the {@code protobuf} package. Others should not use this
* class directly.
*
* This class contains constants and helper functions useful for dealing with
* the Protocol Buffer wire format.
*
* @author kenton@google.com Kenton Varda
*/
public final class WireFormat {
// Do not allow instantiation.
private WireFormat() {}
static final int WIRETYPE_VARINT = 0;
static final int WIRETYPE_FIXED64 = 1;
static final int WIRETYPE_LENGTH_DELIMITED = 2;
static final int WIRETYPE_START_GROUP = 3;
static final int WIRETYPE_END_GROUP = 4;
static final int WIRETYPE_FIXED32 = 5;
// Osmand Delta change
public static final int WIRETYPE_FIXED32_LENGTH_DELIMITED = 6;
static final int TAG_TYPE_BITS = 3;
static final int TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1;
/** Given a tag value, determines the wire type (the lower 3 bits). */
public static int getTagWireType(final int tag) {
return tag & TAG_TYPE_MASK;
}
/** Given a tag value, determines the field number (the upper 29 bits). */
public static int getTagFieldNumber(final int tag) {
return tag >>> TAG_TYPE_BITS;
}
/** Makes a tag value given a field number and wire type. */
static int makeTag(final int fieldNumber, final int wireType) {
return (fieldNumber << TAG_TYPE_BITS) | wireType;
}
/**
* Lite equivalent to {@link Descriptors.FieldDescriptor.JavaType}. This is
* only here to support the lite runtime and should not be used by users.
*/
public enum JavaType {
INT(0),
LONG(0L),
FLOAT(0F),
DOUBLE(0D),
BOOLEAN(false),
STRING(""),
BYTE_STRING(ByteString.EMPTY),
ENUM(null),
MESSAGE(null);
JavaType(final Object defaultDefault) {
this.defaultDefault = defaultDefault;
}
/**
* The default default value for fields of this type, if it's a primitive
* type.
*/
Object getDefaultDefault() {
return defaultDefault;
}
private final Object defaultDefault;
}
/**
* Lite equivalent to {@link Descriptors.FieldDescriptor.Type}. This is
* only here to support the lite runtime and should not be used by users.
*/
public enum FieldType {
DOUBLE (JavaType.DOUBLE , WIRETYPE_FIXED64 ),
FLOAT (JavaType.FLOAT , WIRETYPE_FIXED32 ),
INT64 (JavaType.LONG , WIRETYPE_VARINT ),
UINT64 (JavaType.LONG , WIRETYPE_VARINT ),
INT32 (JavaType.INT , WIRETYPE_VARINT ),
FIXED64 (JavaType.LONG , WIRETYPE_FIXED64 ),
FIXED32 (JavaType.INT , WIRETYPE_FIXED32 ),
BOOL (JavaType.BOOLEAN , WIRETYPE_VARINT ),
// OSMAND FIX no methods override for enum
STRING (JavaType.STRING , WIRETYPE_LENGTH_DELIMITED, false),
GROUP (JavaType.MESSAGE , WIRETYPE_START_GROUP , false),
MESSAGE (JavaType.MESSAGE , WIRETYPE_LENGTH_DELIMITED , false),
BYTES (JavaType.BYTE_STRING, WIRETYPE_LENGTH_DELIMITED , false),
UINT32 (JavaType.INT , WIRETYPE_VARINT ),
ENUM (JavaType.ENUM , WIRETYPE_VARINT ),
SFIXED32(JavaType.INT , WIRETYPE_FIXED32 ),
SFIXED64(JavaType.LONG , WIRETYPE_FIXED64 ),
SINT32 (JavaType.INT , WIRETYPE_VARINT ),
SINT64 (JavaType.LONG , WIRETYPE_VARINT );
FieldType(final JavaType javaType, final int wireType) {
this.javaType = javaType;
this.wireType = wireType;
this.packable = true;
}
FieldType(final JavaType javaType, final int wireType, boolean packable) {
this.javaType = javaType;
this.wireType = wireType;
this.packable = packable;
}
private final JavaType javaType;
private final int wireType;
private final boolean packable;
public JavaType getJavaType() { return javaType; }
public int getWireType() { return wireType; }
public boolean isPackable() { return true; }
}
// Field numbers for feilds in MessageSet wire format.
static final int MESSAGE_SET_ITEM = 1;
static final int MESSAGE_SET_TYPE_ID = 2;
static final int MESSAGE_SET_MESSAGE = 3;
// Tag numbers.
static final int MESSAGE_SET_ITEM_TAG =
makeTag(MESSAGE_SET_ITEM, WIRETYPE_START_GROUP);
static final int MESSAGE_SET_ITEM_END_TAG =
makeTag(MESSAGE_SET_ITEM, WIRETYPE_END_GROUP);
static final int MESSAGE_SET_TYPE_ID_TAG =
makeTag(MESSAGE_SET_TYPE_ID, WIRETYPE_VARINT);
static final int MESSAGE_SET_MESSAGE_TAG =
makeTag(MESSAGE_SET_MESSAGE, WIRETYPE_LENGTH_DELIMITED);
}

View file

@ -0,0 +1,18 @@
package net.osmand;
/**
* That class is designed to be common interface for callbacks with one param
* @author victor
* @param <T>
*/
public interface CallbackWithObject<T> {
/**
* Calls on processing result
* @param result could be null depends on usage
* @return processed or not
*/
public boolean processResult(T result);
}

View file

@ -0,0 +1,11 @@
package net.osmand;
/**
* Wrapper of java.text. Collator
*/
public interface Collator extends java.util.Comparator<Object>, Cloneable{
public boolean equals(String source, String target);
public abstract int compare(String source, String target);
}

View file

@ -0,0 +1,129 @@
package net.osmand;
/**
* Abstract collator matcher that basically supports subclasses with some collator
* matching.
*
* @author pavol.zibrita
*/
public class CollatorStringMatcher implements StringMatcher {
private final Collator collator;
private final StringMatcherMode mode;
private final String part;
public static enum StringMatcherMode {
CHECK_ONLY_STARTS_WITH,
CHECK_STARTS_FROM_SPACE,
CHECK_STARTS_FROM_SPACE_NOT_BEGINNING,
CHECK_CONTAINS
}
public CollatorStringMatcher(String part, StringMatcherMode mode) {
this.collator = PlatformUtil.primaryCollator();
this.part = part;
this.mode = mode;
}
public Collator getCollator() {
return collator;
}
@Override
public boolean matches(String name) {
return cmatches(collator, name, part, mode);
}
public static boolean cmatches(Collator collator, String base, String part, StringMatcherMode mode){
switch (mode) {
case CHECK_CONTAINS:
return ccontains(collator, base, part);
case CHECK_STARTS_FROM_SPACE:
return cstartsWith(collator, base, part, true, true);
case CHECK_STARTS_FROM_SPACE_NOT_BEGINNING:
return cstartsWith(collator, base, part, false, true);
case CHECK_ONLY_STARTS_WITH:
return cstartsWith(collator, base, part, true, false);
}
return false;
}
/**
* Check if part contains in base
*
* @param collator Collator to use
* @param part String to search
* @param base String where to search
* @return true if part is contained in base
*/
public static boolean ccontains(Collator collator, String base, String part) {
int pos = 0;
if (part.length() > 3) {
// improve searching by searching first 3 characters
pos = cindexOf(collator, pos, part.substring(0, 3), base);
if (pos == -1) {
return false;
}
}
pos = cindexOf(collator, pos, part, base);
if (pos == -1) {
return false;
}
return true;
}
private static int cindexOf(Collator collator, int start, String part, String base) {
for (int pos = start; pos <= base.length() - part.length(); pos++) {
if (collator.equals(base.substring(pos, pos + part.length()), part)) {
return pos;
}
}
return -1;
}
/**
* Checks if string starts with another string.
* Special check try to find as well in the middle of name
*
* @param collator
* @param searchIn
* @param theStart
* @return true if searchIn starts with token
*/
public static boolean cstartsWith(Collator collator, String searchIn, String theStart,
boolean checkBeginning, boolean checkSpaces) {
int startLength = theStart.length();
int searchInLength = searchIn.length();
if (startLength == 0) {
return true;
}
if (startLength > searchInLength) {
return false;
}
// simulate starts with for collator
if (checkBeginning) {
boolean starts = collator.equals(searchIn.substring(0, startLength), theStart);
if (starts) {
return true;
}
}
if (checkSpaces) {
for (int i = 1; i <= searchInLength - startLength; i++) {
if (isSpace(searchIn.charAt(i - 1)) && !isSpace(searchIn.charAt(i))) {
if (collator.equals(searchIn.substring(i, i + startLength), theStart)) {
return true;
}
}
}
}
return false;
}
private static boolean isSpace(char c){
return !Character.isLetter(c) && !Character.isDigit(c);
}
}

View file

@ -0,0 +1,38 @@
package net.osmand;
/**
* This class can be replaced with more effective on ARM devices
*/
public class FloatMath {
public static float PI = (float) Math.PI;
public static float cos(float a) {
return (float) Math.cos(a);
}
public static float sin(float a) {
return (float) Math.sin(a);
}
public static float abs(float a) {
return (float) Math.abs(a);
}
public static float atan2(float py, float px) {
return (float) Math.atan2(py, px);
}
public static float sqrt(float f) {
return (float) Math.sqrt(f);
}
public static float max(float a, float b) {
if(a > b) {
return a;
}
return b;
}
}

View file

@ -0,0 +1,77 @@
package net.osmand;
import java.io.EOFException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import org.apache.commons.logging.Log;
public class GeoidAltitudeCorrection {
private final Log log = PlatformUtil.getLog(GeoidAltitudeCorrection.class);
private File f;
private RandomAccessFile rf;
private int cachedPointer = -1;
private short cachedValue = 0;
public GeoidAltitudeCorrection(File dir) {
String[] fnames = dir.list();
if(fnames != null) {
String fn = null;
for(String f : fnames) {
if(f.contains("WW15MGH")) {
fn = f;
break;
}
}
if (fn != null) {
this.f = new File(dir, fn);
if (f.exists()) {
try {
rf = new RandomAccessFile(f.getPath(), "r");
} catch (FileNotFoundException e) {
log.error("Error", e);
}
}
}
}
}
public boolean isGeoidInformationAvailable(){
return rf != null;
}
public float getGeoidHeight(double lat, double lon) {
if (!isGeoidInformationAvailable()) {
return 0;
}
int shy = (int) Math.floor((90 - lat) * 4);
int shx = (int) Math.floor((lon >= 0 ? lon : lon + 360) * 4);
int pointer = ((shy * 1440) + shx) * 2;
short res = 0;
if (pointer != cachedPointer) {
try {
rf.seek(pointer);
// read short
cachedValue = readShort();
cachedPointer = pointer;
} catch (IOException e) {
log.error("Geoid info error", e);
}
}
res = cachedValue;
return res / 100f;
}
private short readShort() throws IOException {
byte[] b = new byte[2];
rf.read(b);
int ch1 = b[0] < 0 ? b[0] + 256 : b[0];
int ch2 = b[1] < 0 ? b[1] + 256 : b[1];
return (short)((ch1 << 8) + (ch2 << 0));
}
}

View file

@ -0,0 +1,57 @@
package net.osmand;
/**
* That common interface that could be used by background operations.
* Implementation of it depends on chosen UI platform
*/
public interface IProgress {
/**
* @param taskName
* @param work - -1 means that indeterminate task,
* otherwise number of could be specified
*/
public void startTask(String taskName, int work);
public void startWork(int work);
public void progress(int deltaWork);
public void remaining(int remainingWork);
public void setGeneralProgress(String genProgress);
public void finishTask();
public boolean isIndeterminate();
public boolean isInterrupted();
public IProgress EMPTY_PROGRESS = new IProgress() {
@Override
public void startWork(int work) {}
@Override
public void startTask(String taskName, int work) {}
@Override
public void setGeneralProgress(String genProgress) {}
@Override
public void remaining(int remainingWork) {}
@Override
public void progress(int deltaWork) {}
@Override
public boolean isInterrupted() {return false;}
@Override
public boolean isIndeterminate() {return false;}
@Override
public void finishTask() {}
};
}

View file

@ -0,0 +1,48 @@
package net.osmand;
public class IndexConstants {
// Important : Every time you change schema of db upgrade version!!!
// If you want that new application support old index : put upgrade code in android app ResourceManager
public final static int POI_TABLE_VERSION = 1;
public final static int BINARY_MAP_VERSION = 2; // starts with 1
public final static int VOICE_VERSION = 0; //supported download versions
public final static int TTSVOICE_VERSION = 1; //supported download versions
public static final String POI_INDEX_EXT = ".poi.odb"; //$NON-NLS-1$
public static final String BINARY_MAP_INDEX_EXT = ".obf"; //$NON-NLS-1$
public static final String GEN_LOG_EXT = ".gen.log"; //$NON-NLS-1$
public static final String POI_INDEX_EXT_ZIP = ".poi.zip"; //$NON-NLS-1$
public static final String VOICE_INDEX_EXT_ZIP = ".voice.zip"; //$NON-NLS-1$
public static final String TTSVOICE_INDEX_EXT_ZIP = ".ttsvoice.zip"; //$NON-NLS-1$
public static final String ANYVOICE_INDEX_EXT_ZIP = "voice.zip"; //$NON-NLS-1$ //to cactch both voices, .voice.zip and .ttsvoice.zip
public static final String BINARY_MAP_INDEX_EXT_ZIP = ".obf.zip"; //$NON-NLS-1$
public static final String EXTRA_ZIP_EXT = ".extra.zip";
public static final String EXTRA_EXT = ".extra";
public static final String RENDERER_INDEX_EXT = ".render.xml"; //$NON-NLS-1$
public final static String POI_TABLE = "poi"; //$NON-NLS-1$
public static final String INDEX_DOWNLOAD_DOMAIN = "download.osmand.net";
public static final String APP_DIR = "osmand/"; //$NON-NLS-1$
public static final String MAPS_PATH = "";
public static final String BACKUP_INDEX_DIR= "backup/";
public static final String GPX_INDEX_DIR= "tracks/";
public static final String TILES_INDEX_DIR= "tiles/";
public static final String SRTM_INDEX_DIR = "srtm/"; //$NON-NLS-1$
public static final String AV_INDEX_DIR = "avnotes/"; //$NON-NLS-1$
public static final String VOICE_INDEX_DIR = "voice/"; //$NON-NLS-1$
public static final String RENDERERS_DIR = "rendering/"; //$NON-NLS-1$
public static final String ROUTING_XML_FILE= "routing.xml";
public static final String SQLITE_EXT = ".sqlitedb"; //$NON-NLS-1$
public static final String TEMP_SOURCE_TO_LOAD = "temp";
}

View file

@ -0,0 +1,514 @@
package net.osmand;
/*
* Copyright (C) 2007 The Android Open Source Project
*
* 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.
*/
/**
* A class representing a geographic location sensed at a particular
* time (a "fix"). A location consists of a latitude and longitude, a
* UTC timestamp. and optionally information on altitude, speed, and
* bearing.
*
* <p> Information specific to a particular provider or class of
* providers may be communicated to the application using getExtras,
* which returns a Bundle of key/value pairs. Each provider will only
* provide those entries for which information is available.
*/
public class Location {
private String mProvider;
private long mTime = 0;
private double mLatitude = 0.0;
private double mLongitude = 0.0;
private boolean mHasAltitude = false;
private double mAltitude = 0.0f;
private boolean mHasSpeed = false;
private float mSpeed = 0.0f;
private boolean mHasBearing = false;
private float mBearing = 0.0f;
private boolean mHasAccuracy = false;
private float mAccuracy = 0.0f;
// Cache the inputs and outputs of computeDistanceAndBearing
// so calls to distanceTo() and bearingTo() can share work
private double mLat1 = 0.0;
private double mLon1 = 0.0;
private double mLat2 = 0.0;
private double mLon2 = 0.0;
private float mDistance = 0.0f;
private float mInitialBearing = 0.0f;
// Scratchpad
private float[] mResults = new float[2];
/**
* Constructs a new Location. By default, time, latitude,
* longitude, and numSatellites are 0; hasAltitude, hasSpeed, and
* hasBearing are false; and there is no extra information.
*
* @param provider the name of the location provider that generated this
* location fix.
*/
public Location(String provider) {
mProvider = provider;
}
/**
* Constructs a new Location object that is a copy of the given
* location.
*/
public Location(Location l) {
set(l);
}
/**
* Sets the contents of the location to the values from the given location.
*/
public void set(Location l) {
mProvider = l.mProvider;
mTime = l.mTime;
mLatitude = l.mLatitude;
mLongitude = l.mLongitude;
mHasAltitude = l.mHasAltitude;
mAltitude = l.mAltitude;
mHasSpeed = l.mHasSpeed;
mSpeed = l.mSpeed;
mHasBearing = l.mHasBearing;
mBearing = l.mBearing;
mHasAccuracy = l.mHasAccuracy;
mAccuracy = l.mAccuracy;
}
/**
* Clears the contents of the location.
*/
public void reset() {
mProvider = null;
mTime = 0;
mLatitude = 0;
mLongitude = 0;
mHasAltitude = false;
mAltitude = 0;
mHasSpeed = false;
mSpeed = 0;
mHasBearing = false;
mBearing = 0;
mHasAccuracy = false;
mAccuracy = 0;
}
private static void computeDistanceAndBearing(double lat1, double lon1,
double lat2, double lon2, float[] results) {
// Based on http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
// using the "Inverse Formula" (section 4)
int MAXITERS = 20;
// Convert lat/long to radians
lat1 *= Math.PI / 180.0;
lat2 *= Math.PI / 180.0;
lon1 *= Math.PI / 180.0;
lon2 *= Math.PI / 180.0;
double a = 6378137.0; // WGS84 major axis
double b = 6356752.3142; // WGS84 semi-major axis
double f = (a - b) / a;
double aSqMinusBSqOverBSq = (a * a - b * b) / (b * b);
double L = lon2 - lon1;
double A = 0.0;
double U1 = Math.atan((1.0 - f) * Math.tan(lat1));
double U2 = Math.atan((1.0 - f) * Math.tan(lat2));
double cosU1 = Math.cos(U1);
double cosU2 = Math.cos(U2);
double sinU1 = Math.sin(U1);
double sinU2 = Math.sin(U2);
double cosU1cosU2 = cosU1 * cosU2;
double sinU1sinU2 = sinU1 * sinU2;
double sigma = 0.0;
double deltaSigma = 0.0;
double cosSqAlpha = 0.0;
double cos2SM = 0.0;
double cosSigma = 0.0;
double sinSigma = 0.0;
double cosLambda = 0.0;
double sinLambda = 0.0;
double lambda = L; // initial guess
for (int iter = 0; iter < MAXITERS; iter++) {
double lambdaOrig = lambda;
cosLambda = Math.cos(lambda);
sinLambda = Math.sin(lambda);
double t1 = cosU2 * sinLambda;
double t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
double sinSqSigma = t1 * t1 + t2 * t2; // (14)
sinSigma = Math.sqrt(sinSqSigma);
cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda; // (15)
sigma = Math.atan2(sinSigma, cosSigma); // (16)
double sinAlpha = (sinSigma == 0) ? 0.0 :
cosU1cosU2 * sinLambda / sinSigma; // (17)
cosSqAlpha = 1.0 - sinAlpha * sinAlpha;
cos2SM = (cosSqAlpha == 0) ? 0.0 :
cosSigma - 2.0 * sinU1sinU2 / cosSqAlpha; // (18)
double uSquared = cosSqAlpha * aSqMinusBSqOverBSq; // defn
A = 1 + (uSquared / 16384.0) * // (3)
(4096.0 + uSquared *
(-768 + uSquared * (320.0 - 175.0 * uSquared)));
double B = (uSquared / 1024.0) * // (4)
(256.0 + uSquared *
(-128.0 + uSquared * (74.0 - 47.0 * uSquared)));
double C = (f / 16.0) *
cosSqAlpha *
(4.0 + f * (4.0 - 3.0 * cosSqAlpha)); // (10)
double cos2SMSq = cos2SM * cos2SM;
deltaSigma = B * sinSigma * // (6)
(cos2SM + (B / 4.0) *
(cosSigma * (-1.0 + 2.0 * cos2SMSq) -
(B / 6.0) * cos2SM *
(-3.0 + 4.0 * sinSigma * sinSigma) *
(-3.0 + 4.0 * cos2SMSq)));
lambda = L +
(1.0 - C) * f * sinAlpha *
(sigma + C * sinSigma *
(cos2SM + C * cosSigma *
(-1.0 + 2.0 * cos2SM * cos2SM))); // (11)
double delta = (lambda - lambdaOrig) / lambda;
if (Math.abs(delta) < 1.0e-12) {
break;
}
}
float distance = (float) (b * A * (sigma - deltaSigma));
results[0] = distance;
if (results.length > 1) {
float initialBearing = (float) Math.atan2(cosU2 * sinLambda,
cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
initialBearing *= 180.0 / Math.PI;
results[1] = initialBearing;
if (results.length > 2) {
float finalBearing = (float) Math.atan2(cosU1 * sinLambda,
-sinU1 * cosU2 + cosU1 * sinU2 * cosLambda);
finalBearing *= 180.0 / Math.PI;
results[2] = finalBearing;
}
}
}
/**
* Computes the approximate distance in meters between two
* locations, and optionally the initial and final bearings of the
* shortest path between them. Distance and bearing are defined using the
* WGS84 ellipsoid.
*
* <p> The computed distance is stored in results[0]. If results has length
* 2 or greater, the initial bearing is stored in results[1]. If results has
* length 3 or greater, the final bearing is stored in results[2].
*
* @param startLatitude the starting latitude
* @param startLongitude the starting longitude
* @param endLatitude the ending latitude
* @param endLongitude the ending longitude
* @param results an array of floats to hold the results
*
* @throws IllegalArgumentException if results is null or has length < 1
*/
public static void distanceBetween(double startLatitude, double startLongitude,
double endLatitude, double endLongitude, float[] results) {
if (results == null || results.length < 1) {
throw new IllegalArgumentException("results is null or has length < 1");
}
computeDistanceAndBearing(startLatitude, startLongitude,
endLatitude, endLongitude, results);
}
/**
* Returns the approximate distance in meters between this
* location and the given location. Distance is defined using
* the WGS84 ellipsoid.
*
* @param dest the destination location
* @return the approximate distance in meters
*/
public float distanceTo(Location dest) {
// See if we already have the result
synchronized (mResults) {
if (mLatitude != mLat1 || mLongitude != mLon1 ||
dest.mLatitude != mLat2 || dest.mLongitude != mLon2) {
computeDistanceAndBearing(mLatitude, mLongitude,
dest.mLatitude, dest.mLongitude, mResults);
mLat1 = mLatitude;
mLon1 = mLongitude;
mLat2 = dest.mLatitude;
mLon2 = dest.mLongitude;
mDistance = mResults[0];
mInitialBearing = mResults[1];
}
return mDistance;
}
}
/**
* Returns the approximate initial bearing in degrees East of true
* North when traveling along the shortest path between this
* location and the given location. The shortest path is defined
* using the WGS84 ellipsoid. Locations that are (nearly)
* antipodal may produce meaningless results.
*
* @param dest the destination location
* @return the initial bearing in degrees
*/
public float bearingTo(Location dest) {
synchronized (mResults) {
// See if we already have the result
if (mLatitude != mLat1 || mLongitude != mLon1 ||
dest.mLatitude != mLat2 || dest.mLongitude != mLon2) {
computeDistanceAndBearing(mLatitude, mLongitude,
dest.mLatitude, dest.mLongitude, mResults);
mLat1 = mLatitude;
mLon1 = mLongitude;
mLat2 = dest.mLatitude;
mLon2 = dest.mLongitude;
mDistance = mResults[0];
mInitialBearing = mResults[1];
}
return mInitialBearing;
}
}
/**
* Returns the name of the provider that generated this fix,
* or null if it is not associated with a provider.
*/
public String getProvider() {
return mProvider;
}
/**
* Sets the name of the provider that generated this fix.
*/
public void setProvider(String provider) {
mProvider = provider;
}
/**
* Returns the UTC time of this fix, in milliseconds since January 1,
* 1970.
*/
public long getTime() {
return mTime;
}
/**
* Sets the UTC time of this fix, in milliseconds since January 1,
* 1970.
*/
public void setTime(long time) {
mTime = time;
}
/**
* Returns the latitude of this fix.
*/
public double getLatitude() {
return mLatitude;
}
/**
* Sets the latitude of this fix.
*/
public void setLatitude(double latitude) {
mLatitude = latitude;
}
/**
* Returns the longitude of this fix.
*/
public double getLongitude() {
return mLongitude;
}
/**
* Sets the longitude of this fix.
*/
public void setLongitude(double longitude) {
mLongitude = longitude;
}
/**
* Returns true if this fix contains altitude information, false
* otherwise.
*/
public boolean hasAltitude() {
return mHasAltitude;
}
/**
* Returns the altitude of this fix. If {@link #hasAltitude} is false,
* 0.0f is returned.
*/
public double getAltitude() {
return mAltitude;
}
/**
* Sets the altitude of this fix. Following this call,
* hasAltitude() will return true.
*/
public void setAltitude(double altitude) {
mAltitude = altitude;
mHasAltitude = true;
}
/**
* Clears the altitude of this fix. Following this call,
* hasAltitude() will return false.
*/
public void removeAltitude() {
mAltitude = 0.0f;
mHasAltitude = false;
}
/**
* Returns true if this fix contains speed information, false
* otherwise. The default implementation returns false.
*/
public boolean hasSpeed() {
return mHasSpeed;
}
/**
* Returns the speed of the device over ground in meters/second.
* If hasSpeed() is false, 0.0f is returned.
*/
public float getSpeed() {
return mSpeed;
}
/**
* Sets the speed of this fix, in meters/second. Following this
* call, hasSpeed() will return true.
*/
public void setSpeed(float speed) {
mSpeed = speed;
mHasSpeed = true;
}
/**
* Clears the speed of this fix. Following this call, hasSpeed()
* will return false.
*/
public void removeSpeed() {
mSpeed = 0.0f;
mHasSpeed = false;
}
/**
* Returns true if the provider is able to report bearing information,
* false otherwise. The default implementation returns false.
*/
public boolean hasBearing() {
return mHasBearing;
}
/**
* Returns the direction of travel in degrees East of true
* North. If hasBearing() is false, 0.0 is returned.
*/
public float getBearing() {
return mBearing;
}
/**
* Sets the bearing of this fix. Following this call, hasBearing()
* will return true.
*/
public void setBearing(float bearing) {
while (bearing < 0.0f) {
bearing += 360.0f;
}
while (bearing >= 360.0f) {
bearing -= 360.0f;
}
mBearing = bearing;
mHasBearing = true;
}
/**
* Clears the bearing of this fix. Following this call, hasBearing()
* will return false.
*/
public void removeBearing() {
mBearing = 0.0f;
mHasBearing = false;
}
/**
* Returns true if the provider is able to report accuracy information,
* false otherwise. The default implementation returns false.
*/
public boolean hasAccuracy() {
return mHasAccuracy;
}
/**
* Returns the accuracy of the fix in meters. If hasAccuracy() is false,
* 0.0 is returned.
*/
public float getAccuracy() {
return mAccuracy;
}
/**
* Sets the accuracy of this fix. Following this call, hasAccuracy()
* will return true.
*/
public void setAccuracy(float accuracy) {
mAccuracy = accuracy;
mHasAccuracy = true;
}
/**
* Clears the accuracy of this fix. Following this call, hasAccuracy()
* will return false.
*/
public void removeAccuracy() {
mAccuracy = 0.0f;
mHasAccuracy = false;
}
@Override public String toString() {
return "Location[mProvider=" + mProvider +
",mTime=" + mTime +
",mLatitude=" + mLatitude +
",mLongitude=" + mLongitude +
",mHasAltitude=" + mHasAltitude +
",mAltitude=" + mAltitude +
",mHasSpeed=" + mHasSpeed +
",mSpeed=" + mSpeed +
",mHasBearing=" + mHasBearing +
",mBearing=" + mBearing +
",mHasAccuracy=" + mHasAccuracy +
",mAccuracy=" + mAccuracy;
}
}

View file

@ -0,0 +1,289 @@
package net.osmand;
import gnu.trove.list.array.TIntArrayList;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.logging.Log;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteSubregion;
import net.osmand.binary.RouteDataObject;
import net.osmand.render.RenderingRuleSearchRequest;
import net.osmand.render.RenderingRulesStorage;
import net.osmand.router.GeneralRouter;
import net.osmand.router.RouteCalculationProgress;
import net.osmand.router.RouteSegmentResult;
import net.osmand.router.RoutingConfiguration;
public class NativeLibrary {
public static class RenderingGenerationResult {
public RenderingGenerationResult(ByteBuffer bitmap) {
bitmapBuffer = bitmap;
}
public final ByteBuffer bitmapBuffer;
}
public static class NativeSearchResult {
public long nativeHandler;
private NativeSearchResult(long nativeHandler) {
this.nativeHandler = nativeHandler;
}
@Override
protected void finalize() throws Throwable {
deleteNativeResult();
super.finalize();
}
public void deleteNativeResult() {
if (nativeHandler != 0) {
deleteSearchResult(nativeHandler);
nativeHandler = 0;
}
}
}
public static class NativeRouteSearchResult {
public long nativeHandler;
public RouteDataObject[] objects;
public RouteSubregion region;
public NativeRouteSearchResult(long nativeHandler, RouteDataObject[] objects) {
this.nativeHandler = nativeHandler;
this.objects = objects;
}
@Override
protected void finalize() throws Throwable {
deleteNativeResult();
super.finalize();
}
public void deleteNativeResult() {
if (nativeHandler != 0) {
deleteRouteSearchResult(nativeHandler);
nativeHandler = 0;
}
}
}
/**
* @param searchResultHandle
* - must be null if there is no need to append to previous results returns native handle to results
*/
public NativeSearchResult searchObjectsForRendering(int sleft, int sright, int stop, int sbottom, int zoom,
RenderingRuleSearchRequest request, boolean skipDuplicates, Object objectWithInterruptedField, String msgIfNothingFound) {
int renderRouteDataFile = 0;
if (request.searchRenderingAttribute("showRoadMapsAttribute")) {
renderRouteDataFile = request.getIntPropertyValue(request.ALL.R_ATTR_INT_VALUE);
}
return new NativeSearchResult(searchNativeObjectsForRendering(sleft, sright, stop, sbottom, zoom, request, skipDuplicates,
renderRouteDataFile, objectWithInterruptedField, msgIfNothingFound));
}
public RouteDataObject[] getDataObjects(NativeRouteSearchResult rs, int x31, int y31) {
if (rs.nativeHandler == 0) {
// do not throw exception because it is expected situation
return new RouteDataObject[0];
}
return getRouteDataObjects(rs.region.routeReg, rs.nativeHandler, x31, y31);
}
public boolean initMapFile(String filePath) {
return initBinaryMapFile(filePath);
}
public boolean initCacheMapFile(String filePath) {
return initCacheMapFiles(filePath);
}
public boolean closeMapFile(String filePath) {
return closeBinaryMapFile(filePath);
}
public RouteSegmentResult[] runNativeRouting(int sx31, int sy31, int ex31, int ey31, RoutingConfiguration config,
RouteRegion[] regions, RouteCalculationProgress progress) {
TIntArrayList state = new TIntArrayList();
List<String> keys = new ArrayList<String>();
List<String> values = new ArrayList<String>();
GeneralRouter r = (GeneralRouter) config.router;
fillObjects(state, keys, values, 0, r.highwaySpeed);
fillObjects(state, keys, values, 1, r.highwayPriorities);
fillObjects(state, keys, values, 2, r.avoid);
fillObjects(state, keys, values, 3, r.obstacles);
fillObjects(state, keys, values, 4, r.routingObstacles);
LinkedHashMap<String, String> attrs = new LinkedHashMap<String, String>(config.attributes);
attrs.putAll(r.attributes);
fillObjects(state, keys, values, 5, attrs);
return nativeRouting(new int[] { sx31, sy31, ex31, ey31 }, state.toArray(), keys.toArray(new String[keys.size()]),
values.toArray(new String[values.size()]), config.initialDirection == null ? -360 : config.initialDirection.floatValue(),
regions, progress);
}
public <T> void fillObjects(TIntArrayList state, List<String> keys, List<String> values, int s, Map<String, T> map) {
Iterator<Entry<String, T>> it = map.entrySet().iterator();
while (it.hasNext()) {
Entry<String, T> n = it.next();
state.add(s);
keys.add(n.getKey());
values.add(n.getValue() + "");
}
}
public NativeRouteSearchResult loadRouteRegion(RouteSubregion sub, boolean loadObjects) {
NativeRouteSearchResult lr = loadRoutingData(sub.routeReg, sub.routeReg.getName(), sub.routeReg.getFilePointer(), sub, loadObjects);
if (lr != null && lr.nativeHandler != 0) {
lr.region = sub;
}
return lr;
}
/**/
protected static native NativeRouteSearchResult loadRoutingData(RouteRegion reg, String regName, int regfp, RouteSubregion subreg,
boolean loadObjects);
protected static native void deleteRouteSearchResult(long searchResultHandle);
protected static native RouteDataObject[] getRouteDataObjects(RouteRegion reg, long rs, int x31, int y31);
protected static native RouteSegmentResult[] nativeRouting(int[] coordinates, int[] state, String[] keyConfig, String[] valueConfig,
float initDirection, RouteRegion[] regions, RouteCalculationProgress progress);
protected static native void deleteSearchResult(long searchResultHandle);
protected static native boolean initBinaryMapFile(String filePath);
protected static native boolean initCacheMapFiles(String filePath);
protected static native boolean closeBinaryMapFile(String filePath);
protected static native void initRenderingRulesStorage(RenderingRulesStorage storage);
protected static native RenderingGenerationResult generateRenderingIndirect(RenderingContext rc, long searchResultHandler,
boolean isTransparent, RenderingRuleSearchRequest render, boolean encodePng);
protected static native long searchNativeObjectsForRendering(int sleft, int sright, int stop, int sbottom, int zoom,
RenderingRuleSearchRequest request, boolean skipDuplicates, int renderRouteDataFile, Object objectWithInterruptedField,
String msgIfNothingFound);
public static native void testRoutingPing();
public static native int testNativeRouting(String obfPath, double sLat, double sLon, double eLat, double eLon);
/**/
// Empty native impl
/*
* protected static NativeRouteSearchResult loadRoutingData(RouteRegion reg, String regName, int regfp,RouteSubregion subreg, boolean
* loadObjects) { return null;}
*
* protected static void deleteRouteSearchResult(long searchResultHandle) {}
*
* protected static RouteDataObject[] getRouteDataObjects(RouteRegion reg, long rs, int x31, int y31){return null;}
*
* protected static RouteSegmentResult[] nativeRouting(int[] coordinates, int[] state, String[] keyConfig, String[] valueConfig, float
* initDirection, RouteRegion[] regions, RouteCalculationProgress progress) {return null;}
*
* protected static void deleteSearchResult(long searchResultHandle) {}
*
* protected static boolean initBinaryMapFile(String filePath) {return false;}
*
* protected static boolean initCacheMapFiles(String filePath) {return false;}
*
* protected static boolean closeBinaryMapFile(String filePath) {return false;}
*
* protected static void initRenderingRulesStorage(RenderingRulesStorage storage) {}
*
*
* protected static RenderingGenerationResult generateRenderingIndirect(RenderingContext rc, long searchResultHandler, boolean
* isTransparent, RenderingRuleSearchRequest render, boolean encodePng) { return null; }
*
* protected static long searchNativeObjectsForRendering(int sleft, int sright, int stop, int sbottom, int zoom,
* RenderingRuleSearchRequest request, boolean skipDuplicates, int renderRouteDataFile, Object objectWithInterruptedField, String
* msgIfNothingFound) { return 0; }
*
* public static void testRoutingPing() {}
*
* public static int testNativeRouting(String obfPath, double sLat, double sLon, double eLat, double eLon) {return 0;} /*
*/
private static final Log log = PlatformUtil.getLog(NativeLibrary.class);
public static boolean loadAllLibs(String path) {
boolean b = true;
b &= load("Qt5Core", path);
b &= load("OsmAndCore", path);
b &= load("OsmAndCoreUtils", path);
b &= load("OsmAndJNI", path);
return b;
}
public static boolean load(String libBaseName, String path) {
// look for a pre-installed library
if (path != null) {
try {
System.load(path + libBaseName);
return true;
} catch (UnsatisfiedLinkError e) {
log.error(e);
} // fall through
}
// guess what a bundled library would be called
String osname = System.getProperty("os.name").toLowerCase();
String osarch = System.getProperty("os.arch");
if (osname.startsWith("mac os")) {
osname = "mac";
osarch = "universal";
}
if (osname.startsWith("windows"))
osname = "win";
if (osname.startsWith("sunos"))
osname = "solaris";
if (osarch.startsWith("i") && osarch.endsWith("86"))
osarch = "x86";
String libname = libBaseName + "-" + osname + '-' + osarch + ".lib";
// try a bundled library
try {
ClassLoader cl = NativeLibrary.class.getClassLoader();
InputStream in = cl.getResourceAsStream( libname);
if (in == null) {
log.error("libname: " + libname + " not found");
return false;
}
File tmplib = File.createTempFile(libBaseName + "-", ".lib");
tmplib.deleteOnExit();
OutputStream out = new FileOutputStream(tmplib);
byte[] buf = new byte[1024];
for (int len; (len = in.read(buf)) != -1;)
out.write(buf, 0, len);
in.close();
out.close();
System.load(tmplib.getAbsolutePath());
return true;
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage(), e);
} catch (UnsatisfiedLinkError e) {
log.error(e.getMessage(), e);
} // fall through
return false;
}
}

View file

@ -0,0 +1,88 @@
package net.osmand;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
/**
* That class is replacing of standard LogFactory due to
* problems with Android implementation of LogFactory.
* See Android analog of LogUtil
*
* That class should be very simple & always use LogFactory methods,
* there is an intention to delegate all static methods to LogFactory.
*/
public class PlatformUtil {
public static boolean AVIAN_LIBRARY = false;
public static Log getLog(Class<?> cl){
return LogFactory.getLog(cl);
}
public static XmlPullParser newXMLPullParser() throws XmlPullParserException{
return new org.kxml2.io.KXmlParser();
}
// AVIAN missing dependency
/*public static net.osmand.Collator primaryCollator(){
return new net.osmand.Collator() {
@Override
public int compare(Object o1, Object o2) {
return compare(o1+"", o2+"");
}
@Override
public boolean equals(String source, String target) {
if(source == null) {
return source == target;
}
return source.equals(target);
}
@Override
public int compare(String source, String target) {
if(source == null) {
return source == target ? 0 : -1;
}
return source.compareTo(target);
}
};
}*/
public static net.osmand.Collator primaryCollator(){
final java.text.Collator instance = java.text.Collator.getInstance();
instance.setStrength(java.text.Collator.PRIMARY);
return wrapCollator(instance);
}
public static net.osmand.Collator wrapCollator(final java.text.Collator instance) {
return new net.osmand.Collator() {
@Override
public int compare(Object o1, Object o2) {
return instance.compare(o1, o2);
}
@Override
public boolean equals(Object obj) {
return instance.equals(obj);
}
@Override
public boolean equals(String source, String target) {
return instance.equals(source, target);
}
@Override
public int compare(String source, String target) {
return instance.compare(source, target);
}
};
}
}

View file

@ -0,0 +1,69 @@
package net.osmand;
public class RenderingContext {
static enum ShadowRenderingMode {
// int shadowRenderingMode = 0; // no shadow (minumum CPU)
// int shadowRenderingMode = 1; // classic shadow (the implementaton in master)
// int shadowRenderingMode = 2; // blur shadow (most CPU, but still reasonable)
// int shadowRenderingMode = 3; solid border (CPU use like classic version or even smaller)
NO_SHADOW(0), ONE_STEP(1), BLUR_SHADOW(2), SOLID_SHADOW(3);
public final int value;
ShadowRenderingMode(int v) {
this.value = v;
}
}
// FIELDS OF THAT CLASS ARE USED IN C++
public boolean interrupted = false;
public boolean nightMode = false;
public boolean useEnglishNames = false;
public int defaultColor = 0xf1eee8;
public RenderingContext() {
}
public float leftX;
public float topY;
public int width;
public int height;
public int zoom;
public float tileDivisor;
public float rotate;
// debug purpose
public int pointCount = 0;
public int pointInsideCount = 0;
public int visible = 0;
public int allObjects = 0;
public int textRenderingTime = 0;
public int lastRenderedKey = 0;
// be aware field is using in C++
public int shadowRenderingMode = ShadowRenderingMode.BLUR_SHADOW.value;
public int shadowRenderingColor = 0xff969696;
public String renderingDebugInfo;
private float density = 1;
public void setDensityValue(boolean highResMode, float mapTextSize, float density) {
// boolean highResMode = false;
// float mapTextSize = 1;
if (highResMode && density > 1) {
this.density = density * mapTextSize;
} else {
this.density = mapTextSize;
}
}
public float getDensityValue(float val) {
return val * density;
}
protected byte[] getIconRawData(String data) {
return null;
}
}

View file

@ -0,0 +1,88 @@
package net.osmand;
import org.apache.commons.logging.Log;
import com.ibm.icu.text.ArabicShaping;
import com.ibm.icu.text.ArabicShapingException;
import com.ibm.icu.text.Bidi;
import com.ibm.icu.text.BidiRun;
public class Reshaper {
private final static Log LOG = PlatformUtil.getLog(Reshaper.class);
public static String reshape(String s) {
// if(true) {
// return s;
// }
try {
ArabicShaping as = new ArabicShaping(ArabicShaping.LETTERS_SHAPE | ArabicShaping.LENGTH_GROW_SHRINK);
try {
s = as.shape(s);
} catch (ArabicShapingException e) {
LOG.error(e.getMessage(), e);
}
Bidi line = new Bidi(s.length(), s.length());
line.setPara(s, Bidi.LEVEL_DEFAULT_LTR, null);
byte direction = line.getDirection();
if (direction != Bidi.MIXED) {
// unidirectional
if(line.isLeftToRight()) {
return s;
} else {
char[] chs = new char[s.length()];
for(int i = 0; i< chs.length ; i++) {
chs[i] = s.charAt(chs.length - i - 1);
}
return new String(chs);
}
} else {
// // mixed-directional
int count = line.countRuns();
// if (styleRunCount <= 1) {
// int style = styleRuns[0].style;
// // iterate over directional runs
// for (i = 0; i < count; ++i) {
// run = line.getVisualRun(i);
// renderRun(text, run.getStart(), run.getLimit(),
// run.getDirection(), style);
// }
// }
StringBuilder res = new StringBuilder();
// iterate over both directional and style runs
for (int i = 0; i < count; ++i) {
BidiRun run = line.getVisualRun(i);
int st = run.getStart();
int e = run.getLimit();
int j = run.getDirection() == Bidi.LTR ? st : e - 1;
int l = run.getDirection() == Bidi.LTR ? e : st - 1;
boolean plus = run.getDirection() == Bidi.LTR;
while (j != l) {
res.append(s.charAt(j));
if (plus) {
j++;
} else {
j--;
}
}
}
return res.toString();
}
} catch (RuntimeException e) {
LOG.error(e.getMessage(), e);
return s;
}
}
/*
public static void main(String[] args) {
// char[] c = new char[] {'א', 'ד','ם', ' ', '1', '2'} ;
// String reshape = "אדם";
char[] c = new char[] {'א', 'ד','ם'} ;
String reshape = reshape(new String(c));
for(int i=0; i < reshape.length(); i++) {
System.out.println(reshape.charAt(i));
}
}*/
}

View file

@ -0,0 +1,20 @@
package net.osmand;
/**
* Easy matcher to be able to publish results immediately
*
*/
public interface ResultMatcher<T> {
/**
* @param name
* @return true if result should be added to final list
*/
boolean publish(T object);
/**
* @returns true to stop processing
*/
boolean isCancelled();
}

View file

@ -0,0 +1,10 @@
package net.osmand;
/**
* Abstract listener represents state changed for a particular object
*/
public interface StateChangedListener<T> {
void stateChanged(T change);
}

View file

@ -0,0 +1,17 @@
package net.osmand;
/**
* Easy matcher to be able to filter streets,buildings, etc.. using custom
* rules
*
* @author pavol.zibrita
*/
public interface StringMatcher {
/**
* @param name
* @return true if this matcher matches the <code>name</code> String
*/
boolean matches(String name);
}

View file

@ -0,0 +1,32 @@
package net.osmand.binary;
public class BinaryIndexPart {
String name;
int length;
int filePointer;
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int getFilePointer() {
return filePointer;
}
public void setFilePointer(int filePointer) {
this.filePointer = filePointer;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View file

@ -0,0 +1,667 @@
package net.osmand.binary;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.map.hash.TIntObjectHashMap;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapAddressReaderAdapter.AddressRegion;
import net.osmand.binary.BinaryMapAddressReaderAdapter.CitiesBlock;
import net.osmand.binary.BinaryMapIndexReader.MapIndex;
import net.osmand.binary.BinaryMapIndexReader.MapRoot;
import net.osmand.binary.BinaryMapIndexReader.SearchFilter;
import net.osmand.binary.BinaryMapIndexReader.SearchPoiTypeFilter;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.BinaryMapIndexReader.TagValuePair;
import net.osmand.binary.BinaryMapPoiReaderAdapter.PoiRegion;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
import net.osmand.binary.BinaryMapTransportReaderAdapter.TransportIndex;
import net.osmand.data.Amenity;
import net.osmand.data.AmenityType;
import net.osmand.data.Building;
import net.osmand.data.City;
import net.osmand.data.MapObject;
import net.osmand.data.Street;
import net.osmand.util.MapUtils;
import com.google.protobuf.CodedOutputStream;
import com.google.protobuf.WireFormat;
public class BinaryInspector {
public static final int BUFFER_SIZE = 1 << 20;
public static void main(String[] args) throws IOException {
inspector(args);
// test cases show info
// inspector(new String[]{"-vaddress", "-vcities", "-vstreets", "-bbox=14.4,50.1,14.5,50.01", "/home/victor/projects/OsmAnd/data/osm-gen/Map.obf"});
// test case extract parts
// test case
}
private static void println(String s) {
System.out.println(s);
}
private static void print(String s) {
System.out.print(s);
}
protected static class VerboseInfo {
boolean vaddress;
boolean vcities;
boolean vstreetgroups;
boolean vstreets;
boolean vbuildings;
boolean vintersections;
boolean vtransport;
boolean vpoi;
boolean vmap;
boolean vmapObjects;
double lattop = 85;
double latbottom = -85;
double lonleft = -180;
double lonright = 180;
int zoom = -1;
public boolean isVaddress() {
return vaddress;
}
public int getZoom() {
return zoom;
}
public boolean isVmap() {
return vmap;
}
public boolean isVpoi() {
return vpoi;
}
public boolean isVtransport() {
return vtransport;
}
public VerboseInfo(String[] params){
for(int i=0;i<params.length;i++){
if(params[i].equals("-vaddress")){
vaddress = true;
} else if(params[i].equals("-vstreets")){
vstreets = true;
} else if(params[i].equals("-vstreetgroups")){
vstreetgroups = true;
} else if(params[i].equals("-vcities")){
vcities = true;
} else if(params[i].equals("-vbuildings")){
vbuildings = true;
} else if(params[i].equals("-vintersections")){
vintersections = true;
} else if(params[i].equals("-vmap")){
vmap = true;
} else if(params[i].equals("-vmapobjects")){
vmapObjects = true;
} else if(params[i].equals("-vpoi")){
vpoi = true;
} else if(params[i].equals("-vtransport")){
vtransport = true;
} else if(params[i].startsWith("-zoom=")){
zoom = Integer.parseInt(params[i].substring("-zoom=".length()));
} else if(params[i].startsWith("-bbox=")){
String[] values = params[i].substring("-bbox=".length()).split(",");
lonleft = Double.parseDouble(values[0]);
lattop = Double.parseDouble(values[1]);
lonright = Double.parseDouble(values[2]);
latbottom = Double.parseDouble(values[3]);
}
}
}
public boolean contains(MapObject o){
return lattop >= o.getLocation().getLatitude() && latbottom <= o.getLocation().getLatitude()
&& lonleft <= o.getLocation().getLongitude() && lonright >= o.getLocation().getLongitude();
}
}
public static void inspector(String[] args) throws IOException {
if(args == null || args.length == 0){
printUsage(null);
return;
}
String f = args[0];
if (f.charAt(0) == '-') {
// command
if (f.equals("-c") || f.equals("-combine")) {
if (args.length < 4) {
printUsage("Too few parameters to extract (require minimum 4)");
} else {
Map<File, String> parts = new HashMap<File, String>();
for (int i = 2; i < args.length; i++) {
File file = new File(args[i]);
if (!file.exists()) {
System.err.println("File to extract from doesn't exist " + args[i]);
return;
}
parts.put(file, null);
if (i < args.length - 1) {
if (args[i + 1].startsWith("-") || args[i + 1].startsWith("+")) {
parts.put(file, args[i + 1]);
i++;
}
}
}
List<Float> extracted = combineParts(new File(args[1]), parts);
if (extracted != null) {
println("\n" + extracted.size() + " parts were successfully extracted to " + args[1]);
}
}
} else if (f.startsWith("-v")) {
if (args.length < 2) {
printUsage("Missing file parameter");
} else {
VerboseInfo vinfo = new VerboseInfo(args);
printFileInformation(args[args.length - 1], vinfo);
}
} else {
printUsage("Unknown command : " + f);
}
} else {
printFileInformation(f, null);
}
}
public static final void writeInt(CodedOutputStream ous, int v) throws IOException {
ous.writeRawByte((v >>> 24) & 0xFF);
ous.writeRawByte((v >>> 16) & 0xFF);
ous.writeRawByte((v >>> 8) & 0xFF);
ous.writeRawByte((v >>> 0) & 0xFF);
//written += 4;
}
@SuppressWarnings("unchecked")
public static List<Float> combineParts(File fileToExtract, Map<File, String> partsToExtractFrom) throws IOException {
BinaryMapIndexReader[] indexes = new BinaryMapIndexReader[partsToExtractFrom.size()];
RandomAccessFile[] rafs = new RandomAccessFile[partsToExtractFrom.size()];
LinkedHashSet<Float>[] partsSet = new LinkedHashSet[partsToExtractFrom.size()];
int c = 0;
Set<String> addressNames = new LinkedHashSet<String>();
int version = -1;
// Go through all files and validate conistency
for(File f : partsToExtractFrom.keySet()){
if(f.getAbsolutePath().equals(fileToExtract.getAbsolutePath())){
System.err.println("Error : Input file is equal to output file " + f.getAbsolutePath());
return null;
}
rafs[c] = new RandomAccessFile(f.getAbsolutePath(), "r");
indexes[c] = new BinaryMapIndexReader(rafs[c]);
partsSet[c] = new LinkedHashSet<Float>();
if(version == -1){
version = indexes[c].getVersion();
} else {
if(indexes[c].getVersion() != version){
System.err.println("Error : Different input files has different input versions " + indexes[c].getVersion() + " != " + version);
return null;
}
}
LinkedHashSet<Float> temp = new LinkedHashSet<Float>();
String pattern = partsToExtractFrom.get(f);
boolean minus = true;
for (int i = 0; i < indexes[c].getIndexes().size(); i++) {
partsSet[c].add(new Float(i + 1f));
BinaryIndexPart part = indexes[c].getIndexes().get(i);
if(part instanceof MapIndex){
List<MapRoot> roots = ((MapIndex) part).getRoots();
int rsize = roots.size();
for(int j=0; j<rsize; j++){
partsSet[c].add(new Float((i+1f)+(j+1)/10f));
}
}
}
if(pattern != null){
minus = pattern.startsWith("-");
String[] split = pattern.substring(1).split(",");
for(String s : split){
temp.add(Float.valueOf(s));
}
}
Iterator<Float> p = partsSet[c].iterator();
while (p.hasNext()) {
Float part = p.next();
if (minus) {
if (temp.contains(part)) {
p.remove();
}
} else {
if (!temp.contains(part)) {
p.remove();
}
}
}
c++;
}
// write files
FileOutputStream fout = new FileOutputStream(fileToExtract);
CodedOutputStream ous = CodedOutputStream.newInstance(fout, BUFFER_SIZE);
List<Float> list = new ArrayList<Float>();
byte[] BUFFER_TO_READ = new byte[BUFFER_SIZE];
ous.writeInt32(OsmandOdb.OsmAndStructure.VERSION_FIELD_NUMBER, version);
ous.writeInt64(OsmandOdb.OsmAndStructure.DATECREATED_FIELD_NUMBER, System.currentTimeMillis());
for (int k = 0; k < indexes.length; k++) {
LinkedHashSet<Float> partSet = partsSet[k];
BinaryMapIndexReader index = indexes[k];
RandomAccessFile raf = rafs[k];
for (int i = 0; i < index.getIndexes().size(); i++) {
if (!partSet.contains(Float.valueOf(i + 1f))) {
continue;
}
list.add(new Float(i + 1f));
BinaryIndexPart part = index.getIndexes().get(i);
String map;
if (part instanceof MapIndex) {
ous.writeTag(OsmandOdb.OsmAndStructure.MAPINDEX_FIELD_NUMBER, WireFormat.WIRETYPE_FIXED32_LENGTH_DELIMITED);
map = "Map";
} else if (part instanceof AddressRegion) {
ous.writeTag(OsmandOdb.OsmAndStructure.ADDRESSINDEX_FIELD_NUMBER, WireFormat.WIRETYPE_FIXED32_LENGTH_DELIMITED);
map = "Address";
if (addressNames.contains(part.getName())) {
System.err.println("Error : going to merge 2 addresses with same names. Skip " + part.getName());
continue;
}
addressNames.add(part.getName());
} else if (part instanceof TransportIndex) {
ous.writeTag(OsmandOdb.OsmAndStructure.TRANSPORTINDEX_FIELD_NUMBER, WireFormat.WIRETYPE_FIXED32_LENGTH_DELIMITED);
map = "Transport";
} else if (part instanceof PoiRegion) {
ous.writeTag(OsmandOdb.OsmAndStructure.POIINDEX_FIELD_NUMBER, WireFormat.WIRETYPE_FIXED32_LENGTH_DELIMITED);
map = "POI";
} else if (part instanceof RouteRegion) {
ous.writeTag(OsmandOdb.OsmAndStructure.ROUTINGINDEX_FIELD_NUMBER, WireFormat.WIRETYPE_FIXED32_LENGTH_DELIMITED);
map = "Routing";
} else {
throw new UnsupportedOperationException();
}
writeInt(ous, part.getLength());
copyBinaryPart(ous, BUFFER_TO_READ, raf, part.getFilePointer(), part.getLength());
println(MessageFormat.format("{2} part {0} is extracted {1} bytes",
new Object[]{part.getName(), part.getLength(), map}));
}
}
ous.writeInt32(OsmandOdb.OsmAndStructure.VERSIONCONFIRM_FIELD_NUMBER, version);
ous.flush();
fout.close();
return list;
}
private static void copyBinaryPart(CodedOutputStream ous, byte[] BUFFER, RandomAccessFile raf, long fp, int length)
throws IOException {
raf.seek(fp);
int toRead = length;
while (toRead > 0) {
int read = raf.read(BUFFER);
if (read == -1) {
throw new IllegalArgumentException("Unexpected end of file");
}
if (toRead < read) {
read = toRead;
}
ous.writeRawBytes(BUFFER, 0, read);
toRead -= read;
}
}
protected static String formatBounds(int left, int right, int top, int bottom){
double l = MapUtils.get31LongitudeX(left);
double r = MapUtils.get31LongitudeX(right);
double t = MapUtils.get31LatitudeY(top);
double b = MapUtils.get31LatitudeY(bottom);
return formatLatBounds(l, r, t, b);
}
protected static String formatLatBounds(double l, double r, double t, double b){
MessageFormat format = new MessageFormat("(left top - right bottom) : {0,number,#.####}, {1,number,#.####} NE - {2,number,#.####}, {3,number,#.####} NE", new Locale("EN", "US"));
return format.format(new Object[]{l, t, r, b});
}
public static void printFileInformation(String fileName,VerboseInfo verbose) throws IOException {
File file = new File(fileName);
if(!file.exists()){
println("Binary OsmAnd index " + fileName + " was not found.");
return;
}
printFileInformation(file,verbose);
}
public static void printFileInformation(File file, VerboseInfo verbose) throws IOException {
RandomAccessFile r = new RandomAccessFile(file.getAbsolutePath(), "r");
try {
BinaryMapIndexReader index = new BinaryMapIndexReader(r);
int i = 1;
println("Binary index " + file.getName() + " version = " + index.getVersion());
for(BinaryIndexPart p : index.getIndexes()){
String partname = "";
if(p instanceof MapIndex ){
partname = "Map";
} else if(p instanceof TransportIndex){
partname = "Transport";
} else if(p instanceof RouteRegion){
partname = "Routing";
} else if(p instanceof PoiRegion){
partname = "Poi";
} else if(p instanceof AddressRegion){
partname = "Address";
}
String name = p.getName() == null ? "" : p.getName();
println(MessageFormat.format("{0} {1} data {3} - {2,number,#} bytes",
new Object[]{Integer.valueOf(i), partname, p.getLength(), name}));
if(p instanceof TransportIndex){
TransportIndex ti = ((TransportIndex) p);
int sh = (31 - BinaryMapIndexReader.TRANSPORT_STOP_ZOOM);
println("\tBounds " + formatBounds(ti.getLeft() << sh, ti.getRight() << sh,
ti.getTop() << sh, ti.getBottom() << sh));
} else if(p instanceof RouteRegion){
RouteRegion ri = ((RouteRegion) p);
println("\tBounds " + formatLatBounds(ri.getLeftLongitude(), ri.getRightLongitude(),
ri.getTopLatitude(), ri.getBottomLatitude()));
} else if(p instanceof MapIndex){
MapIndex m = ((MapIndex) p);
int j = 1;
for(MapRoot mi : m.getRoots()){
println(MessageFormat.format("\t{4}.{5} Map level minZoom = {0}, maxZoom = {1}, size = {2,number,#} bytes \n\t\tBounds {3}",
new Object[] {
mi.getMinZoom(), mi.getMaxZoom(), mi.getLength(),
formatBounds(mi.getLeft(), mi.getRight(), mi.getTop(), mi.getBottom()),
i, j++}));
}
if((verbose != null && verbose.isVmap())){
printMapDetailInfo(verbose, index, m);
}
} else if(p instanceof PoiRegion && (verbose != null && verbose.isVpoi())){
printPOIDetailInfo(verbose, index, (PoiRegion) p);
} else if (p instanceof AddressRegion) {
List<CitiesBlock> cities = ((AddressRegion) p).cities;
for (CitiesBlock c : cities) {
println("\t" + i + "." + c.type + " Address part size=" + c.length + " bytes");
}
if (verbose != null && verbose.isVaddress()) {
printAddressDetailedInfo(verbose, index, (AddressRegion) p);
}
}
i++;
}
} catch (IOException e) {
System.err.println("File is not valid index : " + file.getAbsolutePath());
throw e;
}
}
private static void printAddressDetailedInfo(VerboseInfo verbose, BinaryMapIndexReader index, AddressRegion region) throws IOException {
String[] cityType_String = new String[] {
"Cities/Towns section",
"Villages section",
"Postcodes section",
};
int[] cityType = new int[] {
BinaryMapAddressReaderAdapter.CITY_TOWN_TYPE,
BinaryMapAddressReaderAdapter.VILLAGES_TYPE,
BinaryMapAddressReaderAdapter.POSTCODES_TYPE
};
for (int j = 0; j < cityType.length; j++) {
int type = cityType[j];
final List<City> cities = index.getCities(region, null, type);
print(MessageFormat.format("\t{0}, {1,number,#} group(s)", new Object[]{cityType_String[j], Integer.valueOf(cities.size())}));
if (BinaryMapAddressReaderAdapter.CITY_TOWN_TYPE == type) {
if (!verbose.vstreetgroups && !verbose.vcities) {
println("");
continue;
}
} else if (!verbose.vstreetgroups) {
println("");
continue;
}
println(":");
for (City c : cities) {
int size = index.preloadStreets(c, null);
List<Street> streets = new ArrayList<Street>(c.getStreets());
print(MessageFormat.format("\t\t''{0}'' [{1,number,#}], {2,number,#} street(s) size {3,number,#} bytes",
new Object[]{c.getEnName(), Long.valueOf(c.getId()), Integer.valueOf(streets.size()), Integer.valueOf(size)}));
if(!verbose.vstreets)
{
println("");
continue;
}
println(":");
if (!verbose.contains(c))
continue;
for (Street t : streets) {
if (!verbose.contains(t))
continue;
index.preloadBuildings(t, null);
final List<Building> buildings = t.getBuildings();
final List<Street> intersections = t.getIntersectedStreets();
println(MessageFormat.format("\t\t\t''{0}'' [{1,number,#}], {2,number,#} building(s), {3,number,#} intersections(s)",
new Object[]{t.getEnName(), Long.valueOf(t.getId()), Integer.valueOf(buildings.size()), Integer.valueOf(intersections.size())}));
if (buildings != null && !buildings.isEmpty() && verbose.vbuildings) {
println("\t\t\t\tBuildings:");
for (Building b : buildings) {
println(MessageFormat.format("\t\t\t\t{0} [{1,number,#}]",
new Object[]{b.getName(true), Long.valueOf(b.getId())}));
}
}
if (intersections != null && !intersections.isEmpty() && verbose.vintersections) {
print("\t\t\t\tIntersects with:");
for (Street s : intersections) {
println("\t\t\t\t\t" + s.getEnName());
}
}
}
}
}
}
private static class DamnCounter
{
int value;
}
private static void printMapDetailInfo(final VerboseInfo verbose, BinaryMapIndexReader index, MapIndex mapIndex) throws IOException {
final StringBuilder b = new StringBuilder();
final DamnCounter mapObjectsCounter = new DamnCounter();
SearchRequest<BinaryMapDataObject> req = BinaryMapIndexReader.buildSearchRequest(
MapUtils.get31TileNumberX(verbose.lonleft),
MapUtils.get31TileNumberX(verbose.lonright),
MapUtils.get31TileNumberY(verbose.lattop),
MapUtils.get31TileNumberY(verbose.latbottom),
verbose.getZoom(),
new SearchFilter() {
@Override
public boolean accept(TIntArrayList types, MapIndex index) {
return true;
}
},
new ResultMatcher<BinaryMapDataObject>() {
@Override
public boolean publish(BinaryMapDataObject obj) {
mapObjectsCounter.value++;
if(verbose.vmapObjects)
{
b.setLength(0);
boolean multipolygon = obj.getPolygonInnerCoordinates() != null && obj.getPolygonInnerCoordinates().length > 0;
if(multipolygon ) {
b.append("Multipolygon");
} else {
b.append(obj.area? "Area" : (obj.getPointsLength() > 1? "Way" : "Point"));
}
int[] types = obj.getTypes();
b.append(" types [");
for(int j = 0; j<types.length; j++){
if(j > 0) {
b.append(", ");
}
TagValuePair pair = obj.getMapIndex().decodeType(types[j]);
if(pair == null) {
System.err.println("Type " + types[j] + "was not found");
continue;
// throw new NullPointerException("Type " + obj.getAdditionalTypes()[j] + "was not found");
}
b.append(pair.toSimpleString()+" ("+types[j]+")");
}
b.append("]");
if(obj.getAdditionalTypes() != null && obj.getAdditionalTypes().length > 0){
b.append(" add_types [");
for(int j = 0; j<obj.getAdditionalTypes().length; j++){
if(j > 0) {
b.append(", ");
}
TagValuePair pair = obj.getMapIndex().decodeType(obj.getAdditionalTypes()[j]);
if(pair == null) {
System.err.println("Type " + obj.getAdditionalTypes()[j] + "was not found");
continue;
// throw new NullPointerException("Type " + obj.getAdditionalTypes()[j] + "was not found");
}
b.append(pair.toSimpleString()+"("+obj.getAdditionalTypes()[j]+")");
}
b.append("]");
}
TIntObjectHashMap<String> names = obj.getObjectNames();
if(names != null && !names.isEmpty()) {
b.append(" Names [");
int[] keys = names.keys();
for(int j = 0; j<keys.length; j++){
if(j > 0) {
b.append(", ");
}
TagValuePair pair = obj.getMapIndex().decodeType(keys[j]);
if(pair == null) {
throw new NullPointerException("Type " + keys[j] + "was not found");
}
b.append(pair.toSimpleString()+"("+keys[j]+")");
b.append(" - ").append(names.get(keys[j]));
}
b.append("]");
}
b.append(" id ").append((obj.getId() >> 1));
b.append(" lat/lon : ");
for(int i=0; i<obj.getPointsLength(); i++) {
float x = (float) MapUtils.get31LongitudeX(obj.getPoint31XTile(i));
float y = (float) MapUtils.get31LatitudeY(obj.getPoint31YTile(i));
b.append(x).append(" / ").append(y).append(" , ");
}
println(b.toString());
}
return false;
}
@Override
public boolean isCancelled() {
return false;
}
});
index.searchMapIndex(req, mapIndex);
println("\tTotal map objects: " + mapObjectsCounter.value);
}
private static void printPOIDetailInfo(VerboseInfo verbose, BinaryMapIndexReader index, PoiRegion p) throws IOException {
SearchRequest<Amenity> req = BinaryMapIndexReader.buildSearchPoiRequest(
MapUtils.get31TileNumberX(verbose.lonleft),
MapUtils.get31TileNumberX(verbose.lonright),
MapUtils.get31TileNumberY(verbose.lattop),
MapUtils.get31TileNumberY(verbose.latbottom),
verbose.getZoom(),
new SearchPoiTypeFilter() {
@Override
public boolean accept(AmenityType type, String subcategory) {
return true;
}
},
new ResultMatcher<Amenity>() {
@Override
public boolean publish(Amenity object) {
println(object.getType().toString() + " : " + object.getSubType() + " " + object.getEnName() + " " + object.getLocation() + " id=" + (object.getId() >> 1));
return false;
}
@Override
public boolean isCancelled() {
return false;
}
});
index.initCategories(p);
println("\tRegion: " + p.name);
println("\t\tBounds " + formatLatBounds(p.getLeftLongitude(), p.getRightLongitude(),
p.getTopLatitude(), p.getBottomLatitude()));
println("\t\tCategories:");
for(int i =0; i< p.categories.size(); i++) {
println("\t\t\t" + p.categories.get(i));
for(int j = 0; j < p.subcategories.get(i).size(); j++)
println("\t\t\t\t" + p.subcategories.get(i).get(j));
}
req.poiTypeFilter = null;//TODO: for test only
index.searchPoi(p, req);
}
public static void printUsage(String warning) {
if(warning != null){
println(warning);
}
println("Inspector is console utility for working with binary indexes of OsmAnd.");
println("It allows print info about file, extract parts and merge indexes.");
println("\nUsage for print info : inspector [-vaddress] [-vstreetgroups] [-vstreets] [-vbuildings] [-vintersections] [-vmap] [-vpoi] [-vtransport] [-zoom=Zoom] [-bbox=LeftLon,TopLat,RightLon,BottomLan] [file]");
println(" Prints information about [file] binary index of OsmAnd.");
println(" -v.. more verbouse output (like all cities and their streets or all map objects with tags/values and coordinates)");
println("\nUsage for combining indexes : inspector -c file_to_create (file_from_extract ((+|-)parts_to_extract)? )*");
println("\tCreate new file of extracted parts from input file. [parts_to_extract] could be parts to include or exclude.");
println(" Example : inspector -c output_file input_file +1,2,3\n\tExtracts 1, 2, 3 parts (could be find in print info)");
println(" Example : inspector -c output_file input_file -2,3\n\tExtracts all parts excluding 2, 3");
println(" Example : inspector -c output_file input_file1 input_file2 input_file3\n\tSimply combine 3 files");
println(" Example : inspector -c output_file input_file1 input_file2 -4\n\tCombine all parts of 1st file and all parts excluding 4th part of 2nd file");
}
}

View file

@ -0,0 +1,52 @@
package net.osmand.binary;
import java.io.IOException;
import net.osmand.NativeLibrary;
import net.osmand.bridge.ObfInspector;
import net.osmand.bridge.StringVector;
public class BinaryInspectorNative {
public static final int BUFFER_SIZE = 1 << 20;
public static void main(String[] args) throws IOException {
if(args == null || args.length == 0) {
printUsage(null);
return;
}
args = new String[]{"-vmap", "-bbox=11.3,47.1,11.6,47", "/home/victor/projects/OsmAnd/data/osm-gen/Austria_2.obf"};
// test cases show info
NativeLibrary.loadAllLibs(null);
StringVector vector = new StringVector();
for(int i = 0; i < args.length; i++) {
vector.add(args[i]);
}
ObfInspector.inspector(vector);
}
public static void printUsage(String warning) {
if(warning != null){
println(warning);
}
println("Inspector is console utility for working with binary indexes of OsmAnd.");
println("It allows print info about file, extract parts and merge indexes.");
println("\nUsage for print info : inspector [-vaddress] [-vstreetgroups] [-vstreets] [-vbuildings] [-vintersections] [-vmap] [-vpoi] [-vtransport] [-zoom=Zoom] [-bbox=LeftLon,TopLat,RightLon,BottomLan] [file]");
println(" Prints information about [file] binary index of OsmAnd.");
println(" -v.. more verbouse output (like all cities and their streets or all map objects with tags/values and coordinates)");
println("\nUsage for combining indexes : inspector -c file_to_create (file_from_extract ((+|-)parts_to_extract)? )*");
println("\tCreate new file of extracted parts from input file. [parts_to_extract] could be parts to include or exclude.");
println(" Example : inspector -c output_file input_file +1,2,3\n\tExtracts 1, 2, 3 parts (could be find in print info)");
println(" Example : inspector -c output_file input_file -2,3\n\tExtracts all parts excluding 2, 3");
println(" Example : inspector -c output_file input_file1 input_file2 input_file3\n\tSimply combine 3 files");
println(" Example : inspector -c output_file input_file1 input_file2 -4\n\tCombine all parts of 1st file and all parts excluding 4th part of 2nd file");
}
private static void println(String string) {
System.out.println(string);
}
}

View file

@ -0,0 +1,593 @@
package net.osmand.binary;
import gnu.trove.list.array.TIntArrayList;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import net.osmand.CollatorStringMatcher;
import net.osmand.CollatorStringMatcher.StringMatcherMode;
import net.osmand.PlatformUtil;
import net.osmand.StringMatcher;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.OsmandOdb.AddressNameIndexDataAtom;
import net.osmand.binary.OsmandOdb.OsmAndAddressIndex.CitiesIndex;
import net.osmand.binary.OsmandOdb.OsmAndAddressNameIndexData;
import net.osmand.binary.OsmandOdb.OsmAndAddressNameIndexData.AddressNameIndexData;
import net.osmand.data.Building;
import net.osmand.data.Building.BuildingInterpolation;
import net.osmand.data.City;
import net.osmand.data.City.CityType;
import net.osmand.data.LatLon;
import net.osmand.data.MapObject;
import net.osmand.data.Street;
import net.osmand.util.MapUtils;
import net.sf.junidecode.Junidecode;
import org.apache.commons.logging.Log;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.WireFormat;
public class BinaryMapAddressReaderAdapter {
public final static int CITY_TOWN_TYPE = 1;
public final static int POSTCODES_TYPE = 2;
public final static int VILLAGES_TYPE = 3;
public final static int STREET_TYPE = 4;
private static final Log LOG = PlatformUtil.getLog(BinaryMapAddressReaderAdapter.class);
public static class AddressRegion extends BinaryIndexPart {
String enName;
int indexNameOffset = -1;
List<CitiesBlock> cities = new ArrayList<BinaryMapAddressReaderAdapter.CitiesBlock>();
LatLon calculatedCenter = null;
public String getEnName() {
return enName;
}
public List<CitiesBlock> getCities() {
return cities;
}
public int getIndexNameOffset() {
return indexNameOffset;
}
}
public static class CitiesBlock extends BinaryIndexPart {
int type;
}
private CodedInputStream codedIS;
private final BinaryMapIndexReader map;
protected BinaryMapAddressReaderAdapter(BinaryMapIndexReader map){
this.codedIS = map.codedIS;
this.map = map;
}
private void skipUnknownField(int t) throws IOException {
map.skipUnknownField(t);
}
private int readInt() throws IOException {
return map.readInt();
}
protected void readAddressIndex(AddressRegion region) throws IOException {
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
if(region.enName == null || region.enName.length() == 0){
region.enName = Junidecode.unidecode(region.name);
}
return;
case OsmandOdb.OsmAndAddressIndex.NAME_FIELD_NUMBER :
region.name = codedIS.readString();
break;
case OsmandOdb.OsmAndAddressIndex.NAME_EN_FIELD_NUMBER :
region.enName = codedIS.readString();
break;
case OsmandOdb.OsmAndAddressIndex.CITIES_FIELD_NUMBER :
CitiesBlock block = new CitiesBlock();
region.cities.add(block);
block.type = 1;
block.length = readInt();
block.filePointer = codedIS.getTotalBytesRead();
while(true){
int tt = codedIS.readTag();
int ttag = WireFormat.getTagFieldNumber(tt);
if(ttag == 0) {
break;
} else if(ttag == CitiesIndex.TYPE_FIELD_NUMBER){
block.type = codedIS.readUInt32();
break;
} else {
skipUnknownField(tt);
}
}
codedIS.seek(block.filePointer + block.length);
break;
case OsmandOdb.OsmAndAddressIndex.NAMEINDEX_FIELD_NUMBER :
region.indexNameOffset = codedIS.getTotalBytesRead();
int length = readInt();
codedIS.seek(region.indexNameOffset + length + 4);
break;
default:
skipUnknownField(t);
break;
}
}
}
protected void readCities(List<City> cities, SearchRequest<City> resultMatcher, StringMatcher matcher, boolean useEn) throws IOException {
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case CitiesIndex.CITIES_FIELD_NUMBER :
int fp = codedIS.getTotalBytesRead();
int length = codedIS.readRawVarint32();
int oldLimit = codedIS.pushLimit(length);
City c = readCityHeader(matcher, fp, useEn);
if(c != null){
if (resultMatcher == null || resultMatcher.publish(c)) {
cities.add(c);
}
}
codedIS.popLimit(oldLimit);
if(resultMatcher != null && resultMatcher.isCancelled()){
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
}
break;
default:
skipUnknownField(t);
break;
}
}
}
protected void readCityStreets(SearchRequest<Street> resultMatcher, City city) throws IOException{
int x = MapUtils.get31TileNumberX(city.getLocation().getLongitude());
int y = MapUtils.get31TileNumberY(city.getLocation().getLatitude());
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case OsmandOdb.CityBlockIndex.STREETS_FIELD_NUMBER :
Street s = new Street(city);
s.setFileOffset(codedIS.getTotalBytesRead());
int length = codedIS.readRawVarint32();
int oldLimit = codedIS.pushLimit(length);
readStreet(s, null, false, x >> 7, y >> 7, city.isPostcode() ? city.getName() : null);
if(resultMatcher == null || resultMatcher.publish(s)){
city.registerStreet(s);
}
if(resultMatcher != null && resultMatcher.isCancelled()) {
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
}
codedIS.popLimit(oldLimit);
break;
case OsmandOdb.CityBlockIndex.BUILDINGS_FIELD_NUMBER :
// buildings for the town are not used now
skipUnknownField(t);
default:
skipUnknownField(t);
break;
}
}
}
protected City readCityHeader(StringMatcher nameMatcher, int filePointer, boolean useEn) throws IOException{
int x = 0;
int y = 0;
City c = null;
boolean englishNameMatched = false;
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return c;
case OsmandOdb.CityIndex.CITY_TYPE_FIELD_NUMBER :
int type = codedIS.readUInt32();
c = new City(CityType.values()[type]);
break;
case OsmandOdb.CityIndex.ID_FIELD_NUMBER :
c.setId(codedIS.readUInt64());
if(nameMatcher != null && useEn && !englishNameMatched){
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
return null;
}
break;
case OsmandOdb.CityIndex.NAME_EN_FIELD_NUMBER :
String enName = codedIS.readString();
if (nameMatcher != null && enName.length() > 0 && nameMatcher.matches(enName)) {
englishNameMatched = true;
}
c.setEnName(enName);
break;
case OsmandOdb.CityIndex.NAME_FIELD_NUMBER :
String name = codedIS.readString();
if(nameMatcher != null){
if(!useEn){
if(!nameMatcher.matches(name)) {
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
return null;
}
} else if(nameMatcher.matches(Junidecode.unidecode(name))){
englishNameMatched = true;
}
}
if(c == null) {
c = City.createPostcode(name);
}
c.setName(name);
break;
case OsmandOdb.CityIndex.X_FIELD_NUMBER :
x = codedIS.readUInt32();
break;
case OsmandOdb.CityIndex.Y_FIELD_NUMBER :
y = codedIS.readUInt32();
c.setLocation(MapUtils.get31LatitudeY(y), MapUtils.get31LongitudeX(x));
if(c.getEnName().length() == 0){
c.setEnName(Junidecode.unidecode(c.getName()));
}
break;
case OsmandOdb.CityIndex.SHIFTTOCITYBLOCKINDEX_FIELD_NUMBER :
int offset = readInt();
offset += filePointer;
c.setFileOffset(offset);
break;
default:
skipUnknownField(t);
break;
}
}
}
protected Street readStreet(Street s, SearchRequest<Building> buildingsMatcher, boolean loadBuildingsAndIntersected, int city24X, int city24Y, String postcodeFilter) throws IOException{
int x = 0;
int y = 0;
boolean loadLocation = city24X != 0 || city24Y != 0;
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
if(loadLocation){
s.setLocation(MapUtils.getLatitudeFromTile(24, y), MapUtils.getLongitudeFromTile(24, x));
}
if(s.getEnName().length() == 0){
s.setEnName(Junidecode.unidecode(s.getName()));
}
return s;
case OsmandOdb.StreetIndex.ID_FIELD_NUMBER :
s.setId(codedIS.readUInt64());
break;
case OsmandOdb.StreetIndex.NAME_EN_FIELD_NUMBER :
s.setEnName(codedIS.readString());
break;
case OsmandOdb.StreetIndex.NAME_FIELD_NUMBER :
s.setName(codedIS.readString());
break;
case OsmandOdb.StreetIndex.X_FIELD_NUMBER :
int sx = codedIS.readSInt32();
if(loadLocation){
x = sx + city24X;
} else {
x = (int) MapUtils.getTileNumberX(24, s.getLocation().getLongitude());
}
break;
case OsmandOdb.StreetIndex.Y_FIELD_NUMBER :
int sy = codedIS.readSInt32();
if(loadLocation){
y = sy + city24Y;
} else {
y = (int) MapUtils.getTileNumberY(24, s.getLocation().getLatitude());
}
break;
case OsmandOdb.StreetIndex.INTERSECTIONS_FIELD_NUMBER :
int length = codedIS.readRawVarint32();
if(loadBuildingsAndIntersected){
int oldLimit = codedIS.pushLimit(length);
Street si = readIntersectedStreet(s.getCity(), x, y);
s.addIntersectedStreet(si);
codedIS.popLimit(oldLimit);
} else {
codedIS.skipRawBytes(length);
}
break;
case OsmandOdb.StreetIndex.BUILDINGS_FIELD_NUMBER :
int offset = codedIS.getTotalBytesRead();
length = codedIS.readRawVarint32();
if(loadBuildingsAndIntersected){
int oldLimit = codedIS.pushLimit(length);
Building b = readBuilding(offset, x, y);
if (postcodeFilter == null || postcodeFilter.equalsIgnoreCase(b.getPostcode())) {
if (buildingsMatcher == null || buildingsMatcher.publish(b)) {
s.addBuilding(b);
}
}
codedIS.popLimit(oldLimit);
} else {
codedIS.skipRawBytes(length);
}
break;
default:
skipUnknownField(t);
break;
}
}
}
protected Street readIntersectedStreet(City c, int street24X, int street24Y) throws IOException{
int x = 0;
int y = 0;
Street s = new Street(c);
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
s.setLocation(MapUtils.getLatitudeFromTile(24, y), MapUtils.getLongitudeFromTile(24, x));
if(s.getEnName().length() == 0){
s.setEnName(Junidecode.unidecode(s.getName()));
}
return s;
case OsmandOdb.BuildingIndex.ID_FIELD_NUMBER :
s.setId(codedIS.readUInt64());
break;
case OsmandOdb.StreetIntersection.NAME_EN_FIELD_NUMBER:
s.setEnName(codedIS.readString());
break;
case OsmandOdb.StreetIntersection.NAME_FIELD_NUMBER:
s.setName(codedIS.readString());
break;
case OsmandOdb.StreetIntersection.INTERSECTEDX_FIELD_NUMBER :
x = codedIS.readSInt32() + street24X;
break;
case OsmandOdb.StreetIntersection.INTERSECTEDY_FIELD_NUMBER :
y = codedIS.readSInt32() + street24Y;
break;
default:
skipUnknownField(t);
break;
}
}
}
protected Building readBuilding(int fileOffset, int street24X, int street24Y) throws IOException{
int x = 0;
int y = 0;
int x2 = 0;
int y2 = 0;
Building b = new Building();
b.setFileOffset(fileOffset);
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
b.setLocation(MapUtils.getLatitudeFromTile(24, y), MapUtils.getLongitudeFromTile(24, x));
if(x2 != 0 && y2 != 0) {
b.setLatLon2(new LatLon(MapUtils.getLatitudeFromTile(24, y2), MapUtils.getLongitudeFromTile(24, x2)));
}
if(b.getEnName().length() == 0){
b.setEnName(Junidecode.unidecode(b.getName()));
}
return b;
case OsmandOdb.BuildingIndex.ID_FIELD_NUMBER :
b.setId(codedIS.readUInt64());
break;
case OsmandOdb.BuildingIndex.NAME_EN_FIELD_NUMBER :
b.setEnName(codedIS.readString());
break;
case OsmandOdb.BuildingIndex.NAME_FIELD_NUMBER :
b.setName(codedIS.readString());
break;
case OsmandOdb.BuildingIndex.NAME_EN2_FIELD_NUMBER :
// no where to set now
codedIS.readString();
break;
case OsmandOdb.BuildingIndex.NAME2_FIELD_NUMBER :
b.setName2(codedIS.readString());
break;
case OsmandOdb.BuildingIndex.INTERPOLATION_FIELD_NUMBER :
int sint = codedIS.readSInt32();
if(sint > 0) {
b.setInterpolationInterval(sint);
} else {
b.setInterpolationType(BuildingInterpolation.fromValue(sint));
}
break;
case OsmandOdb.BuildingIndex.X_FIELD_NUMBER :
x = codedIS.readSInt32() + street24X;
break;
case OsmandOdb.BuildingIndex.X2_FIELD_NUMBER :
x2 = codedIS.readSInt32() + street24X;
break;
case OsmandOdb.BuildingIndex.Y_FIELD_NUMBER :
y = codedIS.readSInt32() + street24Y;
break;
case OsmandOdb.BuildingIndex.Y2_FIELD_NUMBER :
y2 = codedIS.readSInt32() + street24Y;
break;
case OsmandOdb.BuildingIndex.POSTCODE_FIELD_NUMBER :
b.setPostcode(codedIS.readString());
break;
default:
skipUnknownField(t);
break;
}
}
}
public void searchAddressDataByName(AddressRegion reg, SearchRequest<MapObject> req, int[] typeFilter) throws IOException {
TIntArrayList loffsets = new TIntArrayList();
CollatorStringMatcher matcher = new CollatorStringMatcher( req.nameQuery, StringMatcherMode.CHECK_STARTS_FROM_SPACE);
long time = System.currentTimeMillis();
int indexOffset = 0;
while (true) {
if (req.isCancelled()) {
return;
}
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case OsmAndAddressNameIndexData.TABLE_FIELD_NUMBER:
int length = readInt();
indexOffset = codedIS.getTotalBytesRead();
int oldLimit = codedIS.pushLimit(length);
// here offsets are sorted by distance
map.readIndexedStringTable(matcher.getCollator(), req.nameQuery, "", loffsets, 0);
codedIS.popLimit(oldLimit);
break;
case OsmAndAddressNameIndexData.ATOM_FIELD_NUMBER:
// also offsets can be randomly skipped by limit
loffsets.sort();
TIntArrayList[] refs = new TIntArrayList[5];
for (int i = 0; i < refs.length; i++) {
refs[i] = new TIntArrayList();
}
LOG.info("Searched address structure in " + (System.currentTimeMillis() - time) + "ms. Found " + loffsets.size()
+ " subtress");
for (int j = 0; j < loffsets.size(); j++) {
int fp = indexOffset + loffsets.get(j);
codedIS.seek(fp);
int len = codedIS.readRawVarint32();
int oldLim = codedIS.pushLimit(len);
int stag = 0;
do {
int st = codedIS.readTag();
stag = WireFormat.getTagFieldNumber(st);
if(stag == AddressNameIndexData.ATOM_FIELD_NUMBER) {
int slen = codedIS.readRawVarint32();
int soldLim = codedIS.pushLimit(slen);
readAddressNameData(req, refs, fp);
codedIS.popLimit(soldLim);
} else if(stag != 0){
skipUnknownField(st);
}
} while(stag != 0);
codedIS.popLimit(oldLim);
if (req.isCancelled()) {
return;
}
}
if (typeFilter == null) {
typeFilter = new int[] { CITY_TOWN_TYPE, POSTCODES_TYPE, VILLAGES_TYPE, STREET_TYPE };
}
for (int i = 0; i < typeFilter.length && !req.isCancelled(); i++) {
TIntArrayList list = refs[typeFilter[i]];
if (typeFilter[i] == STREET_TYPE) {
for (int j = 0; j < list.size() && !req.isCancelled(); j += 2) {
City obj = null;
{
codedIS.seek(list.get(j + 1));
int len = codedIS.readRawVarint32();
int old = codedIS.pushLimit(len);
obj = readCityHeader(null, list.get(j + 1), false);
codedIS.popLimit(old);
}
if (obj != null) {
codedIS.seek(list.get(j));
int len = codedIS.readRawVarint32();
int old = codedIS.pushLimit(len);
LatLon l = obj.getLocation();
Street s = new Street(obj);
readStreet(s, null, false, MapUtils.get31TileNumberX(l.getLongitude()) >> 7,
MapUtils.get31TileNumberY(l.getLatitude()) >> 7, obj.isPostcode() ? obj.getName() : null);
if (matcher.matches(s.getName())) {
req.publish(s);
}
codedIS.popLimit(old);
}
}
} else {
list.sort();
for (int j = 0; j < list.size() && !req.isCancelled(); j++) {
codedIS.seek(list.get(j));
int len = codedIS.readRawVarint32();
int old = codedIS.pushLimit(len);
City obj = readCityHeader(matcher, list.get(j), false);
if (obj != null) {
req.publish(obj);
}
codedIS.popLimit(old);
}
}
}
LOG.info("Whole address search by name is done in " + (System.currentTimeMillis() - time) + "ms. Found "
+ req.getSearchResults().size());
return;
default:
skipUnknownField(t);
break;
}
}
}
private void readAddressNameData(SearchRequest<MapObject> req, TIntArrayList[] refs, int fp) throws IOException {
TIntArrayList toAdd = null;
while(true){
if(req.isCancelled()){
return;
}
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case AddressNameIndexDataAtom.NAMEEN_FIELD_NUMBER :
codedIS.readString();
break;
case AddressNameIndexDataAtom.NAME_FIELD_NUMBER :
codedIS.readString();
break;
case AddressNameIndexDataAtom.SHIFTTOCITYINDEX_FIELD_NUMBER :
if(toAdd != null) {
toAdd.add(fp - codedIS.readInt32());
}
break;
case AddressNameIndexDataAtom.SHIFTTOINDEX_FIELD_NUMBER :
if(toAdd != null) {
toAdd.add(fp - codedIS.readInt32());
}
break;
case AddressNameIndexDataAtom.TYPE_FIELD_NUMBER :
int type = codedIS.readInt32();
toAdd = refs[type];
break;
default:
skipUnknownField(t);
break;
}
}
}
}

View file

@ -0,0 +1,166 @@
package net.osmand.binary;
import gnu.trove.map.hash.TIntObjectHashMap;
import net.osmand.binary.BinaryMapIndexReader.MapIndex;
import net.osmand.render.RenderingRulesStorage;
public class BinaryMapDataObject {
protected int[] coordinates = null;
protected int[][] polygonInnerCoordinates = null;
protected boolean area = false;
protected int[] types = null;
protected int[] additionalTypes = null;
protected int objectType = RenderingRulesStorage.POINT_RULES;
protected TIntObjectHashMap<String> objectNames = null;
protected long id = 0;
protected MapIndex mapIndex = null;
public BinaryMapDataObject(){
}
public BinaryMapDataObject(int[] coordinates, int[] types, int[][] polygonInnerCoordinates, long id){
this.polygonInnerCoordinates = polygonInnerCoordinates;
this.coordinates = coordinates;
this.additionalTypes = new int[0];
this.types = types;
this.id = id;
}
protected void setCoordinates(int[] coordinates) {
this.coordinates = coordinates;
}
public String getName(){
if(objectNames == null){
return "";
}
String name = objectNames.get(mapIndex.nameEncodingType);
if(name == null){
return "";
}
return name;
}
public TIntObjectHashMap<String> getObjectNames() {
return objectNames;
}
public void putObjectName(int type, String name){
if(objectNames == null){
objectNames = new TIntObjectHashMap<String>();
}
objectNames.put(type, name);
}
public int[][] getPolygonInnerCoordinates() {
return polygonInnerCoordinates;
}
public int[] getTypes(){
return types;
}
public boolean containsType(int cachedType) {
if(cachedType != -1) {
for(int i=0; i<types.length; i++){
if(types[i] == cachedType) {
return true;
}
}
}
return false;
}
public boolean containsAdditionalType(int cachedType) {
if (cachedType != -1) {
for (int i = 0; i < additionalTypes.length; i++) {
if (additionalTypes[i] == cachedType) {
return true;
}
}
}
return false;
}
public String getNameByType(int type) {
if(type != -1 && objectNames != null) {
return objectNames.get(type);
}
return null;
}
public int[] getAdditionalTypes() {
return additionalTypes;
}
public boolean isArea() {
return area;
}
public boolean isCycle(){
if(coordinates == null || coordinates.length < 2) {
return false;
}
return coordinates[0] == coordinates[coordinates.length - 2] &&
coordinates[1] == coordinates[coordinates.length - 1];
}
public void setArea(boolean area) {
this.area = area;
}
public long getId() {
return id;
}
protected void setId(long id) {
this.id = id;
}
protected void setTypes(int[] types) {
this.types = types;
}
public int getSimpleLayer(){
if(mapIndex != null) {
for (int i = 0; i < additionalTypes.length; i++) {
if (mapIndex.positiveLayers.contains(additionalTypes[i])) {
return 1;
} else if (mapIndex.negativeLayers.contains(additionalTypes[i])) {
return -1;
}
}
}
return 0;
}
public MapIndex getMapIndex() {
return mapIndex;
}
public void setMapIndex(MapIndex mapIndex) {
this.mapIndex = mapIndex;
}
public int getPointsLength(){
if(coordinates == null){
return 0;
}
return coordinates.length / 2;
}
public int getPoint31YTile(int ind) {
return coordinates[2 * ind + 1];
}
public int getPoint31XTile(int ind) {
return coordinates[2 * ind];
}
}

View file

@ -0,0 +1,171 @@
package net.osmand.binary;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.set.hash.TIntHashSet;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapIndexReader.MapIndex;
import net.osmand.binary.BinaryMapIndexReader.SearchFilter;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.BinaryMapIndexReader.TagValuePair;
import net.osmand.render.RenderingRulesStorage;
import net.osmand.util.MapUtils;
public class BinaryMapIndexFilter {
private final BinaryMapIndexReader reader;
public BinaryMapIndexFilter(File file) throws IOException{
reader = new BinaryMapIndexReader(new RandomAccessFile(file.getPath(), "r"));
}
private static class Stat {
int pointCount = 0;
int totalCount = 0;
int wayCount = 0;
int polygonCount = 0;
int polygonBigSize = 0;
@Override
public String toString() {
return " ways " + wayCount + " polygons " + polygonCount + " points " + pointCount + " total " + totalCount +"\n"+
" polygons big size " + polygonBigSize;
}
}
private double calculateArea(BinaryMapDataObject o, int zoom){
double sum = 0;
for(int i=0; i< o.getPointsLength(); i++){
double x = MapUtils.getTileNumberX(zoom + 8, MapUtils.get31LongitudeX(o.getPoint31XTile(i)));
int prev = i == 0 ? o.getPointsLength() - 1 : i -1;
int next = i == o.getPointsLength() - 1 ? 0 : i + 1;
double y1 = MapUtils.getTileNumberY(zoom + 8, MapUtils.get31LatitudeY(o.getPoint31YTile(prev)));
double y2 = MapUtils.getTileNumberY(zoom + 8, MapUtils.get31LatitudeY(o.getPoint31YTile(next)));
sum += x * (y1 - y2);
}
return Math.abs(sum);
}
private double calculateLength(BinaryMapDataObject o, int zoom){
double sum = 0;
for(int i=1; i< o.getPointsLength(); i++){
double x = MapUtils.getTileNumberX(zoom + 8, MapUtils.get31LongitudeX(o.getPoint31XTile(i)));
double y = MapUtils.getTileNumberY(zoom + 8, MapUtils.get31LatitudeY(o.getPoint31YTile(i)));
double x2 = MapUtils.getTileNumberX(zoom + 8, MapUtils.get31LongitudeX(o.getPoint31XTile(i - 1)));
double y2 = MapUtils.getTileNumberY(zoom + 8, MapUtils.get31LatitudeY(o.getPoint31YTile(i - 1)));
sum += Math.sqrt((x - x2) * (x - x2) + (y - y2) * (y - y2));
}
return Math.abs(sum);
}
private int tilesCovers(BinaryMapDataObject o, int zoom, TIntHashSet set){
set.clear();
for(int i=0; i< o.getPointsLength(); i++){
int x = (int) MapUtils.getTileNumberX(zoom, MapUtils.get31LongitudeX(o.getPoint31XTile(i)));
int y = (int) MapUtils.getTileNumberY(zoom, MapUtils.get31LatitudeY(o.getPoint31YTile(i)));
int val = ((x << 16) | y);
set.add(val);
}
return set.size();
}
private Stat process(final int zoom) throws IOException {
final Stat stat = new Stat();
final Map<TagValuePair, Integer> map = new HashMap<TagValuePair, Integer>();
SearchFilter sf = new SearchFilter() {
@Override
public boolean accept(TIntArrayList types, MapIndex index) {
boolean polygon = false;
boolean polyline = false;
for (int j = 0; j < types.size(); j++) {
int wholeType = types.get(j);
TagValuePair pair = index.decodeType(wholeType);
if (pair != null) {
int t = wholeType & 3;
if (t == RenderingRulesStorage.POINT_RULES) {
stat.pointCount++;
} else if (t == RenderingRulesStorage.LINE_RULES) {
stat.wayCount++;
polyline = true;
} else {
polygon = true;
stat.polygonCount++;
if (!map.containsKey(pair)) {
map.put(pair, 0);
}
map.put(pair, map.get(pair) + 1);
}
}
}
stat.totalCount++;
return polyline;
}
};
ResultMatcher<BinaryMapDataObject> matcher = new ResultMatcher<BinaryMapDataObject>() {
TIntHashSet set = new TIntHashSet();
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean publish(BinaryMapDataObject object) {
// double area = calculateArea(object, zoom);
double len = calculateLength(object, zoom);
if(/*tilesCovers(object, zoom, set) >= 2 && */ len > 100){
stat.polygonBigSize ++;
if(stat.polygonBigSize % 10000 == 0){
return true;
}
}
return false;
}
};
SearchRequest<BinaryMapDataObject> req = BinaryMapIndexReader.buildSearchRequest(0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, zoom,
sf, matcher);
List<BinaryMapDataObject> result = reader.searchMapIndex(req);
ArrayList<TagValuePair> list = new ArrayList<TagValuePair>(map.keySet());
Collections.sort(list, new Comparator<TagValuePair>() {
@Override
public int compare(TagValuePair o1, TagValuePair o2) {
return -map.get(o1) + map.get(o2);
}
});
for(TagValuePair tp : list){
Integer i = map.get(tp);
if(i > 10){
// System.out.println(tp.toString() + " " + i);
}
}
for(BinaryMapDataObject obj : result){
System.out.println("id " + (obj.getId() >> 3) + " " + calculateArea(obj, zoom));
}
return stat;
}
public static void main(String[] iargs) throws IOException {
BinaryMapIndexFilter filter = new BinaryMapIndexFilter(new File(""));
for (int i = 10; i <= 14; i++) {
Stat st = filter.process(i);
System.out.println(i + " zoom -> " + st);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,718 @@
package net.osmand.binary;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.map.hash.TIntLongHashMap;
import gnu.trove.set.hash.TLongHashSet;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import net.osmand.Collator;
import net.osmand.CollatorStringMatcher;
import net.osmand.CollatorStringMatcher.StringMatcherMode;
import net.osmand.PlatformUtil;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.OsmandOdb.OsmAndPoiNameIndex.OsmAndPoiNameIndexData;
import net.osmand.data.Amenity;
import net.osmand.data.AmenityType;
import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
import net.sf.junidecode.Junidecode;
import org.apache.commons.logging.Log;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.WireFormat;
public class BinaryMapPoiReaderAdapter {
private static final Log LOG = PlatformUtil.getLog(BinaryMapPoiReaderAdapter.class);
public static final int SHIFT_BITS_CATEGORY = 7;
private static final int CATEGORY_MASK = (1 << SHIFT_BITS_CATEGORY) - 1 ;
private static final int ZOOM_TO_SKIP_FILTER = 3;
private static final int BUCKET_SEARCH_BY_NAME = 5;
public static class PoiRegion extends BinaryIndexPart {
List<String> categories = new ArrayList<String>();
List<AmenityType> categoriesType = new ArrayList<AmenityType>();
List<List<String> > subcategories = new ArrayList<List<String> >();
double leftLongitude;
double rightLongitude;
double topLatitude;
double bottomLatitude;
public double getLeftLongitude() {
return leftLongitude;
}
public double getRightLongitude() {
return rightLongitude;
}
public double getTopLatitude() {
return topLatitude;
}
public double getBottomLatitude() {
return bottomLatitude;
}
}
private CodedInputStream codedIS;
private final BinaryMapIndexReader map;
protected BinaryMapPoiReaderAdapter(BinaryMapIndexReader map){
this.codedIS = map.codedIS;
this.map = map;
}
private void skipUnknownField(int t) throws IOException {
map.skipUnknownField(t);
}
private int readInt() throws IOException {
return map.readInt();
}
private void readPoiBoundariesIndex(PoiRegion region) throws IOException {
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case OsmandOdb.OsmAndTileBox.LEFT_FIELD_NUMBER:
region.leftLongitude = MapUtils.get31LongitudeX(codedIS.readUInt32());
break;
case OsmandOdb.OsmAndTileBox.RIGHT_FIELD_NUMBER:
region.rightLongitude = MapUtils.get31LongitudeX(codedIS.readUInt32());
break;
case OsmandOdb.OsmAndTileBox.TOP_FIELD_NUMBER:
region.topLatitude = MapUtils.get31LatitudeY(codedIS.readUInt32());
break;
case OsmandOdb.OsmAndTileBox.BOTTOM_FIELD_NUMBER:
region.bottomLatitude = MapUtils.get31LatitudeY(codedIS.readUInt32());
break;
default:
skipUnknownField(t);
break;
}
}
}
protected void readPoiIndex(PoiRegion region, boolean readCategories) throws IOException {
int length;
int oldLimit;
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case OsmandOdb.OsmAndPoiIndex.NAME_FIELD_NUMBER :
region.name = codedIS.readString();
break;
case OsmandOdb.OsmAndPoiIndex.BOUNDARIES_FIELD_NUMBER:
length = codedIS.readRawVarint32();
oldLimit = codedIS.pushLimit(length);
readPoiBoundariesIndex(region);
codedIS.popLimit(oldLimit);
break;
case OsmandOdb.OsmAndPoiIndex.CATEGORIESTABLE_FIELD_NUMBER :
if(!readCategories){
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
return;
}
length = codedIS.readRawVarint32();
oldLimit = codedIS.pushLimit(length);
readCategory(region);
codedIS.popLimit(oldLimit);
break;
case OsmandOdb.OsmAndPoiIndex.BOXES_FIELD_NUMBER :
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
return;
default:
skipUnknownField(t);
break;
}
}
}
private void readCategory(PoiRegion region) throws IOException {
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case OsmandOdb.OsmAndCategoryTable.CATEGORY_FIELD_NUMBER :
String cat = codedIS.readString().intern();
region.categories.add(cat);
region.categoriesType.add(AmenityType.fromString(cat));
region.subcategories.add(new ArrayList<String>());
break;
case OsmandOdb.OsmAndCategoryTable.SUBCATEGORIES_FIELD_NUMBER :
region.subcategories.get(region.subcategories.size() - 1).add(codedIS.readString().intern());
break;
default:
skipUnknownField(t);
break;
}
}
}
public void initCategories(PoiRegion region) throws IOException {
if(region.categories.isEmpty()) {
codedIS.seek(region.filePointer);
int oldLimit = codedIS.pushLimit(region.length);
readPoiIndex(region, true);
codedIS.popLimit(oldLimit);
}
}
protected void searchPoiByName( PoiRegion region, SearchRequest<Amenity> req) throws IOException {
TIntLongHashMap offsets = new TIntLongHashMap();
CollatorStringMatcher matcher = new CollatorStringMatcher(req.nameQuery,
StringMatcherMode.CHECK_STARTS_FROM_SPACE);
long time = System.currentTimeMillis();
int indexOffset = codedIS.getTotalBytesRead();
while(true){
if(req.isCancelled()){
return;
}
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case OsmandOdb.OsmAndPoiIndex.NAMEINDEX_FIELD_NUMBER :
int length = readInt();
int oldLimit = codedIS.pushLimit(length);
// here offsets are sorted by distance
offsets = readPoiNameIndex(matcher.getCollator(), req.nameQuery, req);
codedIS.popLimit(oldLimit);
break;
case OsmandOdb.OsmAndPoiIndex.POIDATA_FIELD_NUMBER :
// also offsets can be randomly skipped by limit
Integer[] offKeys = new Integer[offsets.size()];
if (offsets.size() > 0) {
int[] keys = offsets.keys();
for (int i = 0; i < keys.length; i++) {
offKeys[i] = keys[i];
}
final TIntLongHashMap foffsets = offsets;
Arrays.sort(offKeys, new Comparator<Integer>() {
@Override
public int compare(Integer object1, Integer object2) {
return Double.compare(foffsets.get(object1), foffsets.get(object2));
}
});
int p = BUCKET_SEARCH_BY_NAME * 3 ;
if (p < offKeys.length) {
for (int i = p + BUCKET_SEARCH_BY_NAME;; i += BUCKET_SEARCH_BY_NAME) {
if (i > offKeys.length) {
Arrays.sort(offKeys, p, offKeys.length);
break;
} else {
Arrays.sort(offKeys, p, i);
}
p = i;
}
}
}
LOG.info("Searched poi structure in "+(System.currentTimeMillis() - time) +
"ms. Found " + offKeys.length +" subtress");
for (int j = 0; j < offKeys.length; j++) {
codedIS.seek(offKeys[j] + indexOffset);
int len = readInt();
int oldLim = codedIS.pushLimit(len);
readPoiData(matcher, req, region);
codedIS.popLimit(oldLim);
if(req.isCancelled() || req.limitExceeded()){
return;
}
}
LOG.info("Whole poi by name search is done in "+(System.currentTimeMillis() - time) +
"ms. Found " + req.getSearchResults().size());
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
return;
default:
skipUnknownField(t);
break;
}
}
}
private TIntLongHashMap readPoiNameIndex(Collator instance, String query, SearchRequest<Amenity> req) throws IOException {
TIntLongHashMap offsets = new TIntLongHashMap();
TIntArrayList dataOffsets = null;
int offset = 0;
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return offsets;
case OsmandOdb.OsmAndPoiNameIndex.TABLE_FIELD_NUMBER : {
int length = readInt();
int oldLimit = codedIS.pushLimit(length);
dataOffsets = new TIntArrayList();
offset = codedIS.getTotalBytesRead();
map.readIndexedStringTable(instance, query, "", dataOffsets, 0);
codedIS.popLimit(oldLimit);
break; }
case OsmandOdb.OsmAndPoiNameIndex.DATA_FIELD_NUMBER : {
if(dataOffsets != null){
dataOffsets.sort(); // 1104125
for (int i = 0; i < dataOffsets.size(); i++) {
codedIS.seek(dataOffsets.get(i) + offset);
int len = codedIS.readRawVarint32();
int oldLim = codedIS.pushLimit(len);
readPoiNameIndexData(offsets, req);
codedIS.popLimit(oldLim);
if (req.isCancelled()) {
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
return offsets;
}
}
}
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
return offsets; }
default:
skipUnknownField(t);
break;
}
}
}
private void readPoiNameIndexData(TIntLongHashMap offsets, SearchRequest<Amenity> req) throws IOException {
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case OsmAndPoiNameIndexData.ATOMS_FIELD_NUMBER :
int len = codedIS.readRawVarint32();
int oldLim = codedIS.pushLimit(len);
readPoiNameIndexDataAtom(offsets, req);
codedIS.popLimit(oldLim);
break;
default:
skipUnknownField(t);
break;
}
}
}
private void readPoiNameIndexDataAtom(TIntLongHashMap offsets, SearchRequest<Amenity> req) throws IOException {
int x = 0;
int y = 0;
int zoom = 15;
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case OsmandOdb.OsmAndPoiNameIndexDataAtom.X_FIELD_NUMBER :
x = codedIS.readUInt32();
break;
case OsmandOdb.OsmAndPoiNameIndexDataAtom.Y_FIELD_NUMBER :
y = codedIS.readUInt32();
break;
case OsmandOdb.OsmAndPoiNameIndexDataAtom.ZOOM_FIELD_NUMBER :
zoom = codedIS.readUInt32();
break;
case OsmandOdb.OsmAndPoiNameIndexDataAtom.SHIFTTO_FIELD_NUMBER :
int x31 = (x << (31 - zoom));
int y31 = (y << (31 - zoom));
int shift = readInt();
if (req.contains(x31, y31, x31, y31)) {
long d = Math.abs(req.x - x31) + Math.abs(req.y - y31);
offsets.put(shift, d);
}
break;
default:
skipUnknownField(t);
break;
}
}
}
protected void searchPoiIndex(int left31, int right31, int top31, int bottom31,
SearchRequest<Amenity> req, PoiRegion region) throws IOException {
int indexOffset = codedIS.getTotalBytesRead();
long time = System.currentTimeMillis();
TLongHashSet skipTiles = null;
int zoomToSkip = 31;
if(req.zoom != -1){
skipTiles = new TLongHashSet();
zoomToSkip = req.zoom + ZOOM_TO_SKIP_FILTER;
}
int length ;
int oldLimit ;
TIntLongHashMap offsetsMap = new TIntLongHashMap();
while(true){
if(req.isCancelled()){
return;
}
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case OsmandOdb.OsmAndPoiIndex.BOXES_FIELD_NUMBER :
length = readInt();
oldLimit = codedIS.pushLimit(length);
readBoxField(left31, right31, top31, bottom31, 0, 0, 0, offsetsMap, skipTiles, req, region);
codedIS.popLimit(oldLimit);
break;
case OsmandOdb.OsmAndPoiIndex.POIDATA_FIELD_NUMBER :
int[] offsets = offsetsMap.keys();
// also offsets can be randomly skipped by limit
Arrays.sort(offsets);
if(skipTiles != null){
skipTiles.clear();
}
LOG.info("Searched poi structure in "+(System.currentTimeMillis() - time) +
"ms. Found " + offsets.length +" subtress");
for (int j = 0; j < offsets.length; j++) {
codedIS.seek(offsets[j] + indexOffset);
int len = readInt();
int oldLim = codedIS.pushLimit(len);
readPoiData(left31, right31, top31, bottom31, req, region, skipTiles, zoomToSkip);
codedIS.popLimit(oldLim);
if(req.isCancelled()){
return;
}
}
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
return;
default:
skipUnknownField(t);
break;
}
}
}
private void readPoiData(CollatorStringMatcher matcher, SearchRequest<Amenity> req, PoiRegion region) throws IOException {
int x = 0;
int y = 0;
int zoom = 0;
while(true){
if(req.isCancelled() || req.limitExceeded()){
return;
}
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case OsmandOdb.OsmAndPoiBoxData.X_FIELD_NUMBER :
x = codedIS.readUInt32();
break;
case OsmandOdb.OsmAndPoiBoxData.ZOOM_FIELD_NUMBER :
zoom = codedIS.readUInt32();
break;
case OsmandOdb.OsmAndPoiBoxData.Y_FIELD_NUMBER :
y = codedIS.readUInt32();
break;
case OsmandOdb.OsmAndPoiBoxData.POIDATA_FIELD_NUMBER:
int len = codedIS.readRawVarint32();
int oldLim = codedIS.pushLimit(len);
Amenity am = readPoiPoint(0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, x, y, zoom, req, region, false);
codedIS.popLimit(oldLim);
if (am != null) {
if(matcher.matches(am.getName(false)) || matcher.matches(am.getName(true))) {
req.publish(am);
}
}
break;
default:
skipUnknownField(t);
break;
}
}
}
private void readPoiData(int left31, int right31, int top31, int bottom31,
SearchRequest<Amenity> req, PoiRegion region, TLongHashSet toSkip, int zSkip) throws IOException {
int x = 0;
int y = 0;
int zoom = 0;
while(true){
if(req.isCancelled()){
return;
}
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case OsmandOdb.OsmAndPoiBoxData.X_FIELD_NUMBER :
x = codedIS.readUInt32();
break;
case OsmandOdb.OsmAndPoiBoxData.ZOOM_FIELD_NUMBER :
zoom = codedIS.readUInt32();
break;
case OsmandOdb.OsmAndPoiBoxData.Y_FIELD_NUMBER :
y = codedIS.readUInt32();
break;
case OsmandOdb.OsmAndPoiBoxData.POIDATA_FIELD_NUMBER:
int len = codedIS.readRawVarint32();
int oldLim = codedIS.pushLimit(len);
Amenity am = readPoiPoint(left31, right31, top31, bottom31, x, y, zoom, req, region, true);
codedIS.popLimit(oldLim);
if (am != null) {
if (toSkip != null) {
int xp = (int) MapUtils.getTileNumberX(zSkip, am.getLocation().getLongitude());
int yp = (int) MapUtils.getTileNumberY(zSkip, am.getLocation().getLatitude());
long val = (((long) xp) << zSkip) | yp;
if (!toSkip.contains(val)) {
boolean publish = req.publish(am);
if(publish) {
toSkip.add(val);
}
}
if(zSkip <= zoom){
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
return;
}
} else {
req.publish(am);
}
}
break;
default:
skipUnknownField(t);
break;
}
}
}
private Amenity readPoiPoint(int left31, int right31, int top31, int bottom31,
int px, int py, int zoom, SearchRequest<Amenity> req, PoiRegion region, boolean checkBounds) throws IOException {
Amenity am = null;
int x = 0;
int y = 0;
AmenityType amenityType = null;
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
if(amenityType == null && (tag > OsmandOdb.OsmAndPoiBoxDataAtom.CATEGORIES_FIELD_NUMBER || tag == 0)) {
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
return null;
}
switch (tag) {
case 0:
if(Algorithms.isEmpty(am.getEnName())){
am.setEnName(Junidecode.unidecode(am.getName()));
}
req.numberOfAcceptedObjects++;
return am;
case OsmandOdb.OsmAndPoiBoxDataAtom.DX_FIELD_NUMBER :
x = (codedIS.readSInt32() + (px << (24 - zoom))) << 7;
break;
case OsmandOdb.OsmAndPoiBoxDataAtom.DY_FIELD_NUMBER :
y = (codedIS.readSInt32() + (py << (24 - zoom))) << 7;
req.numberOfVisitedObjects++;
if (checkBounds) {
if (left31 > x || right31 < x || top31 > y || bottom31 < y) {
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
return null;
}
}
am = new Amenity();
am.setLocation(MapUtils.get31LatitudeY(y), MapUtils.get31LongitudeX(x));
break;
case OsmandOdb.OsmAndPoiBoxDataAtom.CATEGORIES_FIELD_NUMBER :
int cat = codedIS.readUInt32();
int subcatId = cat >> SHIFT_BITS_CATEGORY;
int catId = cat & CATEGORY_MASK;
AmenityType type = AmenityType.OTHER;
String subtype = "";
if (catId < region.categoriesType.size()) {
type = region.categoriesType.get(catId);
List<String> subcats = region.subcategories.get(catId);
if (subcatId < subcats.size()) {
subtype = subcats.get(subcatId);
}
}
if (req.poiTypeFilter == null || req.poiTypeFilter.accept(type, subtype)) {
if (amenityType == null) {
amenityType = type;
am.setSubType(subtype);
am.setType(amenityType);
} else {
am.setSubType(am.getSubType() + ";" + subtype);
}
}
break;
case OsmandOdb.OsmAndPoiBoxDataAtom.ID_FIELD_NUMBER :
am.setId(codedIS.readUInt64());
break;
case OsmandOdb.OsmAndPoiBoxDataAtom.NAME_FIELD_NUMBER :
am.setName(codedIS.readString());
break;
case OsmandOdb.OsmAndPoiBoxDataAtom.NAMEEN_FIELD_NUMBER :
am.setEnName(codedIS.readString());
break;
case OsmandOdb.OsmAndPoiBoxDataAtom.OPENINGHOURS_FIELD_NUMBER :
am.setOpeningHours(codedIS.readString());
break;
case OsmandOdb.OsmAndPoiBoxDataAtom.SITE_FIELD_NUMBER :
am.setSite(codedIS.readString());
break;
case OsmandOdb.OsmAndPoiBoxDataAtom.PHONE_FIELD_NUMBER:
am.setPhone(codedIS.readString());
break;
case OsmandOdb.OsmAndPoiBoxDataAtom.NOTE_FIELD_NUMBER:
am.setDescription(codedIS.readString());
break;
default:
skipUnknownField(t);
break;
}
}
}
private boolean checkCategories(SearchRequest<Amenity> req, PoiRegion region) throws IOException {
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return false;
case OsmandOdb.OsmAndPoiCategories.CATEGORIES_FIELD_NUMBER:
AmenityType type = AmenityType.OTHER;
String subcat = "";
int cat = codedIS.readUInt32();
int subcatId = cat >> SHIFT_BITS_CATEGORY;
int catId = cat & CATEGORY_MASK;
if(catId < region.categoriesType.size()){
type = region.categoriesType.get(catId);
List<String> subcats = region.subcategories.get(catId);
if(subcatId < subcats.size()){
subcat = subcats.get(subcatId);
}
} else {
type = AmenityType.OTHER;
}
if(req.poiTypeFilter.accept(type, subcat)){
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
return true;
}
break;
default:
skipUnknownField(t);
break;
}
}
}
private boolean readBoxField(int left31, int right31, int top31, int bottom31,
int px, int py, int pzoom, TIntLongHashMap offsetsMap, TLongHashSet skipTiles, SearchRequest<Amenity> req, PoiRegion region) throws IOException {
req.numberOfReadSubtrees++;
int zoomToSkip = req.zoom + ZOOM_TO_SKIP_FILTER;
boolean checkBox = true;
boolean existsCategories = false;
int zoom = pzoom;
int dy = py;
int dx = px;
while(true){
if(req.isCancelled()){
return false;
}
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return existsCategories;
case OsmandOdb.OsmAndPoiBox.ZOOM_FIELD_NUMBER :
zoom = codedIS.readUInt32() + pzoom;
break;
case OsmandOdb.OsmAndPoiBox.LEFT_FIELD_NUMBER :
dx = codedIS.readSInt32();
break;
case OsmandOdb.OsmAndPoiBox.TOP_FIELD_NUMBER:
dy = codedIS.readSInt32();
break;
case OsmandOdb.OsmAndPoiBox.CATEGORIES_FIELD_NUMBER:
if(req.poiTypeFilter == null){
skipUnknownField(t);
} else {
int length = codedIS.readRawVarint32();
int oldLimit = codedIS.pushLimit(length);
boolean check = checkCategories(req, region);
codedIS.popLimit(oldLimit);
if(!check){
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
return false;
}
existsCategories = true;
}
break;
case OsmandOdb.OsmAndPoiBox.SUBBOXES_FIELD_NUMBER: {
int x = dx + (px << (zoom - pzoom));
int y = dy + (py << (zoom - pzoom));
if(checkBox){
int xL = x << (31 - zoom);
int xR = ((x + 1) << (31 - zoom)) - 1;
int yT = y << (31 - zoom);
int yB = ((y + 1) << (31 - zoom)) - 1;
// check intersection
if(left31 > xR || xL > right31 || bottom31 < yT || yB < top31){
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
return false;
}
req.numberOfAcceptedSubtrees++;
checkBox = false;
}
int length = readInt();
int oldLimit = codedIS.pushLimit(length);
boolean exists = readBoxField(left31, right31, top31, bottom31, x, y, zoom, offsetsMap, skipTiles, req, region);
codedIS.popLimit(oldLimit);
if (skipTiles != null && zoom >= zoomToSkip && exists) {
long val = ((((long) x) >> (zoom - zoomToSkip)) << zoomToSkip) | (((long) y) >> (zoom - zoomToSkip));
if(skipTiles.contains(val)){
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
return true;
}
}
} break;
case OsmandOdb.OsmAndPoiBox.SHIFTTODATA_FIELD_NUMBER: {
int x = dx + (px << (zoom - pzoom));
int y = dy + (py << (zoom - pzoom));
long l = ((((x << zoom) | y) << 5) | zoom);
offsetsMap.put(readInt(), l);
if(skipTiles != null && zoom >= zoomToSkip){
long val = ((((long) x) >> (zoom - zoomToSkip)) << zoomToSkip) | (((long) y) >> (zoom - zoomToSkip));
skipTiles.add(val);
}
} break;
default:
skipUnknownField(t);
break;
}
}
}
}

View file

@ -0,0 +1,927 @@
package net.osmand.binary;
import gnu.trove.iterator.TLongObjectIterator;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.list.array.TLongArrayList;
import gnu.trove.map.hash.TIntObjectHashMap;
import gnu.trove.map.hash.TLongObjectHashMap;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import net.osmand.PlatformUtil;
import net.osmand.ResultMatcher;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.binary.OsmandOdb.IdTable;
import net.osmand.binary.OsmandOdb.OsmAndRoutingIndex.RouteBorderBox;
import net.osmand.binary.OsmandOdb.OsmAndRoutingIndex.RouteBorderLine;
import net.osmand.binary.OsmandOdb.OsmAndRoutingIndex.RouteBorderLine.Builder;
import net.osmand.binary.OsmandOdb.OsmAndRoutingIndex.RouteBorderPoint;
import net.osmand.binary.OsmandOdb.OsmAndRoutingIndex.RouteBorderPointsBlock;
import net.osmand.binary.OsmandOdb.OsmAndRoutingIndex.RouteDataBlock;
import net.osmand.binary.OsmandOdb.OsmAndRoutingIndex.RouteDataBox;
import net.osmand.binary.OsmandOdb.OsmAndRoutingIndex.RouteEncodingRule;
import net.osmand.binary.OsmandOdb.RestrictionData;
import net.osmand.binary.OsmandOdb.RouteData;
import net.osmand.util.MapUtils;
import org.apache.commons.logging.Log;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.WireFormat;
public class BinaryMapRouteReaderAdapter {
protected static final Log LOG = PlatformUtil.getLog(BinaryMapRouteReaderAdapter.class);
private static final int SHIFT_COORDINATES = 4;
public static class RouteTypeRule {
private final static int ACCESS = 1;
private final static int ONEWAY = 2;
private final static int HIGHWAY_TYPE = 3;
private final static int MAXSPEED = 4;
private final static int ROUNDABOUT = 5;
public final static int TRAFFIC_SIGNALS = 6;
public final static int RAILWAY_CROSSING = 7;
private final static int LANES = 8;
private final String t;
private final String v;
private int intValue;
private float floatValue;
private int type;
public RouteTypeRule(String t, String v) {
this.t = t.intern();
if("true".equals(v)) {
v = "yes";
}
if("false".equals(v)) {
v = "no";
}
this.v = v == null? null : v.intern();
analyze();
}
public String getTag() {
return t;
}
public String getValue(){
return v;
}
public boolean roundabout(){
return type == ROUNDABOUT;
}
public int getType() {
return type;
}
public int onewayDirection(){
if(type == ONEWAY){
return intValue;
}
return 0;
}
public float maxSpeed(){
if(type == MAXSPEED){
return floatValue;
}
return -1;
}
public int lanes(){
if(type == LANES){
return intValue;
}
return -1;
}
public String highwayRoad(){
if(type == HIGHWAY_TYPE){
return v;
}
return null;
}
private void analyze() {
if(t.equalsIgnoreCase("oneway")){
type = ONEWAY;
if("-1".equals(v) || "reverse".equals(v)) {
intValue = -1;
} else if("1".equals(v) || "yes".equals(v)) {
intValue = 1;
} else {
intValue = 0;
}
} else if(t.equalsIgnoreCase("highway") && "traffic_signals".equals(v)){
type = TRAFFIC_SIGNALS;
} else if(t.equalsIgnoreCase("railway") && ("crossing".equals(v) || "level_crossing".equals(v))){
type = RAILWAY_CROSSING;
} else if(t.equalsIgnoreCase("roundabout") && v != null){
type = ROUNDABOUT;
} else if(t.equalsIgnoreCase("junction") && "roundabout".equalsIgnoreCase(v)){
type = ROUNDABOUT;
} else if(t.equalsIgnoreCase("highway") && v != null){
type = HIGHWAY_TYPE;
} else if(t.startsWith("access") && v != null){
type = ACCESS;
} else if(t.equalsIgnoreCase("maxspeed") && v != null){
type = MAXSPEED;
floatValue = -1;
if(v.equals("none")) {
floatValue = RouteDataObject.NONE_MAX_SPEED;
} else {
int i = 0;
while (i < v.length() && Character.isDigit(v.charAt(i))) {
i++;
}
if (i > 0) {
floatValue = Integer.parseInt(v.substring(0, i));
floatValue /= 3.6; // km/h -> m/s
if (v.contains("mph")) {
floatValue *= 1.6;
}
}
}
} else if (t.equalsIgnoreCase("lanes") && v != null) {
intValue = -1;
int i = 0;
type = LANES;
while (i < v.length() && Character.isDigit(v.charAt(i))) {
i++;
}
if (i > 0) {
intValue = Integer.parseInt(v.substring(0, i));
}
}
}
}
public static class RouteRegion extends BinaryIndexPart {
public int regionsRead;
public int borderBoxPointer = 0;
public int baseBorderBoxPointer = 0;
public int borderBoxLength = 0;
public int baseBorderBoxLength = 0;
List<RouteSubregion> subregions = new ArrayList<RouteSubregion>();
List<RouteSubregion> basesubregions = new ArrayList<RouteSubregion>();
List<RouteTypeRule> routeEncodingRules = new ArrayList<BinaryMapRouteReaderAdapter.RouteTypeRule>();
int nameTypeRule = -1;
int refTypeRule = -1;
int destinationTypeRule = -1;
public RouteTypeRule quickGetEncodingRule(int id) {
return routeEncodingRules.get(id);
}
private void initRouteEncodingRule(int id, String tags, String val) {
while(routeEncodingRules.size() <= id) {
routeEncodingRules.add(null);
}
routeEncodingRules.set(id, new RouteTypeRule(tags, val));
if(tags.equals("name")) {
nameTypeRule = id;
} else if(tags.equals("ref")) {
refTypeRule = id;
} else if(tags.equals("destination")) {
destinationTypeRule = id;
}
}
public List<RouteSubregion> getSubregions(){
return subregions;
}
public List<RouteSubregion> getBaseSubregions(){
return basesubregions;
}
public double getLeftLongitude() {
double l = 180;
for(RouteSubregion s : subregions) {
l = Math.min(l, MapUtils.get31LongitudeX(s.left));
}
return l;
}
public double getRightLongitude() {
double l = -180;
for(RouteSubregion s : subregions) {
l = Math.max(l, MapUtils.get31LongitudeX(s.right));
}
return l;
}
public double getBottomLatitude() {
double l = 90;
for(RouteSubregion s : subregions) {
l = Math.min(l, MapUtils.get31LatitudeY(s.bottom));
}
return l;
}
public double getTopLatitude() {
double l = -90;
for(RouteSubregion s : subregions) {
l = Math.max(l, MapUtils.get31LatitudeY(s.top));
}
return l;
}
}
// Used in C++
public static class RouteSubregion {
private final static int INT_SIZE = 4;
public final RouteRegion routeReg;
public RouteSubregion(RouteSubregion copy) {
this.routeReg = copy.routeReg;
this.left = copy.left;
this.right = copy.right;
this.top = copy.top;
this.bottom = copy.bottom;
this.filePointer = copy.filePointer;
this.length = copy.length;
}
public RouteSubregion(RouteRegion routeReg) {
this.routeReg = routeReg;
}
public int length;
public int filePointer;
public int left;
public int right;
public int top;
public int bottom;
public int shiftToData;
public List<RouteSubregion> subregions = null;
public List<RouteDataObject> dataObjects = null;
public int getEstimatedSize(){
int shallow = 7 * INT_SIZE + 4*3;
if (subregions != null) {
shallow += 8;
for (RouteSubregion s : subregions) {
shallow += s.getEstimatedSize();
}
}
return shallow;
}
public int countSubregions(){
int cnt = 1;
if (subregions != null) {
for (RouteSubregion s : subregions) {
cnt += s.countSubregions();
}
}
return cnt;
}
}
private CodedInputStream codedIS;
private final BinaryMapIndexReader map;
protected BinaryMapRouteReaderAdapter(BinaryMapIndexReader map){
this.codedIS = map.codedIS;
this.map = map;
}
private void skipUnknownField(int t) throws IOException {
map.skipUnknownField(t);
}
private int readInt() throws IOException {
return map.readInt();
}
protected void readRouteIndex(RouteRegion region) throws IOException {
int routeEncodingRule =1;
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case OsmandOdb.OsmAndRoutingIndex.NAME_FIELD_NUMBER :
region.name = codedIS.readString();
break;
case OsmandOdb.OsmAndRoutingIndex.RULES_FIELD_NUMBER: {
int len = codedIS.readInt32();
int oldLimit = codedIS.pushLimit(len);
readRouteEncodingRule(region, routeEncodingRule++);
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
codedIS.popLimit(oldLimit);
} break;
case OsmandOdb.OsmAndRoutingIndex.ROOTBOXES_FIELD_NUMBER :
case OsmandOdb.OsmAndRoutingIndex.BASEMAPBOXES_FIELD_NUMBER :{
RouteSubregion subregion = new RouteSubregion(region);
subregion.length = readInt();
subregion.filePointer = codedIS.getTotalBytesRead();
int oldLimit = codedIS.pushLimit(subregion.length);
readRouteTree(subregion, null, 0, true);
if(tag == OsmandOdb.OsmAndRoutingIndex.ROOTBOXES_FIELD_NUMBER) {
region.subregions.add(subregion);
} else {
region.basesubregions.add(subregion);
}
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
codedIS.popLimit(oldLimit);
break;
}
case OsmandOdb.OsmAndRoutingIndex.BASEBORDERBOX_FIELD_NUMBER:
case OsmandOdb.OsmAndRoutingIndex.BORDERBOX_FIELD_NUMBER: {
int length = readInt();
int filePointer = codedIS.getTotalBytesRead();
if(tag == OsmandOdb.OsmAndRoutingIndex.BORDERBOX_FIELD_NUMBER) {
region.borderBoxLength = length;
region.borderBoxPointer = filePointer;
} else {
region.baseBorderBoxLength = length;
region.baseBorderBoxPointer = filePointer;
}
codedIS.skipRawBytes(length);
break;
}
case OsmandOdb.OsmAndRoutingIndex.BLOCKS_FIELD_NUMBER : {
// Finish reading file!
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
break;
}
default:
skipUnknownField(t);
break;
}
}
}
private RouteDataObject readRouteDataObject(RouteRegion reg, int pleftx, int ptopy) throws IOException {
RouteDataObject o = new RouteDataObject(reg);
TIntArrayList pointsX = new TIntArrayList();
TIntArrayList pointsY = new TIntArrayList();
TIntArrayList types = new TIntArrayList();
List<TIntArrayList> globalpointTypes = new ArrayList<TIntArrayList>();
while (true) {
int ts = codedIS.readTag();
int tags = WireFormat.getTagFieldNumber(ts);
switch (tags) {
case 0:
o.pointsX = pointsX.toArray();
o.pointsY = pointsY.toArray();
o.types = types.toArray();
if(globalpointTypes.size() > 0){
o.pointTypes = new int[globalpointTypes.size()][];
for(int k=0; k<o.pointTypes.length; k++) {
TIntArrayList l = globalpointTypes.get(k);
if(l != null) {
o.pointTypes[k] = l.toArray();
}
}
}
return o;
case RouteData.TYPES_FIELD_NUMBER:
int len = codedIS.readRawVarint32();
int oldLimit = codedIS.pushLimit(len);
while(codedIS.getBytesUntilLimit() > 0) {
types.add(codedIS.readRawVarint32());
}
codedIS.popLimit(oldLimit);
break;
case RouteData.STRINGNAMES_FIELD_NUMBER:
o.names = new TIntObjectHashMap<String>();
int sizeL = codedIS.readRawVarint32();
int old = codedIS.pushLimit(sizeL);
while (codedIS.getBytesUntilLimit() > 0) {
int stag = codedIS.readRawVarint32();
int pId = codedIS.readRawVarint32();
o.names.put(stag, ((char)pId)+"");
}
codedIS.popLimit(old);
break;
case RouteData.POINTS_FIELD_NUMBER:
len = codedIS.readRawVarint32();
oldLimit = codedIS.pushLimit(len);
int px = pleftx >> SHIFT_COORDINATES;
int py = ptopy >> SHIFT_COORDINATES;
while(codedIS.getBytesUntilLimit() > 0){
int x = (codedIS.readSInt32() ) + px;
int y = (codedIS.readSInt32() ) + py;
pointsX.add(x << SHIFT_COORDINATES);
pointsY.add(y << SHIFT_COORDINATES);
px = x;
py = y;
}
codedIS.popLimit(oldLimit);
break;
case RouteData.POINTTYPES_FIELD_NUMBER:
len = codedIS.readRawVarint32();
oldLimit = codedIS.pushLimit(len);
while (codedIS.getBytesUntilLimit() > 0) {
int pointInd = codedIS.readRawVarint32();
TIntArrayList pointTypes = new TIntArrayList();
int lens = codedIS.readRawVarint32();
int oldLimits = codedIS.pushLimit(lens);
while (codedIS.getBytesUntilLimit() > 0) {
pointTypes.add(codedIS.readRawVarint32());
}
codedIS.popLimit(oldLimits);
while (pointInd >= globalpointTypes.size()) {
globalpointTypes.add(null);
}
globalpointTypes.set(pointInd, pointTypes);
}
codedIS.popLimit(oldLimit);
break;
case RouteData.ROUTEID_FIELD_NUMBER:
o.id = codedIS.readInt32();
break;
default:
skipUnknownField(ts);
break;
}
}
}
private void readRouteTreeData(RouteSubregion routeTree, TLongArrayList idTables,
TLongObjectHashMap<TLongArrayList> restrictions) throws IOException {
routeTree.dataObjects = new ArrayList<RouteDataObject>();
idTables.clear();
restrictions.clear();
List<String> stringTable = null;
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
TLongObjectIterator<TLongArrayList> it = restrictions.iterator();
while (it.hasNext()) {
it.advance();
int from = (int) it.key();
RouteDataObject fromr = routeTree.dataObjects.get(from);
fromr.restrictions = new long[it.value().size()];
for (int k = 0; k < fromr.restrictions.length; k++) {
int to = (int) (it.value().get(k) >> RouteDataObject.RESTRICTION_SHIFT);
long valto = (idTables.get(to) << RouteDataObject.RESTRICTION_SHIFT) | ((long) it.value().get(k) & RouteDataObject.RESTRICTION_MASK);
fromr.restrictions[k] = valto;
}
}
for (RouteDataObject o : routeTree.dataObjects) {
if (o != null) {
if (o.id < idTables.size()) {
o.id = idTables.get((int) o.id);
}
if (o.names != null && stringTable != null) {
int[] keys = o.names.keys();
for (int j = 0; j < keys.length; j++) {
o.names.put(keys[j], stringTable.get(o.names.get(keys[j]).charAt(0)));
}
}
}
}
return;
case RouteDataBlock.DATAOBJECTS_FIELD_NUMBER :
int length = codedIS.readRawVarint32();
int oldLimit = codedIS.pushLimit(length);
RouteDataObject obj = readRouteDataObject(routeTree.routeReg, routeTree.left, routeTree.top);
while(obj.id >= routeTree.dataObjects.size()) {
routeTree.dataObjects.add(null);
}
routeTree.dataObjects.set((int) obj.id,obj);
codedIS.popLimit(oldLimit);
break;
case RouteDataBlock.IDTABLE_FIELD_NUMBER :
long routeId = 0;
length = codedIS.readRawVarint32();
oldLimit = codedIS.pushLimit(length);
idLoop : while(true){
int ts = codedIS.readTag();
int tags = WireFormat.getTagFieldNumber(ts);
switch (tags) {
case 0:
break idLoop;
case IdTable.ROUTEID_FIELD_NUMBER :
routeId += codedIS.readSInt64();
idTables.add(routeId);
break;
default:
skipUnknownField(ts);
break;
}
}
codedIS.popLimit(oldLimit);
break;
case RouteDataBlock.RESTRICTIONS_FIELD_NUMBER :
length = codedIS.readRawVarint32();
oldLimit = codedIS.pushLimit(length);
long from = 0;
long to = 0;
long type = 0;
idLoop : while(true){
int ts = codedIS.readTag();
int tags = WireFormat.getTagFieldNumber(ts);
switch (tags) {
case 0:
break idLoop;
case RestrictionData.FROM_FIELD_NUMBER :
from = codedIS.readInt32();
break;
case RestrictionData.TO_FIELD_NUMBER :
to = codedIS.readInt32();
break;
case RestrictionData.TYPE_FIELD_NUMBER :
type = codedIS.readInt32();
break;
default:
skipUnknownField(ts);
break;
}
}
if(!restrictions.containsKey(from)) {
restrictions.put(from, new TLongArrayList());
}
restrictions.get(from).add((to << RouteDataObject.RESTRICTION_SHIFT) + type);
codedIS.popLimit(oldLimit);
break;
case RouteDataBlock.STRINGTABLE_FIELD_NUMBER :
length = codedIS.readRawVarint32();
oldLimit = codedIS.pushLimit(length);
stringTable = map.readStringTable();
// codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
codedIS.popLimit(oldLimit);
break;
default:
skipUnknownField(t);
break;
}
}
}
private void readRouteEncodingRule(RouteRegion index, int id) throws IOException {
String tags = null;
String val = null;
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
index.initRouteEncodingRule(id, tags, val);
return;
case RouteEncodingRule.VALUE_FIELD_NUMBER :
val = codedIS.readString().intern();
break;
case RouteEncodingRule.TAG_FIELD_NUMBER :
tags = codedIS.readString().intern();
break;
case RouteEncodingRule.ID_FIELD_NUMBER :
id = codedIS.readUInt32();
break;
default:
skipUnknownField(t);
break;
}
}
}
private RouteSubregion readRouteTree(RouteSubregion thisTree, RouteSubregion parentTree, int depth,
boolean readCoordinates) throws IOException {
boolean readChildren = depth != 0;
if(readChildren) {
thisTree.subregions = new ArrayList<BinaryMapRouteReaderAdapter.RouteSubregion>();
}
thisTree.routeReg.regionsRead++;
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return thisTree;
case RouteDataBox.LEFT_FIELD_NUMBER :
int i = codedIS.readSInt32();
if (readCoordinates) {
thisTree.left = i + (parentTree != null ? parentTree.left : 0);
}
break;
case RouteDataBox.RIGHT_FIELD_NUMBER :
i = codedIS.readSInt32();
if (readCoordinates) {
thisTree.right = i + (parentTree != null ? parentTree.right : 0);
}
break;
case RouteDataBox.TOP_FIELD_NUMBER :
i = codedIS.readSInt32();
if (readCoordinates) {
thisTree.top = i + (parentTree != null ? parentTree.top : 0);
}
break;
case RouteDataBox.BOTTOM_FIELD_NUMBER :
i = codedIS.readSInt32();
if (readCoordinates) {
thisTree.bottom = i + (parentTree != null ? parentTree.bottom : 0);
}
break;
case RouteDataBox.SHIFTTODATA_FIELD_NUMBER :
thisTree.shiftToData = readInt();
if(!readChildren) {
// usually 0
thisTree.subregions = new ArrayList<BinaryMapRouteReaderAdapter.RouteSubregion>();
readChildren = true;
}
break;
case RouteDataBox.BOXES_FIELD_NUMBER :
if(readChildren){
RouteSubregion subregion = new RouteSubregion(thisTree.routeReg);
subregion.length = readInt();
subregion.filePointer = codedIS.getTotalBytesRead();
int oldLimit = codedIS.pushLimit(subregion.length);
readRouteTree(subregion, thisTree, depth - 1, true);
thisTree.subregions.add(subregion);
codedIS.popLimit(oldLimit);
codedIS.seek(subregion.filePointer + subregion.length);
} else {
codedIS.seek(thisTree.filePointer + thisTree.length);
// skipUnknownField(t);
}
break;
default:
skipUnknownField(t);
break;
}
}
}
public void initRouteTypesIfNeeded(SearchRequest<RouteDataObject> req, List<RouteSubregion> list) throws IOException {
for (RouteSubregion rs : list) {
if (req.intersects(rs.left, rs.top, rs.right, rs.bottom)) {
initRouteRegion(rs.routeReg);
}
}
}
public void initRouteRegion(RouteRegion routeReg) throws IOException, InvalidProtocolBufferException {
if (routeReg.routeEncodingRules.isEmpty()) {
codedIS.seek(routeReg.filePointer);
int oldLimit = codedIS.pushLimit(routeReg.length);
readRouteIndex(routeReg);
codedIS.popLimit(oldLimit);
}
}
public List<RouteDataObject> loadRouteRegionData(RouteSubregion rs) throws IOException {
TLongArrayList idMap = new TLongArrayList();
TLongObjectHashMap<TLongArrayList> restrictionMap = new TLongObjectHashMap<TLongArrayList>();
if (rs.dataObjects == null) {
codedIS.seek(rs.filePointer + rs.shiftToData);
int limit = codedIS.readRawVarint32();
int oldLimit = codedIS.pushLimit(limit);
readRouteTreeData(rs, idMap, restrictionMap);
codedIS.popLimit(oldLimit);
}
List<RouteDataObject> res = rs.dataObjects;
rs.dataObjects = null;
return res;
}
public void loadRouteRegionData(List<RouteSubregion> toLoad, ResultMatcher<RouteDataObject> matcher) throws IOException {
Collections.sort(toLoad, new Comparator<RouteSubregion>() {
@Override
public int compare(RouteSubregion o1, RouteSubregion o2) {
int p1 = o1.filePointer + o1.shiftToData;
int p2 = o2.filePointer + o2.shiftToData;
return p1 == p2 ? 0 : (p1 < p2 ? -1 : 1);
}
});
TLongArrayList idMap = new TLongArrayList();
TLongObjectHashMap<TLongArrayList> restrictionMap = new TLongObjectHashMap<TLongArrayList>();
for (RouteSubregion rs : toLoad) {
if (rs.dataObjects == null) {
codedIS.seek(rs.filePointer + rs.shiftToData);
int limit = codedIS.readRawVarint32();
int oldLimit = codedIS.pushLimit(limit);
readRouteTreeData(rs, idMap, restrictionMap);
codedIS.popLimit(oldLimit);
}
for (RouteDataObject ro : rs.dataObjects) {
if (ro != null) {
matcher.publish(ro);
}
}
// free objects
rs.dataObjects = null;
}
}
public List<RouteSubregion> searchRouteRegionTree(SearchRequest<RouteDataObject> req, List<RouteSubregion> list,
List<RouteSubregion> toLoad) throws IOException {
for (RouteSubregion rs : list) {
if (req.intersects(rs.left, rs.top, rs.right, rs.bottom)) {
if (rs.subregions == null) {
codedIS.seek(rs.filePointer);
int old = codedIS.pushLimit(rs.length);
readRouteTree(rs, null, req.contains(rs.left, rs.top, rs.right, rs.bottom) ? -1 : 1, false);
codedIS.popLimit(old);
}
searchRouteRegionTree(req, rs.subregions, toLoad);
if (rs.shiftToData != 0) {
toLoad.add(rs);
}
}
}
return toLoad;
}
public List<RouteDataBorderLinePoint> searchBorderPoints(SearchRequest<RouteDataBorderLinePoint> req, RouteRegion r) throws IOException {
if(r.borderBoxPointer != 0) {
codedIS.seek(r.borderBoxPointer);
int old = codedIS.pushLimit(r.borderBoxLength);
TIntArrayList blocksToRead = new TIntArrayList();
readBorderLines(req, blocksToRead);
blocksToRead.sort();
for(int j = 0; j< blocksToRead.size() ; j++) {
codedIS.seek(blocksToRead.get(j));
int len = codedIS.readRawVarint32();
int oldLimit = codedIS.pushLimit(len);
readBorderLinePoints(req, r);
codedIS.popLimit(oldLimit);
}
codedIS.popLimit(old);
}
return req.getSearchResults();
}
private void readBorderLinePoints(SearchRequest<RouteDataBorderLinePoint> req, RouteRegion r) throws IOException {
int x = 0;
int y = 0;
long id = 0;
while (true) {
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case RouteBorderPointsBlock.X_FIELD_NUMBER: {
x = codedIS.readInt32();
break;
}
case RouteBorderPointsBlock.Y_FIELD_NUMBER: {
y = codedIS.readInt32();
break;
}
case RouteBorderPointsBlock.BASEID_FIELD_NUMBER: {
id = codedIS.readInt64();
break;
}
case RouteBorderPointsBlock.POINTS_FIELD_NUMBER:
int len = codedIS.readRawVarint32();
int oldLimit = codedIS.pushLimit(len);
RouteDataBorderLinePoint p = readBorderLinePoint(new RouteDataBorderLinePoint(r), x, y, id);
codedIS.popLimit(oldLimit);
x = p.x;
y = p.y;
id = p.id;
req.publish(p);
break;
default:
skipUnknownField(t);
break;
}
}
}
private RouteDataBorderLinePoint readBorderLinePoint(RouteDataBorderLinePoint p, int x, int y, long id) throws IOException {
while (true) {
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return p;
case RouteBorderPoint.DX_FIELD_NUMBER:
p.x = x + codedIS.readInt32();
break;
case RouteBorderPoint.DY_FIELD_NUMBER:
p.y = y + codedIS.readInt32();
break;
case RouteBorderPoint.ROADID_FIELD_NUMBER:
p.id = id + codedIS.readSInt64();
break;
case RouteBorderPoint.DIRECTION_FIELD_NUMBER:
p.direction = codedIS.readBool();
break;
case RouteBorderPoint.TYPES_FIELD_NUMBER:
TIntArrayList types = new TIntArrayList();
int len = codedIS.readRawVarint32();
int oldLimit = codedIS.pushLimit(len);
while(codedIS.getBytesUntilLimit() > 0) {
types.add(codedIS.readRawVarint32());
}
codedIS.popLimit(oldLimit);
p.types = types.toArray();
break;
default:
skipUnknownField(t);
break;
}
}
}
private void readBorderLines(SearchRequest<RouteDataBorderLinePoint> req, TIntArrayList blocksToRead) throws IOException {
while (true) {
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case RouteBorderBox.BORDERLINES_FIELD_NUMBER: {
int fp = codedIS.getTotalBytesRead();
int length = codedIS.readRawVarint32();
int old = codedIS.pushLimit(length);
RouteBorderLine ln = readBorderLine();
if(ln.hasTox() && req.intersects(ln.getX(), ln.getY(), ln.getTox(), ln.getY())) {
blocksToRead.add(ln.getShiftToPointsBlock() + fp);
// FIXME borders approach
// } else if(ln.hasToy() && req.intersects(ln.getX(), ln.getY(), ln.getX(), ln.getToy())) {
// blocksToRead.add(ln.getShiftToPointsBlock() + fp);
}
codedIS.popLimit(old);
break;
}
case RouteBorderBox.BLOCKS_FIELD_NUMBER:
return;
default:
skipUnknownField(t);
break;
}
}
}
private RouteBorderLine readBorderLine() throws IOException {
Builder bld = RouteBorderLine.newBuilder();
while (true) {
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return bld.build();
case RouteBorderLine.X_FIELD_NUMBER:
bld.setX(codedIS.readInt32());
break;
case RouteBorderLine.Y_FIELD_NUMBER:
bld.setY(codedIS.readInt32());
break;
case RouteBorderLine.TOX_FIELD_NUMBER:
bld.setTox(codedIS.readInt32());
break;
case RouteBorderLine.TOY_FIELD_NUMBER:
bld.setToy(codedIS.readInt32());
break;
case RouteBorderLine.SHIFTTOPOINTSBLOCK_FIELD_NUMBER:
bld.setShiftToPointsBlock(readInt());
break;
default:
skipUnknownField(t);
break;
}
}
}
public List<RouteSubregion> loadInteresectedPoints(SearchRequest<RouteDataObject> req, List<RouteSubregion> list,
List<RouteSubregion> toLoad) throws IOException {
for (RouteSubregion rs : list) {
if (req.intersects(rs.left, rs.top, rs.right, rs.bottom)) {
if (rs.subregions == null) {
codedIS.seek(rs.filePointer);
int old = codedIS.pushLimit(rs.length);
readRouteTree(rs, null, req.contains(rs.left, rs.top, rs.right, rs.bottom) ? -1 : 1, false);
codedIS.popLimit(old);
}
searchRouteRegionTree(req, rs.subregions, toLoad);
if (rs.shiftToData != 0) {
toLoad.add(rs);
}
}
}
return toLoad;
}
}

View file

@ -0,0 +1,480 @@
package net.osmand.binary;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.map.hash.TIntObjectHashMap;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import net.osmand.binary.BinaryMapIndexReader.SearchRequest;
import net.osmand.data.TransportStop;
import net.osmand.util.MapUtils;
import net.sf.junidecode.Junidecode;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.WireFormat;
public class BinaryMapTransportReaderAdapter {
private CodedInputStream codedIS;
private final BinaryMapIndexReader map;
protected BinaryMapTransportReaderAdapter(BinaryMapIndexReader map){
this.codedIS = map.codedIS;
this.map = map;
}
private void skipUnknownField(int t) throws IOException {
map.skipUnknownField(t);
}
private int readInt() throws IOException {
return map.readInt();
}
public static class TransportIndex extends BinaryIndexPart {
int left = 0;
int right = 0;
int top = 0;
int bottom = 0;
int stopsFileOffset = 0;
int stopsFileLength = 0;
public int getLeft() {
return left;
}
public int getRight() {
return right;
}
public int getTop() {
return top;
}
public int getBottom() {
return bottom;
}
IndexStringTable stringTable = null;
}
protected static class IndexStringTable {
int fileOffset = 0;
int length = 0;
// offset from start for each SIZE_OFFSET_ARRAY elements
// (SIZE_OFFSET_ARRAY + 1) offset = offsets[0] + skipOneString()
TIntArrayList offsets = new TIntArrayList();
}
protected void readTransportIndex(TransportIndex ind) throws IOException {
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case OsmandOdb.OsmAndTransportIndex.ROUTES_FIELD_NUMBER :
skipUnknownField(t);
break;
case OsmandOdb.OsmAndTransportIndex.NAME_FIELD_NUMBER :
ind.setName(codedIS.readString());
break;
case OsmandOdb.OsmAndTransportIndex.STOPS_FIELD_NUMBER :
ind.stopsFileLength = readInt();
ind.stopsFileOffset = codedIS.getTotalBytesRead();
int old = codedIS.pushLimit(ind.stopsFileLength);
readTransportBounds(ind);
codedIS.popLimit(old);
break;
case OsmandOdb.OsmAndTransportIndex.STRINGTABLE_FIELD_NUMBER :
IndexStringTable st = new IndexStringTable();
st.length = codedIS.readRawVarint32();
st.fileOffset = codedIS.getTotalBytesRead();
// Do not cache for now save memory
// readStringTable(st, 0, 20, true);
ind.stringTable = st;
codedIS.seek(st.length + st.fileOffset);
break;
default:
skipUnknownField(t);
break;
}
}
}
private void readTransportBounds(TransportIndex ind) throws IOException {
while(true){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
return;
case OsmandOdb.TransportStopsTree.LEFT_FIELD_NUMBER :
ind.left = codedIS.readSInt32();
break;
case OsmandOdb.TransportStopsTree.RIGHT_FIELD_NUMBER :
ind.right = codedIS.readSInt32();
break;
case OsmandOdb.TransportStopsTree.TOP_FIELD_NUMBER :
ind.top = codedIS.readSInt32();
break;
case OsmandOdb.TransportStopsTree.BOTTOM_FIELD_NUMBER :
ind.bottom = codedIS.readSInt32();
break;
default:
skipUnknownField(t);
break;
}
}
}
protected void searchTransportTreeBounds(int pleft, int pright, int ptop, int pbottom,
SearchRequest<TransportStop> req) throws IOException {
int init = 0;
int lastIndexResult = -1;
int cright = 0;
int cleft = 0;
int ctop = 0;
int cbottom = 0;
req.numberOfReadSubtrees++;
while(true){
if(req.isCancelled()){
return;
}
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
if(init == 0xf){
// coordinates are init
init = 0;
if(cright < req.left || cleft > req.right || ctop > req.bottom || cbottom < req.top){
return;
} else {
req.numberOfAcceptedSubtrees++;
}
}
switch (tag) {
case 0:
return;
case OsmandOdb.TransportStopsTree.BOTTOM_FIELD_NUMBER :
cbottom = codedIS.readSInt32() + pbottom;
init |= 1;
break;
case OsmandOdb.TransportStopsTree.LEFT_FIELD_NUMBER :
cleft = codedIS.readSInt32() + pleft;
init |= 2;
break;
case OsmandOdb.TransportStopsTree.RIGHT_FIELD_NUMBER :
cright = codedIS.readSInt32() + pright;
init |= 4;
break;
case OsmandOdb.TransportStopsTree.TOP_FIELD_NUMBER :
ctop = codedIS.readSInt32() + ptop;
init |= 8;
break;
case OsmandOdb.TransportStopsTree.LEAFS_FIELD_NUMBER :
int stopOffset = codedIS.getTotalBytesRead();
int length = codedIS.readRawVarint32();
int oldLimit = codedIS.pushLimit(length);
if(lastIndexResult == -1){
lastIndexResult = req.getSearchResults().size();
}
req.numberOfVisitedObjects++;
TransportStop transportStop = readTransportStop(stopOffset, cleft, cright, ctop, cbottom, req);
if(transportStop != null){
req.publish(transportStop);
}
codedIS.popLimit(oldLimit);
break;
case OsmandOdb.TransportStopsTree.SUBTREES_FIELD_NUMBER :
// left, ... already initialized
length = readInt();
int filePointer = codedIS.getTotalBytesRead();
if (req.limit == -1 || req.limit >= req.getSearchResults().size()) {
oldLimit = codedIS.pushLimit(length);
searchTransportTreeBounds(cleft, cright, ctop, cbottom, req);
codedIS.popLimit(oldLimit);
}
codedIS.seek(filePointer + length);
if(lastIndexResult >= 0){
throw new IllegalStateException();
}
break;
case OsmandOdb.TransportStopsTree.BASEID_FIELD_NUMBER :
long baseId = codedIS.readUInt64();
if (lastIndexResult != -1) {
for (int i = lastIndexResult; i < req.getSearchResults().size(); i++) {
TransportStop rs = req.getSearchResults().get(i);
rs.setId(rs.getId() + baseId);
}
}
break;
default:
skipUnknownField(t);
break;
}
}
}
private String regStr(TIntObjectHashMap<String> stringTable) throws IOException{
int i = codedIS.readUInt32();
stringTable.putIfAbsent(i, "");
return ((char) i)+"";
}
public net.osmand.data.TransportRoute getTransportRoute(int filePointer, TIntObjectHashMap<String> stringTable,
boolean onlyDescription) throws IOException {
codedIS.seek(filePointer);
int routeLength = codedIS.readRawVarint32();
int old = codedIS.pushLimit(routeLength);
net.osmand.data.TransportRoute dataObject = new net.osmand.data.TransportRoute();
boolean end = false;
long rid = 0;
int rx = 0;
int ry = 0;
long did = 0;
int dx = 0;
int dy = 0;
while(!end){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
end = true;
break;
case OsmandOdb.TransportRoute.DISTANCE_FIELD_NUMBER :
dataObject.setDistance(codedIS.readUInt32());
break;
case OsmandOdb.TransportRoute.ID_FIELD_NUMBER :
dataObject.setId(codedIS.readUInt64());
break;
case OsmandOdb.TransportRoute.REF_FIELD_NUMBER :
dataObject.setRef(codedIS.readString());
break;
case OsmandOdb.TransportRoute.TYPE_FIELD_NUMBER :
dataObject.setType(regStr(stringTable)); //$NON-NLS-1$
break;
case OsmandOdb.TransportRoute.NAME_EN_FIELD_NUMBER :
dataObject.setEnName(regStr(stringTable)); //$NON-NLS-1$
break;
case OsmandOdb.TransportRoute.NAME_FIELD_NUMBER :
dataObject.setName(regStr(stringTable)); //$NON-NLS-1$
break;
case OsmandOdb.TransportRoute.OPERATOR_FIELD_NUMBER:
dataObject.setOperator(regStr(stringTable)); //$NON-NLS-1$
break;
case OsmandOdb.TransportRoute.REVERSESTOPS_FIELD_NUMBER:
if(onlyDescription){
end = true;
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
break;
}
int length = codedIS.readRawVarint32();
int olds = codedIS.pushLimit(length);
TransportStop stop = readTransportRouteStop(dx, dy, did, stringTable);
dataObject.getBackwardStops().add(stop);
did = stop.getId();
dx = (int) MapUtils.getTileNumberX(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, stop.getLocation().getLongitude());
dy = (int) MapUtils.getTileNumberY(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, stop.getLocation().getLatitude());
codedIS.popLimit(olds);
break;
case OsmandOdb.TransportRoute.DIRECTSTOPS_FIELD_NUMBER:
if(onlyDescription){
end = true;
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
break;
}
length = codedIS.readRawVarint32();
olds = codedIS.pushLimit(length);
stop = readTransportRouteStop(rx, ry, rid, stringTable);
dataObject.getForwardStops().add(stop);
rid = stop.getId();
rx = (int) MapUtils.getTileNumberX(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, stop.getLocation().getLongitude());
ry = (int) MapUtils.getTileNumberY(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, stop.getLocation().getLatitude());
codedIS.popLimit(olds);
break;
default:
skipUnknownField(t);
break;
}
}
codedIS.popLimit(old);
return dataObject;
}
protected void initializeStringTable(TransportIndex ind, TIntObjectHashMap<String> stringTable) throws IOException {
int[] values = stringTable.keys();
Arrays.sort(values);
codedIS.seek(ind.stringTable.fileOffset);
int oldLimit = codedIS.pushLimit(ind.stringTable.length);
int current = 0;
int i = 0;
while (i < values.length) {
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
break;
case OsmandOdb.StringTable.S_FIELD_NUMBER:
if (current == values[i]) {
String value = codedIS.readString();
stringTable.put(values[i], value);
i++;
} else {
skipUnknownField(t);
}
current ++;
break;
default:
skipUnknownField(t);
break;
}
}
codedIS.popLimit(oldLimit);
}
protected void initializeNames(boolean onlyDescription, net.osmand.data.TransportRoute dataObject,
TIntObjectHashMap<String> stringTable) throws IOException {
if(dataObject.getName().length() > 0){
dataObject.setName(stringTable.get(dataObject.getName().charAt(0)));
}
if(dataObject.getEnName().length() > 0){
dataObject.setEnName(stringTable.get(dataObject.getEnName().charAt(0)));
}
if(dataObject.getName().length() > 0 && dataObject.getEnName().length() == 0){
dataObject.setEnName(Junidecode.unidecode(dataObject.getName()));
}
if(dataObject.getOperator().length() > 0){
dataObject.setOperator(stringTable.get(dataObject.getOperator().charAt(0)));
}
if(dataObject.getType().length() > 0){
dataObject.setType(stringTable.get(dataObject.getType().charAt(0)));
}
for (int i = 0; i < 2 && !onlyDescription; i++) {
List<TransportStop> stops = i == 0 ? dataObject.getForwardStops() : dataObject.getBackwardStops();
for (TransportStop s : stops) {
initializeNames(stringTable, s);
}
}
}
protected void initializeNames(TIntObjectHashMap<String> stringTable, TransportStop s) {
if (s.getName().length() > 0) {
s.setName(stringTable.get(s.getName().charAt(0)));
}
if (s.getEnName().length() > 0) {
s.setEnName(stringTable.get(s.getEnName().charAt(0)));
}
if (s.getEnName().length() == 0) {
s.setEnName(Junidecode.unidecode(s.getName()));
}
}
private TransportStop readTransportRouteStop(int dx, int dy, long did, TIntObjectHashMap<String> stringTable) throws IOException {
TransportStop dataObject = new TransportStop();
boolean end = false;
while(!end){
int t = codedIS.readTag();
int tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
end = true;
break;
case OsmandOdb.TransportRouteStop.NAME_EN_FIELD_NUMBER :
dataObject.setEnName(regStr(stringTable)); //$NON-NLS-1$
break;
case OsmandOdb.TransportRouteStop.NAME_FIELD_NUMBER :
dataObject.setName(regStr(stringTable)); //$NON-NLS-1$
break;
case OsmandOdb.TransportRouteStop.ID_FIELD_NUMBER :
did += codedIS.readSInt64();
break;
case OsmandOdb.TransportRouteStop.DX_FIELD_NUMBER :
dx += codedIS.readSInt32();
break;
case OsmandOdb.TransportRouteStop.DY_FIELD_NUMBER :
dy += codedIS.readSInt32();
break;
default:
skipUnknownField(t);
break;
}
}
dataObject.setId(did);
dataObject.setLocation(MapUtils.getLatitudeFromTile(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, dy), MapUtils.getLongitudeFromTile(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, dx));
return dataObject;
}
private TransportStop readTransportStop(int shift, int cleft, int cright, int ctop, int cbottom, SearchRequest<TransportStop> req) throws IOException {
int tag = WireFormat.getTagFieldNumber(codedIS.readTag());
if(OsmandOdb.TransportStop.DX_FIELD_NUMBER != tag) {
throw new IllegalArgumentException();
}
int x = codedIS.readSInt32() + cleft;
tag = WireFormat.getTagFieldNumber(codedIS.readTag());
if(OsmandOdb.TransportStop.DY_FIELD_NUMBER != tag) {
throw new IllegalArgumentException();
}
int y = codedIS.readSInt32() + ctop;
if(req.right < x || req.left > x || req.top > y || req.bottom < y){
codedIS.skipRawBytes(codedIS.getBytesUntilLimit());
return null;
}
req.numberOfAcceptedObjects++;
req.cacheTypes.clear();
TransportStop dataObject = new TransportStop();
dataObject.setLocation(MapUtils.getLatitudeFromTile(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, y), MapUtils.getLongitudeFromTile(BinaryMapIndexReader.TRANSPORT_STOP_ZOOM, x));
dataObject.setFileOffset(shift);
while(true){
int t = codedIS.readTag();
tag = WireFormat.getTagFieldNumber(t);
switch (tag) {
case 0:
dataObject.setReferencesToRoutes(req.cacheTypes.toArray());
if(dataObject.getEnName().length() == 0){
dataObject.setEnName(Junidecode.unidecode(dataObject.getName()));
}
return dataObject;
case OsmandOdb.TransportStop.ROUTES_FIELD_NUMBER :
req.cacheTypes.add(shift - codedIS.readUInt32());
break;
case OsmandOdb.TransportStop.NAME_EN_FIELD_NUMBER :
if (req.stringTable != null) {
dataObject.setEnName(regStr(req.stringTable)); //$NON-NLS-1$
} else {
skipUnknownField(t);
}
break;
case OsmandOdb.TransportStop.NAME_FIELD_NUMBER :
if (req.stringTable != null) {
dataObject.setName(regStr(req.stringTable)); //$NON-NLS-1$
} else {
skipUnknownField(t);
}
break;
case OsmandOdb.TransportStop.ID_FIELD_NUMBER :
dataObject.setId(codedIS.readSInt64());
break;
default:
skipUnknownField(t);
break;
}
}
}
}

View file

@ -0,0 +1,327 @@
package net.osmand.binary;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import net.osmand.PlatformUtil;
import net.osmand.binary.BinaryMapAddressReaderAdapter.AddressRegion;
import net.osmand.binary.BinaryMapAddressReaderAdapter.CitiesBlock;
import net.osmand.binary.BinaryMapIndexReader.MapIndex;
import net.osmand.binary.BinaryMapIndexReader.MapRoot;
import net.osmand.binary.BinaryMapPoiReaderAdapter.PoiRegion;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteSubregion;
import net.osmand.binary.BinaryMapTransportReaderAdapter.IndexStringTable;
import net.osmand.binary.BinaryMapTransportReaderAdapter.TransportIndex;
import net.osmand.binary.OsmandIndex.AddressPart;
import net.osmand.binary.OsmandIndex.CityBlock;
import net.osmand.binary.OsmandIndex.FileIndex;
import net.osmand.binary.OsmandIndex.MapLevel;
import net.osmand.binary.OsmandIndex.MapPart;
import net.osmand.binary.OsmandIndex.OsmAndStoredIndex;
import net.osmand.binary.OsmandIndex.PoiPart;
import net.osmand.binary.OsmandIndex.RoutingPart;
import net.osmand.binary.OsmandIndex.RoutingSubregion;
import net.osmand.binary.OsmandIndex.TransportPart;
import net.osmand.util.MapUtils;
import org.apache.commons.logging.Log;
public class CachedOsmandIndexes {
private OsmAndStoredIndex storedIndex;
private OsmAndStoredIndex.Builder storedIndexBuilder;
private Log log = PlatformUtil.getLog(CachedOsmandIndexes.class);
private boolean hasChanged = true;
public static final int VERSION = 2;
public void addToCache(BinaryMapIndexReader reader, File f) {
hasChanged = true;
if(storedIndexBuilder == null) {
storedIndexBuilder = OsmandIndex.OsmAndStoredIndex.newBuilder();
storedIndexBuilder.setVersion(VERSION);
storedIndexBuilder.setDateCreated(System.currentTimeMillis());
if(storedIndex != null) {
for(FileIndex ex : storedIndex.getFileIndexList()) {
storedIndexBuilder.addFileIndex(ex);
}
}
}
FileIndex.Builder fileIndex = OsmandIndex.FileIndex.newBuilder();
long d = reader.getDateCreated();
fileIndex.setDateModified(d== 0?f.lastModified() : d);
fileIndex.setSize(f.length());
fileIndex.setVersion(reader.getVersion());
fileIndex.setFileName(f.getName());
for(MapIndex index : reader.getMapIndexes()) {
MapPart.Builder map = OsmandIndex.MapPart.newBuilder();
map.setSize(index.getLength());
map.setOffset(index.getFilePointer());
if(index.getName() != null) {
map.setName(index.getName());
}
for(MapRoot mr : index.getRoots() ) {
MapLevel.Builder lev = OsmandIndex.MapLevel.newBuilder();
lev.setSize(mr.length);
lev.setOffset(mr.filePointer);
lev.setLeft(mr.left);
lev.setRight(mr.right);
lev.setTop(mr.top);
lev.setBottom(mr.bottom);
lev.setMinzoom(mr.minZoom);
lev.setMaxzoom(mr.maxZoom);
map.addLevels(lev);
}
fileIndex.addMapIndex(map);
}
for(AddressRegion index : reader.getAddressIndexes()) {
AddressPart.Builder addr = OsmandIndex.AddressPart.newBuilder();
addr.setSize(index.getLength());
addr.setOffset(index.getFilePointer());
if(index.getName() != null) {
addr.setName(index.getName());
}
if(index.getEnName() != null) {
addr.setNameEn(index.getEnName());
}
addr.setIndexNameOffset(index.getIndexNameOffset());
for(CitiesBlock mr : index.getCities() ) {
CityBlock.Builder cblock = OsmandIndex.CityBlock.newBuilder();
cblock.setSize(mr.length);
cblock.setOffset(mr.filePointer);
cblock.setType(mr.type);
addr.addCities(cblock);
}
fileIndex.addAddressIndex(addr);
}
for(PoiRegion index : reader.getPoiIndexes()) {
PoiPart.Builder poi = OsmandIndex.PoiPart.newBuilder();
poi.setSize(index.getLength());
poi.setOffset(index.getFilePointer());
if(index.getName() != null) {
poi.setName(index.getName());
}
poi.setLeft(MapUtils.get31TileNumberX(index.getLeftLongitude()));
poi.setRight(MapUtils.get31TileNumberX(index.getRightLongitude()));
poi.setTop(MapUtils.get31TileNumberY(index.getTopLatitude()));
poi.setBottom(MapUtils.get31TileNumberY(index.getBottomLatitude()));
fileIndex.addPoiIndex(poi.build());
}
for(TransportIndex index : reader.getTransportIndexes()) {
TransportPart.Builder transport = OsmandIndex.TransportPart.newBuilder();
transport.setSize(index.getLength());
transport.setOffset(index.getFilePointer());
if(index.getName() != null) {
transport.setName(index.getName());
}
transport.setLeft(index.getLeft());
transport.setRight(index.getRight());
transport.setTop(index.getTop());
transport.setBottom(index.getBottom());
transport.setStopsTableLength(index.stopsFileLength);
transport.setStopsTableOffset(index.stopsFileOffset);
transport.setStringTableLength(index.stringTable.length);
transport.setStringTableOffset(index.stringTable.fileOffset);
fileIndex.addTransportIndex(transport);
}
for(RouteRegion index : reader.getRoutingIndexes()) {
RoutingPart.Builder routing = OsmandIndex.RoutingPart.newBuilder();
routing.setSize(index.getLength());
routing.setOffset(index.getFilePointer());
if(index.getName() != null) {
routing.setName(index.getName());
}
for(RouteSubregion sub : index.getSubregions()) {
addRouteSubregion(routing, sub, false);
}
for(RouteSubregion sub : index.getBaseSubregions()) {
addRouteSubregion(routing, sub, true);
}
fileIndex.addRoutingIndex(routing);
}
storedIndexBuilder.addFileIndex(fileIndex);
}
private void addRouteSubregion(RoutingPart.Builder routing, RouteSubregion sub, boolean base) {
OsmandIndex.RoutingSubregion.Builder rpart = OsmandIndex.RoutingSubregion.newBuilder();
rpart.setSize(sub.length);
rpart.setOffset(sub.filePointer);
rpart.setLeft(sub.left);
rpart.setRight(sub.right);
rpart.setTop(sub.top);
rpart.setBasemap(base);
rpart.setBottom(sub.bottom);
rpart.setShifToData(sub.shiftToData);
routing.addSubregions(rpart);
}
public BinaryMapIndexReader getReader(File f) throws IOException {
RandomAccessFile mf = new RandomAccessFile(f.getPath(), "r");
FileIndex found = null;
if (storedIndex != null) {
for (int i = 0; i < storedIndex.getFileIndexCount(); i++) {
FileIndex fi = storedIndex.getFileIndex(i);
if (f.length() == fi.getSize() && f.getName().equals(fi.getFileName())) {
// f.lastModified() == fi.getDateModified()
found = fi;
break;
}
}
}
BinaryMapIndexReader reader = null;
if (found == null) {
long val = System.currentTimeMillis();
reader = new BinaryMapIndexReader(mf);
addToCache(reader, f);
if (log.isDebugEnabled()) {
log.debug("Initializing db " + f.getAbsolutePath() + " " + (System.currentTimeMillis() - val ) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
} else {
reader = initFileIndex(found, mf);
}
return reader;
}
private BinaryMapIndexReader initFileIndex(FileIndex found, RandomAccessFile mf) throws IOException {
BinaryMapIndexReader reader = new BinaryMapIndexReader(mf, false);
reader.version = found.getVersion();
reader.dateCreated = found.getDateModified();
for(MapPart index : found.getMapIndexList()) {
MapIndex mi = new MapIndex();
mi.length = (int) index.getSize();
mi.filePointer = (int) index.getOffset();
mi.name = index.getName();
for(MapLevel mr : index.getLevelsList()) {
MapRoot root = new MapRoot();
root.length = (int) mr.getSize();
root.filePointer = (int) mr.getOffset();
root.left = mr.getLeft();
root.right = mr.getRight();
root.top = mr.getTop();
root.bottom = mr.getBottom();
root.minZoom = mr.getMinzoom();
root.maxZoom = mr.getMaxzoom();
mi.roots.add(root);
}
reader.mapIndexes.add(mi);
reader.indexes.add(mi);
reader.basemap = reader.basemap || mi.isBaseMap();
}
for(AddressPart index : found.getAddressIndexList()) {
AddressRegion mi = new AddressRegion();
mi.length = (int) index.getSize();
mi.filePointer = (int) index.getOffset();
mi.name = index.getName();
mi.enName = index.getNameEn();
mi.indexNameOffset = index.getIndexNameOffset();
for(CityBlock mr : index.getCitiesList() ) {
CitiesBlock cblock = new CitiesBlock();
cblock.length = (int) mr.getSize();
cblock.filePointer = (int) mr.getOffset();
cblock.type = mr.getType();
mi.cities.add(cblock);
}
reader.addressIndexes.add(mi);
reader.indexes.add(mi);
}
for(PoiPart index : found.getPoiIndexList()) {
PoiRegion mi = new PoiRegion();
mi.length = (int) index.getSize();
mi.filePointer = (int) index.getOffset();
mi.name = index.getName();
mi.leftLongitude = MapUtils.get31LongitudeX(index.getLeft());
mi.rightLongitude = MapUtils.get31LongitudeX(index.getRight());
mi.topLatitude =MapUtils.get31LatitudeY(index.getTop());
mi.bottomLatitude = MapUtils.get31LatitudeY(index.getBottom());
reader.poiIndexes.add(mi);
reader.indexes.add(mi);
}
for(TransportPart index : found.getTransportIndexList()) {
TransportIndex mi = new TransportIndex();
mi.length = (int) index.getSize();
mi.filePointer = (int) index.getOffset();
mi.name = index.getName();
mi.left = index.getLeft();
mi.right =index.getRight();
mi.top = index.getTop();
mi.bottom = index.getBottom();
mi.stopsFileLength = index.getStopsTableLength();
mi.stopsFileOffset = index.getStopsTableOffset();
mi.stringTable = new IndexStringTable();
mi.stringTable.fileOffset = index.getStringTableOffset();
mi.stringTable.length = index.getStringTableLength();
reader.transportIndexes.add(mi);
reader.indexes.add(mi);
}
for(RoutingPart index : found.getRoutingIndexList()) {
RouteRegion mi = new RouteRegion();
mi.length = (int) index.getSize();
mi.filePointer = (int) index.getOffset();
mi.name = index.getName();
for(RoutingSubregion mr : index.getSubregionsList()) {
RouteSubregion sub = new RouteSubregion(mi);
sub.length = (int) mr.getSize();
sub.filePointer = (int) mr.getOffset();
sub.left = mr.getLeft();
sub.right = mr.getRight();
sub.top = mr.getTop();
sub.bottom = mr.getBottom();
sub.shiftToData = mr.getShifToData();
if(mr.getBasemap()) {
mi.basesubregions.add(sub);
} else {
mi.subregions.add(sub);
}
}
reader.routingIndexes.add(mi);
reader.indexes.add(mi);
}
return reader;
}
public void readFromFile(File f, int version) throws IOException {
long time = System.currentTimeMillis();
FileInputStream is = new FileInputStream(f);
try {
storedIndex = OsmandIndex.OsmAndStoredIndex.newBuilder().mergeFrom(is).build();
hasChanged = false;
if(storedIndex.getVersion() != version){
storedIndex = null;
}
} finally {
is.close();
}
log.info("Initialize cache " + (System.currentTimeMillis() - time));
}
public void writeToFile(File f) throws IOException {
if (hasChanged) {
FileOutputStream outputStream = new FileOutputStream(f);
try {
storedIndexBuilder.build().writeTo(outputStream);
} finally {
outputStream.close();
}
}
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,75 @@
package net.osmand.binary;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteTypeRule;
public class RouteDataBorderLinePoint extends RouteDataObject {
// all these arrays supposed to be immutable!
// These feilds accessible from C++
public int x;
public int y;
public boolean direction;
// used in context calculation
public float distanceToStartPoint;
public float distanceToEndPoint;
public RouteDataBorderLinePoint(RouteRegion region) {
super(region);
}
public float getMaximumSpeed(){
int sz = types.length;
for (int i = 0; i < sz; i++) {
RouteTypeRule r = region.quickGetEncodingRule(types[i]);
float maxSpeed = r.maxSpeed();
if (maxSpeed > 0) {
return maxSpeed;
}
}
return 0;
}
public int getOneway() {
int sz = types.length;
for (int i = 0; i < sz; i++) {
RouteTypeRule r = region.quickGetEncodingRule(types[i]);
if (r.onewayDirection() != 0) {
return r.onewayDirection();
} else if (r.roundabout()) {
return 1;
}
}
return 0;
}
public String getRoute() {
int sz = types.length;
for (int i = 0; i < sz; i++) {
RouteTypeRule r = region.quickGetEncodingRule(types[i]);
if ("route".equals(r.getTag())) {
return r.getValue();
}
}
return null;
}
public String getHighway() {
String highway = null;
int sz = types.length;
for (int i = 0; i < sz; i++) {
RouteTypeRule r = region.quickGetEncodingRule(types[i]);
highway = r.highwayRoad();
if (highway != null) {
break;
}
}
return highway;
}
@Override
public String toString() {
return "Border line " + id;
}
}

View file

@ -0,0 +1,269 @@
package net.osmand.binary;
import gnu.trove.map.hash.TIntObjectHashMap;
import java.text.MessageFormat;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteRegion;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteTypeRule;
public class RouteDataObject {
/*private */static final int RESTRICTION_SHIFT = 3;
/*private */static final int RESTRICTION_MASK = 7;
public final RouteRegion region;
// all these arrays supposed to be immutable!
// These fields accessible from C++
public int[] types;
public int[] pointsX;
public int[] pointsY;
public long[] restrictions;
public int[][] pointTypes;
public long id;
public TIntObjectHashMap<String> names;
public final static float NONE_MAX_SPEED = 40f;
public RouteDataObject(RouteRegion region) {
this.region = region;
}
public RouteDataObject(RouteRegion region, int[] nameIds, String[] nameValues) {
this.region = region;
if (nameIds.length > 0) {
names = new TIntObjectHashMap<String>();
}
for (int i = 0; i < nameIds.length; i++) {
names.put(nameIds[i], nameValues[i]);
}
}
public RouteDataObject(RouteDataObject copy) {
this.region = copy.region;
this.pointsX = copy.pointsX;
this.pointsY = copy.pointsY;
this.types = copy.types;
this.names = copy.names;
this.restrictions = copy.restrictions;
this.pointTypes = copy.pointTypes;
this.id = copy.id;
}
public long getId() {
return id;
}
public String getName(){
if(names != null ) {
return names.get(region.nameTypeRule);
}
return null;
}
public String getRef(){
if(names != null ) {
return names.get(region.refTypeRule);
}
return null;
}
public String getDestinationName(){
if(names != null ) {
return names.get(region.destinationTypeRule);
}
return null;
}
public int getPoint31XTile(int i) {
return pointsX[i];
}
public int getPoint31YTile(int i) {
return pointsY[i];
}
public int getPointsLength() {
return pointsX.length;
}
public int getRestrictionLength() {
return restrictions == null ? 0 : restrictions.length;
}
public int getRestrictionType(int i) {
return (int) (restrictions[i] & RESTRICTION_MASK);
}
public long getRestrictionId(int i) {
return restrictions[i] >> RESTRICTION_SHIFT;
}
public void insert(int pos, int x31, int y31) {
int[] opointsX = pointsX;
int[] opointsY = pointsY;
int[][] opointTypes = pointTypes;
pointsX = new int[pointsX.length + 1];
pointsY = new int[pointsY.length + 1];
boolean insTypes = this.pointTypes != null && this.pointTypes.length > pos;
if (insTypes) {
pointTypes = new int[opointTypes.length + 1][];
}
int i = 0;
for (; i < pos; i++) {
pointsX[i] = opointsX[i];
pointsY[i] = opointsY[i];
if (insTypes) {
pointTypes[i] = opointTypes[i];
}
}
pointsX[i] = x31;
pointsY[i] = y31;
if (insTypes) {
pointTypes[i] = null;
}
for (i = i + 1; i < pointsX.length; i++) {
pointsX[i] = opointsX[i - 1];
pointsY[i] = opointsY[i - 1];
if (insTypes && i < pointTypes.length) {
pointTypes[i] = opointTypes[i - 1];
}
}
}
public int[] getPointTypes(int ind) {
if (pointTypes == null || ind >= pointTypes.length) {
return null;
}
return pointTypes[ind];
}
public int[] getTypes() {
return types;
}
public float getMaximumSpeed(){
int sz = types.length;
for (int i = 0; i < sz; i++) {
RouteTypeRule r = region.quickGetEncodingRule(types[i]);
float maxSpeed = r.maxSpeed();
if (maxSpeed > 0) {
return maxSpeed;
}
}
return 0;
}
public boolean loop(){
return pointsX[0] == pointsX[pointsX.length - 1] && pointsY[0] == pointsY[pointsY.length - 1] ;
}
public boolean roundabout(){
int sz = types.length;
for(int i=0; i<sz; i++) {
RouteTypeRule r = region.quickGetEncodingRule(types[i]);
if(r.roundabout()) {
return true;
} else if(r.onewayDirection() != 0 && loop()) {
return true;
}
}
return false;
}
public int getOneway() {
int sz = types.length;
for (int i = 0; i < sz; i++) {
RouteTypeRule r = region.quickGetEncodingRule(types[i]);
if (r.onewayDirection() != 0) {
return r.onewayDirection();
} else if (r.roundabout()) {
return 1;
}
}
return 0;
}
public String getRoute() {
int sz = types.length;
for (int i = 0; i < sz; i++) {
RouteTypeRule r = region.quickGetEncodingRule(types[i]);
if ("route".equals(r.getTag())) {
return r.getValue();
}
}
return null;
}
public String getHighway() {
return getHighway(types, region);
}
public static String getHighway(int[] types, RouteRegion region) {
String highway = null;
int sz = types.length;
for (int i = 0; i < sz; i++) {
RouteTypeRule r = region.quickGetEncodingRule(types[i]);
highway = r.highwayRoad();
if (highway != null) {
break;
}
}
return highway;
}
public int getLanes() {
int sz = types.length;
for (int i = 0; i < sz; i++) {
RouteTypeRule r = region.quickGetEncodingRule(types[i]);
int ln = r.lanes();
if (ln > 0) {
return ln;
}
}
return -1;
}
public double directionRoute(int startPoint, boolean plus) {
// same goes to C++
// Victor : the problem to put more than 5 meters that BinaryRoutePlanner will treat
// 2 consequent Turn Right as UT and here 2 points will have same turn angle
// So it should be fix in both places
return directionRoute(startPoint, plus, 5);
}
// Gives route direction of EAST degrees from NORTH ]-PI, PI]
public double directionRoute(int startPoint, boolean plus, float dist) {
int x = getPoint31XTile(startPoint);
int y = getPoint31YTile(startPoint);
int nx = startPoint;
int px = x;
int py = y;
double total = 0;
do {
if (plus) {
nx++;
if (nx >= getPointsLength()) {
break;
}
} else {
nx--;
if (nx < 0) {
break;
}
}
px = getPoint31XTile(nx);
py = getPoint31YTile(nx);
// translate into meters
total += Math.abs(px - x) * 0.011d + Math.abs(py - y) * 0.01863d;
} while (total < dist);
return -Math.atan2( x - px, y - py );
}
@Override
public String toString() {
String name = getName();
String rf = getRef();
return MessageFormat.format("Road id {0} name {1} ref {2}", getId()+"", name == null ? "" : name, rf == null ? "" : rf);
}
}

View file

@ -0,0 +1,56 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 2.0.7
*
* Do not make changes to this file unless you know what you are doing--modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
package net.osmand.bridge;
public class CoreOsmAnd {
public static int get31TileNumberX(double longitude) {
return CoreOsmAndJNI.get31TileNumberX(longitude);
}
public static int get31TileNumberY(double latitude) {
return CoreOsmAndJNI.get31TileNumberY(latitude);
}
public static double get31LongitudeX(int x) {
return CoreOsmAndJNI.get31LongitudeX(x);
}
public static double get31LatitudeY(int y) {
return CoreOsmAndJNI.get31LatitudeY(y);
}
public static double getTileNumberX(float zoom, double longitude) {
return CoreOsmAndJNI.getTileNumberX(zoom, longitude);
}
public static double getTileNumberY(float zoom, double latitude) {
return CoreOsmAndJNI.getTileNumberY(zoom, latitude);
}
public static double checkLatitude(double latitude) {
return CoreOsmAndJNI.checkLatitude(latitude);
}
public static double checkLongitude(double longitude) {
return CoreOsmAndJNI.checkLongitude(longitude);
}
public static double getPowZoom(float zoom) {
return CoreOsmAndJNI.getPowZoom(zoom);
}
public static double getLongitudeFromTile(float zoom, double x) {
return CoreOsmAndJNI.getLongitudeFromTile(zoom, x);
}
public static double getLatitudeFromTile(float zoom, double y) {
return CoreOsmAndJNI.getLatitudeFromTile(zoom, y);
}
}

View file

@ -0,0 +1,37 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 2.0.7
*
* Do not make changes to this file unless you know what you are doing--modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
package net.osmand.bridge;
public class CoreOsmAndJNI {
public final static native long new_StringVector__SWIG_0();
public final static native long new_StringVector__SWIG_1(long jarg1);
public final static native long StringVector_size(long jarg1, StringVector jarg1_);
public final static native long StringVector_capacity(long jarg1, StringVector jarg1_);
public final static native void StringVector_reserve(long jarg1, StringVector jarg1_, long jarg2);
public final static native boolean StringVector_isEmpty(long jarg1, StringVector jarg1_);
public final static native void StringVector_clear(long jarg1, StringVector jarg1_);
public final static native void StringVector_add(long jarg1, StringVector jarg1_, String jarg2);
public final static native String StringVector_get(long jarg1, StringVector jarg1_, int jarg2);
public final static native void StringVector_set(long jarg1, StringVector jarg1_, int jarg2, String jarg3);
public final static native void delete_StringVector(long jarg1);
public final static native int ObfInspector_inspector(long jarg1, StringVector jarg1_);
public final static native long new_ObfInspector();
public final static native void delete_ObfInspector(long jarg1);
public final static native int get31TileNumberX(double jarg1);
public final static native int get31TileNumberY(double jarg1);
public final static native double get31LongitudeX(int jarg1);
public final static native double get31LatitudeY(int jarg1);
public final static native double getTileNumberX(float jarg1, double jarg2);
public final static native double getTileNumberY(float jarg1, double jarg2);
public final static native double checkLatitude(double jarg1);
public final static native double checkLongitude(double jarg1);
public final static native double getPowZoom(float jarg1);
public final static native double getLongitudeFromTile(float jarg1, double jarg2);
public final static native double getLatitudeFromTile(float jarg1, double jarg2);
}

View file

@ -0,0 +1,46 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 2.0.7
*
* Do not make changes to this file unless you know what you are doing--modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
package net.osmand.bridge;
public class ObfInspector {
private long swigCPtr;
protected boolean swigCMemOwn;
protected ObfInspector(long cPtr, boolean cMemoryOwn) {
swigCMemOwn = cMemoryOwn;
swigCPtr = cPtr;
}
protected static long getCPtr(ObfInspector obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected void finalize() {
delete();
}
public synchronized void delete() {
if (swigCPtr != 0) {
if (swigCMemOwn) {
swigCMemOwn = false;
CoreOsmAndJNI.delete_ObfInspector(swigCPtr);
}
swigCPtr = 0;
}
}
public static int inspector(StringVector argv) {
return CoreOsmAndJNI.ObfInspector_inspector(StringVector.getCPtr(argv), argv);
}
public ObfInspector() {
this(CoreOsmAndJNI.new_ObfInspector(), true);
}
}

View file

@ -0,0 +1,84 @@
/* ----------------------------------------------------------------------------
* This file was automatically generated by SWIG (http://www.swig.org).
* Version 2.0.7
*
* Do not make changes to this file unless you know what you are doing--modify
* the SWIG interface file instead.
* ----------------------------------------------------------------------------- */
package net.osmand.bridge;
import java.util.AbstractList;
import java.util.List;
public class StringVector extends AbstractList<String> {
private long swigCPtr;
protected boolean swigCMemOwn;
protected StringVector(long cPtr, boolean cMemoryOwn) {
swigCMemOwn = cMemoryOwn;
swigCPtr = cPtr;
}
protected static long getCPtr(StringVector obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected void finalize() {
delete();
}
public synchronized void delete() {
if (swigCPtr != 0) {
if (swigCMemOwn) {
swigCMemOwn = false;
CoreOsmAndJNI.delete_StringVector(swigCPtr);
}
swigCPtr = 0;
}
}
public StringVector() {
this(CoreOsmAndJNI.new_StringVector__SWIG_0(), true);
}
public StringVector(long n) {
this(CoreOsmAndJNI.new_StringVector__SWIG_1(n), true);
}
public int size() {
return (int) CoreOsmAndJNI.StringVector_size(swigCPtr, this);
}
public long capacity() {
return CoreOsmAndJNI.StringVector_capacity(swigCPtr, this);
}
public void reserve(long n) {
CoreOsmAndJNI.StringVector_reserve(swigCPtr, this, n);
}
public boolean isEmpty() {
return CoreOsmAndJNI.StringVector_isEmpty(swigCPtr, this);
}
public void clear() {
CoreOsmAndJNI.StringVector_clear(swigCPtr, this);
}
public boolean add(String x) {
CoreOsmAndJNI.StringVector_add(swigCPtr, this, x);
return true;
}
public String get(int i) {
return CoreOsmAndJNI.StringVector_get(swigCPtr, this, i);
}
public String set(int i, String val) {
String prev = CoreOsmAndJNI.StringVector_get(swigCPtr, this, i);
CoreOsmAndJNI.StringVector_set(swigCPtr, this, i, val);
return prev;
}
}

View file

@ -0,0 +1,72 @@
package net.osmand.data;
public class Amenity extends MapObject {
private static final long serialVersionUID = 132083949926339552L;
private String subType;
private AmenityType type;
private String openingHours;
private String phone;
private String description;
private String site;
public Amenity(){
}
public AmenityType getType(){
return type;
}
public String getSubType(){
return subType;
}
public void setType(AmenityType type) {
this.type = type;
}
public void setSubType(String subType) {
this.subType = subType;
}
public String getOpeningHours() {
return openingHours;
}
public void setOpeningHours(String openingHours) {
this.openingHours = openingHours;
}
@Override
public String toString() {
return type.toString() + " : " + subType + " "+ getName();
}
public String getSite() {
return site;
}
public void setSite(String site) {
this.site = site;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View file

@ -0,0 +1,59 @@
package net.osmand.data;
// http://wiki.openstreetmap.org/wiki/Amenity
// POI tags : amenity, leisure, shop, sport, tourism, historic; accessories (internet-access), natural ?
public enum AmenityType {
// Some of those types are subtypes of Amenity tag
SUSTENANCE("amenity"), // restaurant, cafe ... //$NON-NLS-1$ //$NON-NLS-2$
EDUCATION("amenity"), // school, ... //$NON-NLS-1$ //$NON-NLS-2$
TRANSPORTATION("amenity"), // car_wash, parking, ... //$NON-NLS-1$ //$NON-NLS-2$
FINANCE("amenity"), // bank, atm, ... //$NON-NLS-1$ //$NON-NLS-2$
HEALTHCARE("amenity"), // hospital ... //$NON-NLS-1$ //$NON-NLS-2$
ENTERTAINMENT("amenity"), // cinema, ... (+! sauna, brothel) //$NON-NLS-1$ //$NON-NLS-2$
TOURISM("tourism"), // [TAG] tourism hotel, sights, museum .. //$NON-NLS-1$ //$NON-NLS-2$
HISTORIC("historic"), // [TAG] historic places, monuments (should we unify tourism/historic) //$NON-NLS-1$ //$NON-NLS-2$
NATURAL("natural"), // [TAG] natural places, monuments (should we unify tourism/historic) //$NON-NLS-1$ //$NON-NLS-2$
SHOP("shop"), // [TAG] amenity convenience (product), clothes... //$NON-NLS-1$ //$NON-NLS-2$
LEISURE("leisure"), // [TAG] leisure //$NON-NLS-1$ //$NON-NLS-2$
SPORT("sport"), // [TAG] sport //$NON-NLS-1$ //$NON-NLS-2$
BARRIER("barrier"), // [TAG] barrier + traffic_calming //$NON-NLS-1$ //$NON-NLS-2$
LANDUSE("landuse"), // [TAG] landuse //$NON-NLS-1$ //$NON-NLS-2$
MAN_MADE("man_made"), // [TAG] man_made and others //$NON-NLS-1$ //$NON-NLS-2$
OFFICE("office"), // [TAG] office //$NON-NLS-1$ //$NON-NLS-2$
EMERGENCY("emergency"), // [TAG] emergency //$NON-NLS-1$ //$NON-NLS-2$
MILITARY("military"), // [TAG] military //$NON-NLS-1$ //$NON-NLS-2$
ADMINISTRATIVE("administrative"), // [TAG] administrative //$NON-NLS-1$ //$NON-NLS-2$
GEOCACHE("geocache"), //$NON-NLS-1$
OSMWIKI("osmwiki"), //$NON-NLS-1$
USER_DEFINED("user_defined"), //$NON-NLS-1$
OTHER("amenity"), // grave-yard, police, post-office [+Internet_access] //$NON-NLS-1$ //$NON-NLS-2$
;
private final String defaultTag;
private AmenityType(String defaultTag) {
this.defaultTag = defaultTag;
}
public static AmenityType fromString(String s){
try {
return AmenityType.valueOf(s.toUpperCase());
} catch (IllegalArgumentException e) {
return AmenityType.OTHER;
}
}
public String getDefaultTag() {
return defaultTag;
}
public static String valueToString(AmenityType t){
return t.toString().toLowerCase();
}
public static AmenityType[] getCategories(){
return AmenityType.values();
}
}

View file

@ -0,0 +1,144 @@
package net.osmand.data;
import net.osmand.util.Algorithms;
public class Building extends MapObject {
private String postcode;
private LatLon latLon2;
private BuildingInterpolation interpolationType;
private int interpolationInterval;
private String name2;
public enum BuildingInterpolation {
ALL(-1), EVEN(-2), ODD(-3), ALPHABETIC(-4);
private final int val;
BuildingInterpolation(int val) {
this.val = val;
}
public int getValue() {
return val;
}
public static BuildingInterpolation fromValue(int i){
for(BuildingInterpolation b : values()) {
if(b.val == i) {
return b;
}
}
return null;
}
}
public Building(){}
public String getPostcode() {
return postcode;
}
public int getInterpolationInterval() {
return interpolationInterval;
}
public void setInterpolationInterval(int interpolationNumber) {
this.interpolationInterval = interpolationNumber;
}
public BuildingInterpolation getInterpolationType() {
return interpolationType;
}
public void setInterpolationType(BuildingInterpolation interpolationType) {
this.interpolationType = interpolationType;
}
public LatLon getLatLon2() {
return latLon2;
}
public void setLatLon2(LatLon latlon2) {
this.latLon2 = latlon2;
}
public String getName2() {
return name2;
}
public void setName2(String name2) {
this.name2 = name2;
}
public void setPostcode(String postcode) {
this.postcode = postcode;
}
@Override
public String getName(boolean en) {
String fname = super.getName(en);
if(interpolationInterval !=0){
return fname+"-"+name2 +" (+"+interpolationInterval+") ";
} else if(interpolationType != null) {
return fname+"-"+name2 +" ("+interpolationType.toString().toLowerCase()+") ";
}
return name;
}
public float interpolation(String hno) {
if(getInterpolationType() != null || getInterpolationInterval() > 0) {
int num = Algorithms.extractFirstIntegerNumber(hno);
int numB = Algorithms.extractFirstIntegerNumber(super.getName());
int numT = numB;
if (num >= numB) {
if (getName2() != null) {
numT = Algorithms.extractFirstIntegerNumber(getName2());
if(numT < num) {
return -1;
}
}
if (getInterpolationType() == BuildingInterpolation.EVEN && num % 2 == 1) {
return -1;
}
if (getInterpolationType() == BuildingInterpolation.ODD && num % 2 == 0) {
return -1;
}
if (getInterpolationInterval() != 0 && (num - numB) % getInterpolationInterval() != 0) {
return -1;
}
} else {
return -1;
}
if(numT > numB){
return ((float)num - numB) / (((float)numT - numB));
}
return 0;
}
return -1;
}
public boolean belongsToInterpolation(String hno) {
return interpolation(hno) >= 0;
}
@Override
public String toString() {
if(interpolationInterval !=0){
return name+"-"+name2 +" (+"+interpolationInterval+") ";
} else if(interpolationType != null) {
return name+"-"+name2 +" ("+interpolationType+") ";
}
return name;
}
public LatLon getLocation(float interpolation) {
LatLon loc = getLocation();
LatLon latLon2 = getLatLon2();
if (latLon2 != null) {
double lat1 = loc.getLatitude();
double lat2 = latLon2.getLatitude();
double lon1 = loc.getLongitude();
double lon2 = latLon2.getLongitude();
return new LatLon(interpolation * (lat2 - lat1) + lat1, interpolation * (lon2 - lon1) + lon1);
}
return loc;
}
}

View file

@ -0,0 +1,140 @@
package net.osmand.data;
import java.util.Collection;
import java.util.Map;
import java.util.TreeMap;
import net.osmand.PlatformUtil;
import net.osmand.util.Algorithms;
public class City extends MapObject {
public enum CityType {
// that's tricky way to play with that numbers (to avoid including suburbs in city & vice verse)
// district special type and it is not registered as a city
CITY(10000), TOWN(5000), VILLAGE(1300), HAMLET(1000), SUBURB(400), DISTRICT(400);
private double radius;
private CityType(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
public static String valueToString(CityType t) {
return t.toString().toLowerCase();
}
public static CityType valueFromString(String place) {
if (place == null) {
return null;
}
for (CityType t : CityType.values()) {
if (t.name().equalsIgnoreCase(place)) {
return t;
}
}
return null;
}
}
private CityType type = null;
// Be attentive ! Working with street names ignoring case
private Map<String, Street> streets = new TreeMap<String, Street>(PlatformUtil.primaryCollator());
private String isin = null;
private String postcode = null;
private static long POSTCODE_INTERNAL_ID = -1000;
public static City createPostcode(String postcode){
return new City(postcode, POSTCODE_INTERNAL_ID--);
}
public City(CityType type) {
if(type == null) {
throw new NullPointerException();
}
this.type = type;
}
public City(String postcode, long id) {
this.type = null;
this.name = this.enName = postcode;
this.id = id;
}
public String getIsInValue() {
return isin;
}
public boolean isPostcode(){
return type == null;
}
public boolean isEmptyWithStreets() {
return streets.isEmpty();
}
public Street unregisterStreet(String name) {
return streets.remove(name.toLowerCase());
}
public void removeAllStreets() {
streets.clear();
}
public String getPostcode() {
return postcode;
}
public void setPostcode(String postcode) {
this.postcode = postcode;
}
protected Street registerStreet(Street street, boolean en) {
String name = en ? street.getEnName() : street.getName();
name = name.toLowerCase();
if (!Algorithms.isEmpty(name)) {
if (!streets.containsKey(name)) {
return streets.put(name, street);
} else {
// try to merge streets
Street prev = streets.get(name);
prev.mergeWith(street);
return prev;
}
}
return null;
}
public Street registerStreet(Street street) {
return registerStreet(street, false);
}
public CityType getType() {
return type;
}
public Collection<Street> getStreets() {
return streets.values();
}
public Street getStreet(String name) {
return streets.get(name.toLowerCase());
}
@Override
public String toString() {
if(isPostcode()) {
return "Postcode : " + getName(); //$NON-NLS-1$ //$NON-NLS-2$
}
return "City [" + type + "] " + getName(); //$NON-NLS-1$ //$NON-NLS-2$
}
public void setIsin(String isin) {
this.isin = isin;
}
}

View file

@ -0,0 +1,205 @@
package net.osmand.data;
import gnu.trove.map.hash.TLongObjectHashMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.osmand.util.MapUtils;
/**
*
* @param <T> - object to store in that manager
*/
public class DataTileManager<T> {
private final int zoom;
private TLongObjectHashMap<List<T>> objects = new TLongObjectHashMap<List<T>>();
public DataTileManager(){
zoom = 15;
}
public DataTileManager(int z){
zoom = z;
}
public int getZoom() {
return zoom;
}
public boolean isEmpty(){
return getObjectsCount() == 0;
}
@SuppressWarnings("rawtypes")
public int getObjectsCount(){
int x = 0;
for(List s : objects.valueCollection()){
x += s.size();
}
return x;
}
private void putObjects(int tx, int ty, List<T> r){
if(objects.containsKey(evTile(tx, ty))){
r.addAll(objects.get(evTile(tx, ty)));
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public List<T> getAllObjects(){
List<T> l = new ArrayList<T>();
for(List s : objects.valueCollection()){
l.addAll(s);
}
return l;
}
public List<T> getObjects(double latitudeUp, double longitudeUp, double latitudeDown, double longitudeDown) {
int tileXUp = (int) MapUtils.getTileNumberX(zoom, longitudeUp);
int tileYUp = (int) MapUtils.getTileNumberY(zoom, latitudeUp);
int tileXDown = (int) MapUtils.getTileNumberX(zoom, longitudeDown) + 1;
int tileYDown = (int) MapUtils.getTileNumberY(zoom, latitudeDown) + 1;
List<T> result = new ArrayList<T>();
for (int i = tileXUp; i <= tileXDown; i++) {
for (int j = tileYUp; j <= tileYDown; j++) {
putObjects(i, j, result);
}
}
return result;
}
public List<T> getObjects(int leftX31, int topY31, int rightX31, int bottomY31) {
int tileXUp = leftX31 >> (31 - zoom);
int tileYUp = topY31 >> (31 - zoom);
int tileXDown = (rightX31 >> (31 - zoom)) + 1;
int tileYDown = (bottomY31 >> (31 - zoom)) + 1;
List<T> result = new ArrayList<T>();
for (int i = tileXUp; i <= tileXDown; i++) {
for (int j = tileYUp; j <= tileYDown; j++) {
putObjects(i, j, result);
}
}
return result;
}
/**
* @depth of the neighbor tile to visit
* returns not exactly sorted list,
* however the first objects are from closer tile than last
*/
public List<T> getClosestObjects(double latitude, double longitude, int defaultStep){
if(isEmpty()){
return Collections.emptyList();
}
int dp = 0;
List<T> l = null;
while (l == null || l.isEmpty()) {
l = getClosestObjects(latitude, longitude, dp, dp + defaultStep);
dp += defaultStep;
}
return l;
}
public List<T> getClosestObjects(double latitude, double longitude){
return getClosestObjects(latitude, longitude, 3);
}
public List<T> getClosestObjects(double latitude, double longitude, int startDepth, int depth){
int tileX = (int) MapUtils.getTileNumberX(zoom, longitude);
int tileY = (int) MapUtils.getTileNumberY(zoom, latitude);
List<T> result = new ArrayList<T>();
if(startDepth <= 0){
putObjects(tileX, tileY, result);
startDepth = 1;
}
// that's very difficult way visiting node :
// similar to visit by spiral
// however the simplest way could be to visit row by row & after sort tiles by distance (that's less efficient)
// go through circle
for (int i = startDepth; i <= depth; i++) {
// goes
for (int j = 0; j <= i; j++) {
// left & right
int dx = j == 0 ? 0 : -1;
for (; dx < 1 || (j < i && dx == 1); dx += 2) {
// north
putObjects(tileX + dx * j, tileY + i, result);
// east
putObjects(tileX + i, tileY - dx * j, result);
// south
putObjects(tileX - dx * j, tileY - i, result);
// west
putObjects(tileX - i, tileY + dx * j, result);
}
}
}
return result;
}
private long evTile(int tileX, int tileY){
long tx = tileX;
long ty = tileY;
return ((tx) << zoom) + ty;
}
public long evaluateTile(double latitude, double longitude){
int tileX = (int) MapUtils.getTileNumberX(zoom, longitude);
int tileY = (int) MapUtils.getTileNumberY(zoom, latitude);
return evTile(tileX, tileY);
}
public long evaluateTileXY(int x31, int y31){
return evTile(x31 >> (31 - zoom), y31 >> (31 - zoom));
}
public void unregisterObject(double latitude, double longitude, T object){
long tile = evaluateTile(latitude, longitude);
removeObject(object, tile);
}
public void unregisterObjectXY(int x31, int y31, T object){
long tile = evaluateTileXY(x31, y31);
removeObject(object, tile);
}
private void removeObject(T object, long tile) {
if(objects.containsKey(tile)){
objects.get(tile).remove(object);
}
}
public long registerObjectXY(int x31, int y31, T object){
return addObject(object, evTile(x31 >> (31 - zoom), y31 >> (31 - zoom)));
}
public long registerObject(double latitude, double longitude, T object){
long tile = evaluateTile(latitude, longitude);
return addObject(object, tile);
}
private long addObject(T object, long tile) {
if(!objects.containsKey(tile)){
objects.put(tile, new ArrayList<T>());
}
objects.get(tile).add(object);
return tile;
}
public void clear(){
objects.clear();
}
}

View file

@ -0,0 +1,67 @@
package net.osmand.data;
import java.io.Serializable;
public class FavouritePoint implements Serializable {
private static final long serialVersionUID = 729654300829771466L;
private String name;
private String category = "";
private double latitude;
private double longitude;
private boolean stored = false;
public FavouritePoint(){
}
public FavouritePoint(double latitude, double longitude, String name, String category) {
this.latitude = latitude;
this.longitude = longitude;
this.category = category;
this.name = name;
}
public double getLatitude() {
return latitude;
}
public boolean isStored() {
return stored;
}
public void setStored(boolean stored) {
this.stored = stored;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Favourite " + getName(); //$NON-NLS-1$
}
}

View file

@ -0,0 +1,54 @@
package net.osmand.data;
public class LatLon {
private final double longitude;
private final double latitude;
public LatLon(double latitude, double longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(latitude);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(longitude);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
LatLon other = (LatLon) obj;
if (Double.doubleToLongBits(latitude) != Double.doubleToLongBits(other.latitude))
return false;
if (Double.doubleToLongBits(longitude) != Double.doubleToLongBits(other.longitude))
return false;
return true;
}
@Override
public String toString() {
return "Lat " + ((float)latitude) + " Lon " + ((float)longitude); //$NON-NLS-1$ //$NON-NLS-2$
}
public double getLatitude() {
return latitude;
}
public double getLongitude() {
return longitude;
}
}

View file

@ -0,0 +1,127 @@
package net.osmand.data;
import java.io.Serializable;
import java.util.Comparator;
import net.osmand.Collator;
import net.osmand.PlatformUtil;
public abstract class MapObject implements Comparable<MapObject>, Serializable {
protected String name = null;
protected String enName = null;
protected LatLon location = null;
protected int fileOffset = 0;
protected Long id = null;
public void setId(Long id) {
this.id = id;
}
public Long getId() {
if(id != null){
return id;
}
return null;
}
public String getName(boolean en){
if(en){
return getEnName();
} else {
return getName();
}
}
public String getName() {
if (this.name != null) {
return this.name;
}
return ""; //$NON-NLS-1$
}
public void setName(String name) {
this.name = name;
}
public String getEnName() {
if(this.enName != null){
return this.enName;
}
return ""; //$NON-NLS-1$
}
public void setEnName(String enName) {
this.enName = enName;
}
public LatLon getLocation(){
return location;
}
public void setLocation(double latitude, double longitude){
location = new LatLon(latitude, longitude);
}
@Override
public int compareTo(MapObject o) {
return PlatformUtil.primaryCollator().compare(getName(), o.getName());
}
public int getFileOffset() {
return fileOffset;
}
public void setFileOffset(int fileOffset) {
this.fileOffset = fileOffset;
}
@Override
public String toString() {
return getClass().getSimpleName() + " " + name +"("+id+")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MapObject other = (MapObject) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
return true;
}
public static class MapObjectComparator implements Comparator<MapObject>{
private final boolean en;
Collator collator = PlatformUtil.primaryCollator();
public MapObjectComparator(boolean en){
this.en = en;
}
@Override
public int compare(MapObject o1, MapObject o2) {
if(en){
return collator.compare(o1.getEnName(), o2.getEnName());
} else {
return collator.compare(o1.getName(), o2.getName());
}
}
}
}

View file

@ -0,0 +1,24 @@
package net.osmand.data;
public class QuadPoint {
public float x;
public float y;
public QuadPoint() {
}
public QuadPoint(float x, float y) {
this.x = x;
this.y = y;
}
public QuadPoint(QuadPoint a) {
this(a.x, a.y);
}
public void set(float x, float y) {
this.x = x;
this.y = y;
}
}

View file

@ -0,0 +1,67 @@
package net.osmand.data;
public class QuadRect {
public float left;
public float right;
public float top;
public float bottom;
public QuadRect(float left, float top, float right, float bottom) {
this.left = left;
this.right = right;
this.top = top;
this.bottom = bottom;
}
public QuadRect(QuadRect a) {
this(a.left, a.top, a.right, a.bottom);
}
public QuadRect() {
}
public float width() {
return right - left;
}
public float height() {
return bottom - top;
}
public boolean contains(float left, float top, float right, float bottom) {
return this.left < this.right && this.top < this.bottom && this.left <= left && this.top <= top && this.right >= right
&& this.bottom >= bottom;
}
public boolean contains(QuadRect box) {
return contains(box.left, box.top, box.right, box.bottom);
}
public static boolean intersects(QuadRect a, QuadRect b) {
return a.left < b.right && b.left < a.right && a.top < b.bottom && b.top < a.bottom;
}
public float centerX() {
return (left + right) / 2;
}
public float centerY() {
return (top + bottom) / 2;
}
public void offset(float dx, float dy) {
left += dx;
top += dy;
right += dx;
bottom += dy;
}
public void inset(float dx, float dy) {
left += dx;
top += dy;
right -= dx;
bottom -= dy;
}
}

View file

@ -0,0 +1,102 @@
package net.osmand.data;
import java.util.ArrayList;
import java.util.List;
public class QuadTree<T> {
private static class Node<T> {
List<T> data = null;
Node<T>[] children = null;
QuadRect bounds;
@SuppressWarnings("unchecked")
private Node(QuadRect b) {
bounds = new QuadRect(b.left, b.top, b.right, b.bottom);
children = new Node[4];
}
};
private float ratio;
private int maxDepth;
private Node<T> root;
public QuadTree(QuadRect r, int depth/* =8 */, float ratio /* = 0.55 */) {
this.ratio = ratio;
this.root = new Node<T>(r);
this.maxDepth = depth;
}
public void insert(T data, QuadRect box) {
int depth = 0;
doInsertData(data, box, root, depth);
}
public void insert(T data, float x, float y) {
insert(data, new QuadRect(x, y, x, y));
}
public void queryInBox(QuadRect box, List<T> result) {
result.clear();
queryNode(box, result, root);
}
private void queryNode(QuadRect box, List<T> result, Node<T> node) {
if (node != null) {
if (QuadRect.intersects(box, node.bounds)) {
if (node.data != null) {
result.addAll(node.data);
}
for (int k = 0; k < 4; ++k) {
queryNode(box, result, node.children[k]);
}
}
}
}
private void doInsertData(T data, QuadRect box, Node<T> n, int depth) {
if (++depth >= maxDepth) {
if (n.data == null) {
n.data = new ArrayList<T>();
}
n.data.add(data);
} else {
QuadRect[] ext = new QuadRect[4];
splitBox(n.bounds, ext);
for (int i = 0; i < 4; ++i) {
if (ext[i].contains(box)) {
if (n.children[i] == null) {
n.children[i] = new Node<T>(ext[i]);
}
doInsertData(data, box, n.children[i], depth);
return;
}
}
if (n.data == null) {
n.data = new ArrayList<T>();
}
n.data.add(data);
}
}
void splitBox(QuadRect node_extent, QuadRect[] n) {
// coord2d c=node_extent.center();
float width = node_extent.width();
float height = node_extent.height();
float lox = node_extent.left;
float loy = node_extent.top;
float hix = node_extent.right;
float hiy = node_extent.bottom;
n[0] = new QuadRect(lox, loy, lox + width * ratio, loy + height * ratio);
n[1] = new QuadRect(hix - width * ratio, loy, hix, loy + height * ratio);
n[2] = new QuadRect(lox, hiy - height * ratio, lox + width * ratio, hiy);
n[3] = new QuadRect(hix - width * ratio, hiy - height * ratio, hix, hiy);
}
}

View file

@ -0,0 +1,102 @@
package net.osmand.data;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import net.osmand.util.Algorithms;
public class Street extends MapObject {
protected List<Building> buildings = new ArrayList<Building>();
protected List<Street> intersectedStreets = null;
protected final City city;
public Street(City city) {
this.city = city;
}
public void addBuilding(Building building){
buildings.add(building);
}
public List<Street> getIntersectedStreets() {
if(intersectedStreets == null){
return Collections.emptyList();
}
return intersectedStreets;
}
public void addIntersectedStreet(Street s){
if(intersectedStreets == null) {
intersectedStreets = new ArrayList<Street>();
}
intersectedStreets.add(s);
}
public void addBuildingCheckById(Building building){
for(Building b : buildings) {
if(b.getId().longValue() == building.getId().longValue()){
return;
}
}
buildings.add(building);
}
public List<Building> getBuildings() {
return buildings;
}
@Override
public void setName(String name) {
if (name.equals(getName())) {
return;
}
if (city != null && city.getStreet(getName()) == this) {
city.unregisterStreet(getName());
super.setName(name);
Street s = city.registerStreet(this);
} else {
super.setName(name);
}
}
public String getNameWithoutCityPart(boolean en) {
String nm = getName(en);
int t = nm.lastIndexOf('(');
if(t > 0) {
return nm.substring(0, t);
}
return nm;
}
public City getCity() {
return city;
}
public void sortBuildings(){
Collections.sort(buildings, new Comparator<Building>(){
@Override
public int compare(Building o1, Building o2) {
String s1 = o1.getName();
String s2 = o2.getName();
int i1 = Algorithms.extractFirstIntegerNumber(s1);
int i2 = Algorithms.extractFirstIntegerNumber(s2);
if(i1 == i2) {
String t1 = Algorithms.extractIntegerSuffix(s1);
String t2 = Algorithms.extractIntegerSuffix(s2);
return t1.compareTo(t2);
}
return i1 - i2;
}
});
}
public void mergeWith(Street street) {
buildings.addAll(street.getBuildings());
}
}

View file

@ -0,0 +1,74 @@
package net.osmand.data;
import java.util.ArrayList;
import java.util.List;
import net.osmand.util.MapUtils;
public class TransportRoute extends MapObject {
private List<TransportStop> forwardStops = new ArrayList<TransportStop>();
private List<TransportStop> backwardStops = new ArrayList<TransportStop>();
private String ref;
private String operator;
private String type;
private Integer dist = null;
public TransportRoute(){
}
public List<TransportStop> getForwardStops() {
return forwardStops;
}
public List<TransportStop> getBackwardStops() {
return backwardStops;
}
public String getRef() {
return ref;
}
public void setRef(String ref) {
this.ref = ref;
}
public String getOperator() {
return operator;
}
public void setOperator(String operator) {
this.operator = operator;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getDistance(){
if(dist == null){
dist = getAvgBothDistance();
}
return dist;
}
public void setDistance(Integer dist) {
this.dist = dist;
}
public int getAvgBothDistance(){
int d = 0;
int bSsize = backwardStops.size();
int fSsize = forwardStops.size();
for(int i=1; i< bSsize; i++){
d += MapUtils.getDistance(backwardStops.get(i-1).getLocation(), backwardStops.get(i).getLocation());
}
for(int i=1; i< fSsize; i++){
d += MapUtils.getDistance(forwardStops.get(i-1).getLocation(), forwardStops.get(i).getLocation());
}
return d;
}
}

View file

@ -0,0 +1,17 @@
package net.osmand.data;
public class TransportStop extends MapObject {
int[] referencesToRoutes = null;
public TransportStop(){
}
public int[] getReferencesToRoutes() {
return referencesToRoutes;
}
public void setReferencesToRoutes(int[] referencesToRoutes) {
this.referencesToRoutes = referencesToRoutes;
}
}

View file

@ -0,0 +1,5 @@
package net.osmand.map;
public interface IMapLocationListener {
void locationChanged(double newLatitude, double newLongitude, Object source);
}

View file

@ -0,0 +1,24 @@
package net.osmand.map;
public interface ITileSource {
public int getMaximumZoomSupported();
public String getName();
public int getTileSize();
public String getUrlToLoad(int x, int y, int zoom);
public int getMinimumZoomSupported();
public String getTileFormat();
public int getBitDensity();
public boolean isEllipticYTile();
public boolean couldBeDownloadedFromInternet();
}

View file

@ -0,0 +1,230 @@
package net.osmand.map;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import net.osmand.PlatformUtil;
import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log;
public class MapTileDownloader {
// Download manager tile settings
public static int TILE_DOWNLOAD_THREADS = 4;
public static int TILE_DOWNLOAD_SECONDS_TO_WORK = 25;
public static final long TIMEOUT_AFTER_EXCEEDING_LIMIT_ERRORS = 20000;
public static final int TILE_DOWNLOAD_MAX_ERRORS_PER_TIMEOUT = 25;
private static MapTileDownloader downloader = null;
private static Log log = PlatformUtil.getLog(MapTileDownloader.class);
public static String USER_AGENT = "OsmAnd~";
private ThreadPoolExecutor threadPoolExecutor;
private List<IMapDownloaderCallback> callbacks = new ArrayList<IMapDownloaderCallback>();
private Set<File> currentlyDownloaded;
private int currentErrors = 0;
private long timeForErrorCounter = 0;
public static MapTileDownloader getInstance(String userAgent){
if(downloader == null){
downloader = new MapTileDownloader(TILE_DOWNLOAD_THREADS);
if(userAgent != null) {
MapTileDownloader.USER_AGENT = userAgent;
}
}
return downloader;
}
/**
* Callback for map downloader
*/
public interface IMapDownloaderCallback {
/**
* Sometimes null cold be passed as request
* That means that there were a lot of requests but
* once method is called
* (in order to not create a collection of request & reduce calling times)
* @param fileSaved
*/
public void tileDownloaded(DownloadRequest request);
}
/**
* Download request could subclassed to create own detailed request
*/
public static class DownloadRequest {
public final File fileToSave;
public final int zoom;
public final int xTile;
public final int yTile;
public final String url;
public boolean error;
public DownloadRequest(String url, File fileToSave, int xTile, int yTile, int zoom) {
this.url = url;
this.fileToSave = fileToSave;
this.xTile = xTile;
this.yTile = yTile;
this.zoom = zoom;
}
public DownloadRequest(String url, File fileToSave) {
this.url = url;
this.fileToSave = fileToSave;
xTile = -1;
yTile = -1;
zoom = -1;
}
public void setError(boolean error){
this.error = error;
}
}
public MapTileDownloader(int numberOfThreads){
threadPoolExecutor = new ThreadPoolExecutor(numberOfThreads, numberOfThreads, TILE_DOWNLOAD_SECONDS_TO_WORK,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
// 1.6 method but very useful to kill non-running threads
// threadPoolExecutor.allowCoreThreadTimeOut(true);
currentlyDownloaded = Collections.synchronizedSet(new HashSet<File>());
}
public void addDownloaderCallback(IMapDownloaderCallback callback){
callbacks.add(callback);
}
public void removeDownloaderCallback(IMapDownloaderCallback callback){
callbacks.remove(callback);
}
public List<IMapDownloaderCallback> getDownloaderCallbacks() {
return callbacks;
}
public boolean isFileCurrentlyDownloaded(File f){
return currentlyDownloaded.contains(f);
}
public boolean isSomethingBeingDownloaded(){
return !currentlyDownloaded.isEmpty();
}
public int getRemainingWorkers(){
return (int) (threadPoolExecutor.getTaskCount());
}
public void refuseAllPreviousRequests(){
// That's very strange because exception in impl of queue (possibly wrong impl)
// threadPoolExecutor.getQueue().clear();
while(!threadPoolExecutor.getQueue().isEmpty()){
threadPoolExecutor.getQueue().poll();
}
}
public void requestToDownload(DownloadRequest request){
long now = System.currentTimeMillis();
if((int)(now - timeForErrorCounter) > TIMEOUT_AFTER_EXCEEDING_LIMIT_ERRORS ) {
timeForErrorCounter = now;
currentErrors = 0;
} else if(currentErrors > TILE_DOWNLOAD_MAX_ERRORS_PER_TIMEOUT){
return;
}
if(request.url == null){
return;
}
if (!isFileCurrentlyDownloaded(request.fileToSave)) {
threadPoolExecutor.execute(new DownloadMapWorker(request));
}
}
private class DownloadMapWorker implements Runnable, Comparable<DownloadMapWorker> {
private DownloadRequest request;
private DownloadMapWorker(DownloadRequest request){
this.request = request;
}
@Override
public void run() {
if (request != null && request.fileToSave != null && request.url != null) {
if(currentlyDownloaded.contains(request.fileToSave)){
return;
}
currentlyDownloaded.add(request.fileToSave);
if(log.isDebugEnabled()){
log.debug("Start downloading tile : " + request.url); //$NON-NLS-1$
}
long time = System.currentTimeMillis();
try {
request.fileToSave.getParentFile().mkdirs();
URL url = new URL(request.url);
URLConnection connection = url.openConnection();
connection.setRequestProperty("User-Agent", USER_AGENT); //$NON-NLS-1$
connection.setConnectTimeout(35000);
BufferedInputStream inputStream = new BufferedInputStream(connection.getInputStream(), 8 * 1024);
FileOutputStream stream = null;
try {
stream = new FileOutputStream(request.fileToSave);
Algorithms.streamCopy(inputStream, stream);
stream.flush();
} finally {
Algorithms.closeStream(inputStream);
Algorithms.closeStream(stream);
}
if (log.isDebugEnabled()) {
log.debug("Downloading tile : " + request.url + " successfull " + (System.currentTimeMillis() - time) + " ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
} catch (UnknownHostException e) {
currentErrors++;
request.setError(true);
log.error("UnknownHostException, cannot download tile " + request.url + " " + e.getMessage()); //$NON-NLS-1$ //$NON-NLS-2$
} catch (IOException e) {
currentErrors++;
request.setError(true);
log.warn("Cannot download tile : " + request.url, e); //$NON-NLS-1$
} finally {
currentlyDownloaded.remove(request.fileToSave);
}
if (!request.error) {
for (IMapDownloaderCallback c : new ArrayList<IMapDownloaderCallback>(callbacks)) {
c.tileDownloaded(request);
}
}
}
}
@Override
public int compareTo(DownloadMapWorker o) {
return 0; //(int) (time - o.time);
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,110 @@
package net.osmand.map;
import gnu.trove.list.array.TIntArrayList;
import java.util.ArrayList;
import java.util.List;
import net.osmand.map.OsmandRegionInfo.OsmAndRegion;
import net.osmand.util.Algorithms;
public class RegionCountry {
public String continentName;
public TIntArrayList tiles = new TIntArrayList();
public int left, right, top, bottom;
public String name;
public RegionCountry parent;
private List<RegionCountry> regions = new ArrayList<RegionCountry>();
public void add(int xdeg, int ydeg) {
if (tiles.size() == 0) {
left = right = xdeg;
top = bottom = ydeg;
}
left = Math.min(xdeg, left);
right = Math.max(xdeg, right);
bottom = Math.min(ydeg, bottom);
top = Math.max(ydeg, top);
tiles.add(xdeg);
tiles.add(ydeg);
}
public int getTileSize() {
return tiles.size()/2;
}
public int getLon(int i) {
return tiles.get(i*2);
}
public int getLat(int i) {
return tiles.get(i*2 + 1);
}
public void addSubregion(RegionCountry c) {
c.parent = this;
regions.add(c);
}
public List<RegionCountry> getSubRegions() {
return regions;
}
/*public TLongHashSet calculateTileSet(TLongHashSet t, int z) {
for (int j = 0; j < tiles.size(); j++) {
int kx = (int) MapUtils.getTileNumberX(z, getLon(j));
int ex = (int) MapUtils.getTileNumberX(z, getLon(j) + 0.9999f);
int ky = (int) MapUtils.getTileNumberY(z, getLat(j));
int ey = (int) MapUtils.getTileNumberY(z, getLat(j) - 0.9999f);
for (int x = kx; x <= ex; x++) {
for (int y = ky; y <= ey; y++) {
long v = (((long) y) << 31) + x;
t.add(v);
}
}
}
return t;
}*/
public static RegionCountry construct(OsmAndRegion reg) {
RegionCountry rc = new RegionCountry();
if (reg.hasContinentName()) {
rc.continentName = reg.getContinentName();
}
rc.name = reg.getName();
int px = 0;
int py = 0;
for (int i = 0; i < reg.getDegXCount(); i++) {
px = reg.getDegX(i) + px;
py = reg.getDegY(i) + py;
rc.add(px, py);
}
for (int i = 0; i < reg.getSubregionsCount(); i++) {
rc.addSubregion(construct(reg.getSubregions(i)));
}
return rc;
}
public OsmAndRegion convert() {
OsmAndRegion.Builder reg = OsmAndRegion.newBuilder();
// System.out.println(r.name + " " + r.tiles.size() + " ?= " + r.calculateTileSet(new TLongHashSet(), 8).size());
int px = 0;
int py = 0;
for (int i = 0; i < this.getTileSize(); i++) {
reg.addDegX(this.getLon(i) - px);
reg.addDegY(this.getLat(i) - py);
px = this.getLon(i);
py = this.getLat(i);
}
String n = Algorithms.capitalizeFirstLetterAndLowercase(this.name.replace('_', ' '));
reg.setName(n);
if(this.continentName != null) {
reg.setContinentName(Algorithms.capitalizeFirstLetterAndLowercase(this.continentName));
}
for (RegionCountry c : this.regions) {
reg.addSubregions(c.convert());
}
return reg.build();
}
}

View file

@ -0,0 +1,44 @@
package net.osmand.map;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import net.osmand.PlatformUtil;
import net.osmand.map.OsmandRegionInfo.OsmAndRegionInfo;
import org.apache.commons.logging.Log;
public class RegionRegistry {
public static final String fileName = "countries.reginfo";
private static final Log log = PlatformUtil.getLog(RegionRegistry.class);
private static RegionRegistry r = null;
private List<RegionCountry> countries = new ArrayList<RegionCountry>();
public static RegionRegistry getRegionRegistry(){
if(r == null) {
try {
long t = -System.currentTimeMillis();
r = new RegionRegistry();
InputStream in = RegionRegistry.class.getResourceAsStream(RegionRegistry.fileName);
OsmAndRegionInfo regInfo = OsmAndRegionInfo.newBuilder().mergeFrom(in).build();
for(int j = 0; j < regInfo.getRegionInfo().getRegionsCount(); j++) {
r.countries.add(RegionCountry.construct(regInfo.getRegionInfo().getRegions(j)));
}
t += System.currentTimeMillis();
log.info("Initialize regions from file" + t + " ms");
} catch (IOException e) {
log.error("IO exception reading regions", e);
}
}
return r;
}
public List<RegionCountry> getCountries() {
return countries;
}
}

Some files were not shown because too many files have changed in this diff Show more