diff --git a/activemq-protobuf-test/pom.xml b/activemq-protobuf-test/pom.xml new file mode 100644 index 0000000000..7db54ebfaa --- /dev/null +++ b/activemq-protobuf-test/pom.xml @@ -0,0 +1,80 @@ + + + + 4.0.0 + + + org.apache.activemq + activemq-parent + 6.1.3-SNAPSHOT + + + org.apache.activemq.protobuf + activemq-protobuf-test + + jar + ActiveMQ :: Protocol Buffers Tests + + + + junit + junit + test + + + org.apache.activemq.protobuf + activemq-protobuf + ${project.version} + + + + + + + + org.apache.activemq.protobuf + activemq-protobuf + ${project.version} + + + + + + maven-surefire-plugin + + + **/*Test.java + + + + + org.apache.activemq.protobuf + activemq-protobuf + ${project.version} + + + + compile + + + + + + + + diff --git a/activemq-protobuf-test/src/main/proto/deferred_decode.proto b/activemq-protobuf-test/src/main/proto/deferred_decode.proto new file mode 100644 index 0000000000..2535c48060 --- /dev/null +++ b/activemq-protobuf-test/src/main/proto/deferred_decode.proto @@ -0,0 +1,40 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one or more +// contributor license agreements. See the NOTICE file distributed with +// this work for additional information regarding copyright ownership. +// The ASF licenses this file to You under the Apache License, Version 2.0 +// (the "License"); you may not use this file except in compliance with +// the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package org.apache.activemq.protobuf; +option java_outer_classname = "DeferredUnmarshal"; +option deferred_decode = true; + +message Foo { + + optional int32 field1 = 1; + optional int64 field2 = 2; + +} + + +message Bar { + option base_type=Foo; + + // These are the Foo fields. + optional int32 field1 = 1; + optional int64 field2 = 2; + + optional Foo field3 = 3; + +} + diff --git a/activemq-protobuf-test/src/main/proto/multiple_files_test.proto b/activemq-protobuf-test/src/main/proto/multiple_files_test.proto new file mode 100644 index 0000000000..fd2a961956 --- /dev/null +++ b/activemq-protobuf-test/src/main/proto/multiple_files_test.proto @@ -0,0 +1,53 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// +// A proto file which tests the java_multiple_files option. + + +import "unittest.proto"; + +package protobuf_unittest; + +option java_multiple_files = true; +option java_outer_classname = "MultipleFilesTestProto"; + +message MessageWithNoOuter { + message NestedMessage { + optional int32 i = 1; + } + enum NestedEnum { + BAZ = 3; + } + optional NestedMessage nested = 1; + repeated TestAllTypes foreign = 2; + optional NestedEnum nested_enum = 3; + optional EnumWithNoOuter foreign_enum = 4; +} + +enum EnumWithNoOuter { + FOO = 1; + BAR = 2; +} + +service ServiceWithNoOuter { + rpc Foo(MessageWithNoOuter) returns(TestAllTypes); +} + +extend TestAllExtensions { + optional int32 extension_with_outer = 1234567; +} diff --git a/activemq-protobuf-test/src/main/proto/unittest.proto b/activemq-protobuf-test/src/main/proto/unittest.proto new file mode 100644 index 0000000000..a9b9d945b6 --- /dev/null +++ b/activemq-protobuf-test/src/main/proto/unittest.proto @@ -0,0 +1,452 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// A proto file we will use for unit testing. + + +import "unittest_import.proto"; + +// We don't put this in a package within proto2 because we need to make sure +// that the generated code doesn't depend on being in the proto2 namespace. +// In test_util.h we do "using namespace unittest = protobuf_unittest". +package protobuf_unittest; + +// Protos optimized for SPEED use a strict superset of the generated code +// of equivalent ones optimized for CODE_SIZE, so we should optimize all our +// tests for speed unless explicitly testing code size optimization. +option optimize_for = SPEED; + +option java_outer_classname = "UnittestProto"; + +// This proto includes every type of field in both singular and repeated +// forms. +message TestAllTypes { + message NestedMessage { + // The field name "b" fails to compile in proto1 because it conflicts with + // a local variable named "b" in one of the generated methods. Doh. + // This file needs to compile in proto1 to test backwards-compatibility. + optional int32 bb = 1; + } + + enum NestedEnum { + FOO = 1; + BAR = 2; + BAZ = 3; + } + + // Singular + optional int32 optional_int32 = 1; + optional int64 optional_int64 = 2; + optional uint32 optional_uint32 = 3; + optional uint64 optional_uint64 = 4; + optional sint32 optional_sint32 = 5; + optional sint64 optional_sint64 = 6; + optional fixed32 optional_fixed32 = 7; + optional fixed64 optional_fixed64 = 8; + optional sfixed32 optional_sfixed32 = 9; + optional sfixed64 optional_sfixed64 = 10; + optional float optional_float = 11; + optional double optional_double = 12; + optional bool optional_bool = 13; + optional string optional_string = 14; + optional bytes optional_bytes = 15; + + optional group OptionalGroup = 16 { + optional int32 a = 17; + } + + optional NestedMessage optional_nested_message = 18; + optional ForeignMessage optional_foreign_message = 19; + optional protobuf_unittest_import.ImportMessage optional_import_message = 20; + + optional NestedEnum optional_nested_enum = 21; + optional ForeignEnum optional_foreign_enum = 22; + optional protobuf_unittest_import.ImportEnum optional_import_enum = 23; + + optional string optional_string_piece = 24 [ctype=STRING_PIECE]; + optional string optional_cord = 25 [ctype=CORD]; + + // Repeated + repeated int32 repeated_int32 = 31; + repeated int64 repeated_int64 = 32; + repeated uint32 repeated_uint32 = 33; + repeated uint64 repeated_uint64 = 34; + repeated sint32 repeated_sint32 = 35; + repeated sint64 repeated_sint64 = 36; + repeated fixed32 repeated_fixed32 = 37; + repeated fixed64 repeated_fixed64 = 38; + repeated sfixed32 repeated_sfixed32 = 39; + repeated sfixed64 repeated_sfixed64 = 40; + repeated float repeated_float = 41; + repeated double repeated_double = 42; + repeated bool repeated_bool = 43; + repeated string repeated_string = 44; + repeated bytes repeated_bytes = 45; + + repeated group RepeatedGroup = 46 { + optional int32 a = 47; + } + + repeated NestedMessage repeated_nested_message = 48; + repeated ForeignMessage repeated_foreign_message = 49; + repeated protobuf_unittest_import.ImportMessage repeated_import_message = 50; + + repeated NestedEnum repeated_nested_enum = 51; + repeated ForeignEnum repeated_foreign_enum = 52; + repeated protobuf_unittest_import.ImportEnum repeated_import_enum = 53; + + repeated string repeated_string_piece = 54 [ctype=STRING_PIECE]; + repeated string repeated_cord = 55 [ctype=CORD]; + + // Singular with defaults + optional int32 default_int32 = 61 [default = 41 ]; + optional int64 default_int64 = 62 [default = 42 ]; + optional uint32 default_uint32 = 63 [default = 43 ]; + optional uint64 default_uint64 = 64 [default = 44 ]; + optional sint32 default_sint32 = 65 [default = -45 ]; + optional sint64 default_sint64 = 66 [default = 46 ]; + optional fixed32 default_fixed32 = 67 [default = 47 ]; + optional fixed64 default_fixed64 = 68 [default = 48 ]; + optional sfixed32 default_sfixed32 = 69 [default = 49 ]; + optional sfixed64 default_sfixed64 = 70 [default = -50 ]; + optional float default_float = 71 [default = 51.5 ]; + optional double default_double = 72 [default = 52e3 ]; + optional bool default_bool = 73 [default = true ]; + optional string default_string = 74 [default = "hello"]; + optional bytes default_bytes = 75 [default = "world"]; + + optional NestedEnum default_nested_enum = 81 [default = BAR ]; + optional ForeignEnum default_foreign_enum = 82 [default = FOREIGN_BAR]; + optional protobuf_unittest_import.ImportEnum + default_import_enum = 83 [default = IMPORT_BAR]; + + optional string default_string_piece = 84 [ctype=STRING_PIECE,default="abc"]; + optional string default_cord = 85 [ctype=CORD,default="123"]; +} + +// Define these after TestAllTypes to make sure the compiler can handle +// that. +message ForeignMessage { + optional int32 c = 1; +} + +enum ForeignEnum { + FOREIGN_FOO = 4; + FOREIGN_BAR = 5; + FOREIGN_BAZ = 6; +} + +message TestAllExtensions { + extensions 1 to max; +} + +extend TestAllExtensions { + // Singular + optional int32 optional_int32_extension = 1; + optional int64 optional_int64_extension = 2; + optional uint32 optional_uint32_extension = 3; + optional uint64 optional_uint64_extension = 4; + optional sint32 optional_sint32_extension = 5; + optional sint64 optional_sint64_extension = 6; + optional fixed32 optional_fixed32_extension = 7; + optional fixed64 optional_fixed64_extension = 8; + optional sfixed32 optional_sfixed32_extension = 9; + optional sfixed64 optional_sfixed64_extension = 10; + optional float optional_float_extension = 11; + optional double optional_double_extension = 12; + optional bool optional_bool_extension = 13; + optional string optional_string_extension = 14; + optional bytes optional_bytes_extension = 15; + + optional group OptionalGroup_extension = 16 { + optional int32 a = 17; + } + + optional TestAllTypes.NestedMessage optional_nested_message_extension = 18; + optional ForeignMessage optional_foreign_message_extension = 19; + optional protobuf_unittest_import.ImportMessage + optional_import_message_extension = 20; + + optional TestAllTypes.NestedEnum optional_nested_enum_extension = 21; + optional ForeignEnum optional_foreign_enum_extension = 22; + optional protobuf_unittest_import.ImportEnum + optional_import_enum_extension = 23; + + optional string optional_string_piece_extension = 24 [ctype=STRING_PIECE]; + optional string optional_cord_extension = 25 [ctype=CORD]; + + // Repeated + repeated int32 repeated_int32_extension = 31; + repeated int64 repeated_int64_extension = 32; + repeated uint32 repeated_uint32_extension = 33; + repeated uint64 repeated_uint64_extension = 34; + repeated sint32 repeated_sint32_extension = 35; + repeated sint64 repeated_sint64_extension = 36; + repeated fixed32 repeated_fixed32_extension = 37; + repeated fixed64 repeated_fixed64_extension = 38; + repeated sfixed32 repeated_sfixed32_extension = 39; + repeated sfixed64 repeated_sfixed64_extension = 40; + repeated float repeated_float_extension = 41; + repeated double repeated_double_extension = 42; + repeated bool repeated_bool_extension = 43; + repeated string repeated_string_extension = 44; + repeated bytes repeated_bytes_extension = 45; + + repeated group RepeatedGroup_extension = 46 { + optional int32 a = 47; + } + + repeated TestAllTypes.NestedMessage repeated_nested_message_extension = 48; + repeated ForeignMessage repeated_foreign_message_extension = 49; + repeated protobuf_unittest_import.ImportMessage + repeated_import_message_extension = 50; + + repeated TestAllTypes.NestedEnum repeated_nested_enum_extension = 51; + repeated ForeignEnum repeated_foreign_enum_extension = 52; + repeated protobuf_unittest_import.ImportEnum + repeated_import_enum_extension = 53; + + repeated string repeated_string_piece_extension = 54 [ctype=STRING_PIECE]; + repeated string repeated_cord_extension = 55 [ctype=CORD]; + + // Singular with defaults + optional int32 default_int32_extension = 61 [default = 41 ]; + optional int64 default_int64_extension = 62 [default = 42 ]; + optional uint32 default_uint32_extension = 63 [default = 43 ]; + optional uint64 default_uint64_extension = 64 [default = 44 ]; + optional sint32 default_sint32_extension = 65 [default = -45 ]; + optional sint64 default_sint64_extension = 66 [default = 46 ]; + optional fixed32 default_fixed32_extension = 67 [default = 47 ]; + optional fixed64 default_fixed64_extension = 68 [default = 48 ]; + optional sfixed32 default_sfixed32_extension = 69 [default = 49 ]; + optional sfixed64 default_sfixed64_extension = 70 [default = -50 ]; + optional float default_float_extension = 71 [default = 51.5 ]; + optional double default_double_extension = 72 [default = 52e3 ]; + optional bool default_bool_extension = 73 [default = true ]; + optional string default_string_extension = 74 [default = "hello"]; + optional bytes default_bytes_extension = 75 [default = "world"]; + + optional TestAllTypes.NestedEnum + default_nested_enum_extension = 81 [default = BAR]; + optional ForeignEnum + default_foreign_enum_extension = 82 [default = FOREIGN_BAR]; + optional protobuf_unittest_import.ImportEnum + default_import_enum_extension = 83 [default = IMPORT_BAR]; + + optional string default_string_piece_extension = 84 [ctype=STRING_PIECE, + default="abc"]; + optional string default_cord_extension = 85 [ctype=CORD, default="123"]; +} + +// We have separate messages for testing required fields because it's +// annoying to have to fill in required fields in TestProto in order to +// do anything with it. Note that we don't need to test every type of +// required filed because the code output is basically identical to +// optional fields for all types. +message TestRequired { + required int32 a = 1; + optional int32 dummy2 = 2; + required int32 b = 3; + + extend TestAllExtensions { + optional TestRequired single = 1000; + repeated TestRequired multi = 1001; + } + + // Pad the field count to 32 so that we can test that IsInitialized() + // properly checks multiple elements of has_bits_. + optional int32 dummy4 = 4; + optional int32 dummy5 = 5; + optional int32 dummy6 = 6; + optional int32 dummy7 = 7; + optional int32 dummy8 = 8; + optional int32 dummy9 = 9; + optional int32 dummy10 = 10; + optional int32 dummy11 = 11; + optional int32 dummy12 = 12; + optional int32 dummy13 = 13; + optional int32 dummy14 = 14; + optional int32 dummy15 = 15; + optional int32 dummy16 = 16; + optional int32 dummy17 = 17; + optional int32 dummy18 = 18; + optional int32 dummy19 = 19; + optional int32 dummy20 = 20; + optional int32 dummy21 = 21; + optional int32 dummy22 = 22; + optional int32 dummy23 = 23; + optional int32 dummy24 = 24; + optional int32 dummy25 = 25; + optional int32 dummy26 = 26; + optional int32 dummy27 = 27; + optional int32 dummy28 = 28; + optional int32 dummy29 = 29; + optional int32 dummy30 = 30; + optional int32 dummy31 = 31; + optional int32 dummy32 = 32; + + required int32 c = 33; +} + +message TestRequiredForeign { + optional TestRequired optional_message = 1; + repeated TestRequired repeated_message = 2; + optional int32 dummy = 3; +} + +// Test that we can use NestedMessage from outside TestAllTypes. +message TestForeignNested { + optional TestAllTypes.NestedMessage foreign_nested = 1; +} + +// TestEmptyMessage is used to test unknown field support. +message TestEmptyMessage { +} + +// Like above, but declare all field numbers as potential extensions. No +// actual extensions should ever be defined for this type. +message TestEmptyMessageWithExtensions { + extensions 1 to max; +} + +// Test that really large tag numbers don't break anything. +message TestReallyLargeTagNumber { + // The largest possible tag number is 2^28 - 1, since the wire format uses + // three bits to communicate wire type. + optional int32 a = 1; + optional int32 bb = 268435455; +} + +message TestRecursiveMessage { + optional TestRecursiveMessage a = 1; + optional int32 i = 2; +} + +// Test that mutual recursion works. +message TestMutualRecursionA { + optional TestMutualRecursionB bb = 1; +} + +message TestMutualRecursionB { + optional TestMutualRecursionA a = 1; + optional int32 optional_int32 = 2; +} + +// Test that groups have disjoint field numbers from their siblings and +// parents. This is NOT possible in proto1; only proto2. When outputting +// proto1, the dup fields should be dropped. +message TestDupFieldNumber { + optional int32 a = 1; + optional group Foo = 2 { optional int32 a = 1; } + optional group Bar = 3 { optional int32 a = 1; } +} + + +// Needed for a Python test. +message TestNestedMessageHasBits { + message NestedMessage { + repeated int32 nestedmessage_repeated_int32 = 1; + repeated ForeignMessage nestedmessage_repeated_foreignmessage = 2; + } + optional NestedMessage optional_nested_message = 1; +} + + +// Test an enum that has multiple values with the same number. +enum TestEnumWithDupValue { + FOO1 = 1; + BAR1 = 2; + BAZ = 3; + FOO2 = 1; + BAR2 = 2; +} + +// Test an enum with large, unordered values. +enum TestSparseEnum { + SPARSE_A = 123; + SPARSE_B = 62374; + SPARSE_C = 12589234; + SPARSE_D = -15; + SPARSE_E = -53452; + SPARSE_F = 0; + SPARSE_G = 2; +} + +// Test message with CamelCase field names. This violates Protocol Buffer +// standard style. +message TestCamelCaseFieldNames { + optional int32 PrimitiveField = 1; + optional string StringField = 2; + optional ForeignEnum EnumField = 3; + optional ForeignMessage MessageField = 4; + optional string StringPieceField = 5 [ctype=STRING_PIECE]; + optional string CordField = 6 [ctype=CORD]; + + repeated int32 RepeatedPrimitiveField = 7; + repeated string RepeatedStringField = 8; + repeated ForeignEnum RepeatedEnumField = 9; + repeated ForeignMessage RepeatedMessageField = 10; + repeated string RepeatedStringPieceField = 11 [ctype=STRING_PIECE]; + repeated string RepeatedCordField = 12 [ctype=CORD]; +} + + +// We list fields out of order, to ensure that we're using field number and not +// field index to determine serialization order. +message TestFieldOrderings { + optional string my_string = 11; + extensions 2 to 10; + optional int64 my_int = 1; + extensions 12 to 100; + optional float my_float = 101; +} + + +extend TestFieldOrderings { + optional string my_extension_string = 50; + optional int32 my_extension_int = 5; +} + + +message TestExtremeDefaultValues { + optional bytes escaped_bytes = 1 [default = "\0\001\a\b\f\n\r\t\v\\\'\"\xfe"]; + optional uint32 large_uint32 = 2 [default = 0xFFFFFFFF]; + optional uint64 large_uint64 = 3 [default = 0xFFFFFFFFFFFFFFFF]; + optional int32 small_int32 = 4 [default = -0x7FFFFFFF]; + optional int64 small_int64 = 5 [default = -0x7FFFFFFFFFFFFFFF]; + + // The default value here is UTF-8 for "\u1234". (We could also just type + // the UTF-8 text directly into this text file rather than escape it, but + // lots of people use editors that would be confused by this.) + optional string utf8_string = 6 [default = "\341\210\264"]; +} + +// Test that RPC services work. +message FooRequest {} +message FooResponse {} + +service TestService { + rpc Foo(FooRequest) returns (FooResponse); + rpc Bar(BarRequest) returns (BarResponse); +} + + +message BarRequest {} +message BarResponse {} diff --git a/activemq-protobuf-test/src/main/proto/unittest_embed_optimize_for.proto b/activemq-protobuf-test/src/main/proto/unittest_embed_optimize_for.proto new file mode 100644 index 0000000000..d2e172347f --- /dev/null +++ b/activemq-protobuf-test/src/main/proto/unittest_embed_optimize_for.proto @@ -0,0 +1,36 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// A proto file which imports a proto file that uses optimize_for = CODE_SIZE. + +import "unittest_optimize_for.proto"; + +package protobuf_unittest; + +// We optimize for speed here, but we are importing a proto that is optimized +// for code size. +option optimize_for = SPEED; + +message TestEmbedOptimizedForSize { + // Test that embedding a message which has optimize_for = CODE_SIZE into + // one optimized for speed works. + optional TestOptimizedForSize optional_message = 1; + repeated TestOptimizedForSize repeated_message = 2; +} diff --git a/activemq-protobuf-test/src/main/proto/unittest_import.proto b/activemq-protobuf-test/src/main/proto/unittest_import.proto new file mode 100644 index 0000000000..58ce42c39d --- /dev/null +++ b/activemq-protobuf-test/src/main/proto/unittest_import.proto @@ -0,0 +1,47 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// A proto file which is imported by unittest.proto to test importing. + + +// We don't put this in a package within proto2 because we need to make sure +// that the generated code doesn't depend on being in the proto2 namespace. +// In test_util.h we do +// "using namespace unittest_import = protobuf_unittest_import". +package protobuf_unittest_import; + +option optimize_for = SPEED; + +// Excercise the java_package option. +option java_package = "com.google.protobuf.test"; + +// Do not set a java_outer_classname here to verify that Proto2 works without +// one. + +message ImportMessage { + optional int32 d = 1; +} + +enum ImportEnum { + IMPORT_FOO = 7; + IMPORT_BAR = 8; + IMPORT_BAZ = 9; +} + diff --git a/activemq-protobuf-test/src/main/proto/unittest_mset.proto b/activemq-protobuf-test/src/main/proto/unittest_mset.proto new file mode 100644 index 0000000000..455086d2f0 --- /dev/null +++ b/activemq-protobuf-test/src/main/proto/unittest_mset.proto @@ -0,0 +1,58 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// This file contains messages for testing message_set_wire_format. + +package protobuf_unittest; + +option optimize_for = SPEED; + +// A message with message_set_wire_format. +message TestMessageSet { + option message_set_wire_format = true; + extensions 4 to max; +} + +message TestMessageSetContainer { + optional TestMessageSet message_set = 1; +} + +message TestMessageSetExtension1 { + extend TestMessageSet { + optional TestMessageSetExtension1 message_set_extension = 1545008; + } + optional int32 i = 15; +} + +message TestMessageSetExtension2 { + extend TestMessageSet { + optional TestMessageSetExtension2 message_set_extension = 1547769; + } + optional string str = 25; +} + +// MessageSet wire format is equivalent to this. +message RawMessageSet { + repeated group Item = 1 { + required int32 type_id = 2; + required bytes message = 3; + } +} + diff --git a/activemq-protobuf-test/src/main/proto/unittest_optimize_for.proto b/activemq-protobuf-test/src/main/proto/unittest_optimize_for.proto new file mode 100644 index 0000000000..86e727f33f --- /dev/null +++ b/activemq-protobuf-test/src/main/proto/unittest_optimize_for.proto @@ -0,0 +1,38 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// A proto file which uses optimize_for = CODE_SIZE. + +import "unittest.proto"; + +package protobuf_unittest; + +option optimize_for = CODE_SIZE; + +message TestOptimizedForSize { + optional int32 i = 1; + optional ForeignMessage msg = 19; + + extensions 1000 to max; + + extend TestOptimizedForSize { + optional int32 test_extension = 1234; + } +} diff --git a/activemq-protobuf-test/src/test/java/com/google/protobuf/GeneratedMessageTest.java b/activemq-protobuf-test/src/test/java/com/google/protobuf/GeneratedMessageTest.java new file mode 100644 index 0000000000..d9845fbabb --- /dev/null +++ b/activemq-protobuf-test/src/test/java/com/google/protobuf/GeneratedMessageTest.java @@ -0,0 +1,119 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import java.util.Arrays; + +import junit.framework.TestCase; +import protobuf_unittest.EnumWithNoOuter; +import protobuf_unittest.MessageWithNoOuter; +import protobuf_unittest.UnittestProto.ForeignEnum; +import protobuf_unittest.UnittestProto.ForeignMessage; +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestExtremeDefaultValues; + +/** + * Unit test for generated messages and generated code. See also + * {@link MessageTest}, which tests some generated message functionality. + * + * @author kenton@google.com Kenton Varda + */ +public class GeneratedMessageTest extends TestCase { + + + public void testAccessors() throws Exception { + TestAllTypes builder = new TestAllTypes(); + TestUtil.setAllFields(builder); + TestAllTypes message = builder; + TestUtil.assertAllFieldsSet(message); + } + + public void testRepeatedSetters() throws Exception { + TestAllTypes builder = new TestAllTypes(); + TestUtil.setAllFields(builder); + TestUtil.modifyRepeatedFields(builder); + TestAllTypes message = builder; + TestUtil.assertRepeatedFieldsModified(message); + } + + public void testRepeatedAppend() throws Exception { + TestAllTypes builder = new TestAllTypes(); + + builder.addAllRepeatedInt32(Arrays.asList(1, 2, 3, 4)); + builder.addAllRepeatedForeignEnum(Arrays.asList(ForeignEnum.FOREIGN_BAZ)); + + ForeignMessage foreignMessage = new ForeignMessage().setC(12); + builder.addAllRepeatedForeignMessage(Arrays.asList(foreignMessage)); + + TestAllTypes message = builder; + assertEquals(message.getRepeatedInt32List(), Arrays.asList(1, 2, 3, 4)); + assertEquals(message.getRepeatedForeignEnumList(), + Arrays.asList(ForeignEnum.FOREIGN_BAZ)); + assertEquals(1, message.getRepeatedForeignMessageCount()); + assertEquals(12, message.getRepeatedForeignMessage(0).getC()); + } + + public void testSettingForeignMessageUsingBuilder() throws Exception { + TestAllTypes message = new TestAllTypes() + // Pass builder for foreign message instance. + .setOptionalForeignMessage(new ForeignMessage().setC(123)) + ; + TestAllTypes expectedMessage = new TestAllTypes() + // Create expected version passing foreign message instance explicitly. + .setOptionalForeignMessage(new ForeignMessage().setC(123)) + ; + // TODO(ngd): Upgrade to using real #equals method once implemented + assertEquals(expectedMessage.toString(), message.toString()); + } + + public void testSettingRepeatedForeignMessageUsingBuilder() throws Exception { + TestAllTypes message = new TestAllTypes() + // Pass builder for foreign message instance. + .addRepeatedForeignMessage(new ForeignMessage().setC(456)) + ; + TestAllTypes expectedMessage = new TestAllTypes() + // Create expected version passing foreign message instance explicitly. + .addRepeatedForeignMessage( + new ForeignMessage().setC(456)) + ; + assertEquals(expectedMessage.toString(), message.toString()); + } + + public void testDefaults() throws Exception { + TestUtil.assertClear(new TestAllTypes()); + + assertEquals("\u1234", new TestExtremeDefaultValues().getUtf8String()); + } + + // ================================================================= + // multiple_files_test + + public void testMultipleFilesOption() throws Exception { + // We mostly just want to check that things compile. + MessageWithNoOuter message = + new MessageWithNoOuter() + .setNested(new MessageWithNoOuter.NestedMessage().setI(1)) + .addForeign(new TestAllTypes().setOptionalInt32(1)) + .setNestedEnum(MessageWithNoOuter.NestedEnum.BAZ) + .setForeignEnum(EnumWithNoOuter.BAR) + ; + + byte[] data = message.toUnframedByteArray(); + MessageWithNoOuter newMessage = MessageWithNoOuter.parseUnframed(data); + assertEquals(message.toString(), newMessage.toString()); + } +} diff --git a/activemq-protobuf-test/src/test/java/com/google/protobuf/MessageTest.java b/activemq-protobuf-test/src/test/java/com/google/protobuf/MessageTest.java new file mode 100644 index 0000000000..f306d740ae --- /dev/null +++ b/activemq-protobuf-test/src/test/java/com/google/protobuf/MessageTest.java @@ -0,0 +1,114 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import protobuf_unittest.UnittestProto.TestAllTypes; +import protobuf_unittest.UnittestProto.TestRequired; +import protobuf_unittest.UnittestProto.TestRequiredForeign; +import protobuf_unittest.UnittestProto.ForeignMessage; + +import junit.framework.TestCase; + +/** + * Misc. unit tests for message operations that apply to both generated + * and dynamic messages. + * + * @author kenton@google.com Kenton Varda + */ +public class MessageTest extends TestCase { + // ================================================================= + // Message-merging tests. + + static final TestAllTypes MERGE_SOURCE = + new TestAllTypes() + .setOptionalInt32(1) + .setOptionalString("foo") + .setOptionalForeignMessage(new ForeignMessage()) + .addRepeatedString("bar") + ; + + static final TestAllTypes MERGE_DEST = + new TestAllTypes() + .setOptionalInt64(2) + .setOptionalString("baz") + .setOptionalForeignMessage(new ForeignMessage().setC(3)) + .addRepeatedString("qux") + ; + + static final String MERGE_RESULT_TEXT = + "optional_int32: 1\n" + + "optional_int64: 2\n" + + "optional_string: foo\n" + + "optional_foreign_message {\n" + + " c: 3\n" + + "}\n" + + "repeated_string[0]: qux\n" + + "repeated_string[1]: bar\n"; + + public void testMergeFrom() throws Exception { + TestAllTypes result = + new TestAllTypes().mergeFrom(MERGE_DEST) + .mergeFrom(MERGE_SOURCE); + + assertEquals(MERGE_RESULT_TEXT, result.toString()); + } + + + // ================================================================= + // Required-field-related tests. + + private static final TestRequired TEST_REQUIRED_UNINITIALIZED = + new TestRequired(); + private static final TestRequired TEST_REQUIRED_INITIALIZED = + new TestRequired().setA(1).setB(2).setC(3); + + public void testRequired() throws Exception { + TestRequired builder = new TestRequired(); + + assertFalse(builder.isInitialized()); + builder.setA(1); + assertFalse(builder.isInitialized()); + builder.setB(1); + assertFalse(builder.isInitialized()); + builder.setC(1); + assertTrue(builder.isInitialized()); + } + + public void testRequiredForeign() throws Exception { + TestRequiredForeign builder = new TestRequiredForeign(); + + assertTrue(builder.isInitialized()); + + builder.setOptionalMessage(TEST_REQUIRED_UNINITIALIZED); + assertFalse(builder.isInitialized()); + + builder.setOptionalMessage(TEST_REQUIRED_INITIALIZED); + assertTrue(builder.isInitialized()); + + builder.addRepeatedMessage(TEST_REQUIRED_UNINITIALIZED); + assertFalse(builder.isInitialized()); + + builder.setRepeatedMessage(0, TEST_REQUIRED_INITIALIZED); + assertTrue(builder.isInitialized()); + } + + + public void testIsInitialized() throws Exception { + assertFalse(new TestRequired().isInitialized()); + } + +} diff --git a/activemq-protobuf-test/src/test/java/com/google/protobuf/TestUtil.java b/activemq-protobuf-test/src/test/java/com/google/protobuf/TestUtil.java new file mode 100644 index 0000000000..fe2505d379 --- /dev/null +++ b/activemq-protobuf-test/src/test/java/com/google/protobuf/TestUtil.java @@ -0,0 +1,791 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Note: This file contains many lines over 80 characters. It even contains +// many lines over 100 characters, which fails a presubmit test. However, +// given the extremely repetitive nature of the file, I (kenton) feel that +// having similar components of each statement line up is more important than +// avoiding horizontal scrolling. So, I am bypassing the presubmit check. + +package com.google.protobuf; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; + +import org.apache.activemq.protobuf.Buffer; + +import protobuf_unittest.UnittestProto.ForeignEnum; +import protobuf_unittest.UnittestProto.ForeignMessage; +import protobuf_unittest.UnittestProto.TestAllTypes; + +import com.google.protobuf.test.UnittestImport.ImportEnum; +import com.google.protobuf.test.UnittestImport.ImportMessage; + +/** + * Contains methods for setting all fields of {@code TestAllTypes} to + * some vaules as well as checking that all the fields are set to those values. + * These are useful for testing various protocol message features, e.g. + * set all fields of a message, serialize it, parse it, and check that all + * fields are set. + * + * @author kenton@google.com Kenton Varda + */ +class TestUtil { + private TestUtil() {} + + /** Helper to convert a String to ByteSequence. */ + private static Buffer toBytes(String str) { + try { + return new Buffer(str.getBytes("UTF-8")); + } catch(java.io.UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not supported.", e); + } + } + + /** + * Get a {@code TestAllTypes} with all fields set as they would be by + * {@link #setAllFields(TestAllTypes.Builder)}. + */ + public static TestAllTypes getAllSet() { + TestAllTypes builder = new TestAllTypes(); + setAllFields(builder); + return builder; + } + + /** + * Set every field of {@code message} to the values expected by + * {@code assertAllFieldsSet()}. + */ + public static void setAllFields(protobuf_unittest.UnittestProto.TestAllTypes message) { + message.setOptionalInt32 (101); + message.setOptionalInt64 (102); + message.setOptionalUint32 (103); + message.setOptionalUint64 (104); + message.setOptionalSint32 (105); + message.setOptionalSint64 (106); + message.setOptionalFixed32 (107); + message.setOptionalFixed64 (108); + message.setOptionalSfixed32(109); + message.setOptionalSfixed64(110); + message.setOptionalFloat (111); + message.setOptionalDouble (112); + message.setOptionalBool (true); + message.setOptionalString ("115"); + message.setOptionalBytes (toBytes("116")); + + message.setOptionalGroup( + new TestAllTypes.OptionalGroup().setA(117)); + message.setOptionalNestedMessage( + new TestAllTypes.NestedMessage().setBb(118)); + message.setOptionalForeignMessage( + new ForeignMessage().setC(119)); + message.setOptionalImportMessage( + new ImportMessage().setD(120)); + + message.setOptionalNestedEnum (TestAllTypes.NestedEnum.BAZ); + message.setOptionalForeignEnum(ForeignEnum.FOREIGN_BAZ); + message.setOptionalImportEnum (ImportEnum.IMPORT_BAZ); + + message.setOptionalStringPiece("124"); + message.setOptionalCord("125"); + + // ----------------------------------------------------------------- + + message.getRepeatedInt32List().add(201); + message.getRepeatedInt64List().add(202L); + message.getRepeatedUint32List().add(203); + message.getRepeatedUint64List().add(204l); + message.getRepeatedSint32List().add(205); + message.getRepeatedSint64List().add (206l); + message.getRepeatedFixed32List().add (207); + message.getRepeatedFixed64List().add (208l); + message.getRepeatedSfixed32List().add(209); + message.getRepeatedSfixed64List().add(210l); + message.getRepeatedFloatList().add (211f); + message.getRepeatedDoubleList().add (212d); + message.getRepeatedBoolList().add (true); + message.getRepeatedStringList().add ("215"); + message.getRepeatedBytesList().add (toBytes("216")); + + message.getRepeatedGroupList().add( + new TestAllTypes.RepeatedGroup().setA(217)); + message.getRepeatedNestedMessageList().add( + new TestAllTypes.NestedMessage().setBb(218)); + message.getRepeatedForeignMessageList().add( + new ForeignMessage().setC(219)); + message.getRepeatedImportMessageList().add( + new ImportMessage().setD(220)); + + message.getRepeatedNestedEnumList().add(TestAllTypes.NestedEnum.BAR); + message.getRepeatedForeignEnumList().add(ForeignEnum.FOREIGN_BAR); + message.getRepeatedImportEnumList().add(ImportEnum.IMPORT_BAR); + + message.getRepeatedStringPieceList().add("224"); + message.getRepeatedCordList().add("225"); + + // Add a second one of each field. + message.getRepeatedInt32List().add(301); + message.getRepeatedInt64List().add(302L); + message.getRepeatedUint32List().add(303); + message.getRepeatedUint64List().add(304l); + message.getRepeatedSint32List().add(305); + message.getRepeatedSint64List().add (306l); + message.getRepeatedFixed32List().add (307); + message.getRepeatedFixed64List().add (308l); + message.getRepeatedSfixed32List().add(309); + message.getRepeatedSfixed64List().add(310l); + message.getRepeatedFloatList().add (311f); + message.getRepeatedDoubleList().add (312d); + message.getRepeatedBoolList().add (false); + message.getRepeatedStringList().add ("315"); + message.getRepeatedBytesList().add (toBytes("316")); + + message.getRepeatedGroupList().add( + new TestAllTypes.RepeatedGroup().setA(317)); + message.getRepeatedNestedMessageList().add( + new TestAllTypes.NestedMessage().setBb(318)); + message.getRepeatedForeignMessageList().add( + new ForeignMessage().setC(319)); + message.getRepeatedImportMessageList().add( + new ImportMessage().setD(320)); + + message.getRepeatedNestedEnumList().add(TestAllTypes.NestedEnum.BAZ); + message.getRepeatedForeignEnumList().add(ForeignEnum.FOREIGN_BAZ); + message.getRepeatedImportEnumList().add(ImportEnum.IMPORT_BAZ); + + message.getRepeatedStringPieceList().add("324"); + message.getRepeatedCordList().add("325"); + + + // ----------------------------------------------------------------- + + message.setDefaultInt32 (401); + message.setDefaultInt64 (402); + message.setDefaultUint32 (403); + message.setDefaultUint64 (404); + message.setDefaultSint32 (405); + message.setDefaultSint64 (406); + message.setDefaultFixed32 (407); + message.setDefaultFixed64 (408); + message.setDefaultSfixed32(409); + message.setDefaultSfixed64(410); + message.setDefaultFloat (411); + message.setDefaultDouble (412); + message.setDefaultBool (false); + message.setDefaultString ("415"); + message.setDefaultBytes (toBytes("416")); + + message.setDefaultNestedEnum (TestAllTypes.NestedEnum.FOO); + message.setDefaultForeignEnum(ForeignEnum.FOREIGN_FOO); + message.setDefaultImportEnum (ImportEnum.IMPORT_FOO); + + message.setDefaultStringPiece("424"); + message.setDefaultCord("425"); + } + + // ------------------------------------------------------------------- + + /** + * Modify the repeated fields of {@code message} to contain the values + * expected by {@code assertRepeatedFieldsModified()}. + */ + public static void modifyRepeatedFields(TestAllTypes message) { + message.getRepeatedInt32List().set(1, 501); + message.getRepeatedInt64List().set (1, 502l); + message.getRepeatedUint32List().set (1, 503); + message.getRepeatedUint64List().set (1, 504l); + message.getRepeatedSint32List().set (1, 505); + message.getRepeatedSint64List().set (1, 506l); + message.getRepeatedFixed32List().set (1, 507); + message.getRepeatedFixed64List().set (1, 508l); + message.getRepeatedSfixed32List().set(1, 509); + message.getRepeatedSfixed64List().set(1, 510l); + message.getRepeatedFloatList().set (1, 511f); + message.getRepeatedDoubleList().set (1, 512d); + message.getRepeatedBoolList().set (1, true); + message.getRepeatedStringList().set (1, "515"); + message.getRepeatedBytesList().set (1, toBytes("516")); + + message.getRepeatedGroupList().set(1, + new TestAllTypes.RepeatedGroup().setA(517)); + message.getRepeatedNestedMessageList().set(1, + new TestAllTypes.NestedMessage().setBb(518)); + message.getRepeatedForeignMessageList().set(1, + new ForeignMessage().setC(519)); + message.getRepeatedImportMessageList().set(1, + new ImportMessage().setD(520)); + + message.getRepeatedNestedEnumList().set (1, TestAllTypes.NestedEnum.FOO); + message.getRepeatedForeignEnumList().set(1, ForeignEnum.FOREIGN_FOO); + message.getRepeatedImportEnumList().set (1, ImportEnum.IMPORT_FOO); + + message.getRepeatedStringPieceList().set(1, "524"); + message.getRepeatedCordList().set(1, "525"); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all fields of + * {@code message} are set to the values assigned by {@code setAllFields}. + */ + public static void assertAllFieldsSet(TestAllTypes message) { + assertTrue(message.hasOptionalInt32 ()); + assertTrue(message.hasOptionalInt64 ()); + assertTrue(message.hasOptionalUint32 ()); + assertTrue(message.hasOptionalUint64 ()); + assertTrue(message.hasOptionalSint32 ()); + assertTrue(message.hasOptionalSint64 ()); + assertTrue(message.hasOptionalFixed32 ()); + assertTrue(message.hasOptionalFixed64 ()); + assertTrue(message.hasOptionalSfixed32()); + assertTrue(message.hasOptionalSfixed64()); + assertTrue(message.hasOptionalFloat ()); + assertTrue(message.hasOptionalDouble ()); + assertTrue(message.hasOptionalBool ()); + assertTrue(message.hasOptionalString ()); + assertTrue(message.hasOptionalBytes ()); + + assertTrue(message.hasOptionalGroup ()); + assertTrue(message.hasOptionalNestedMessage ()); + assertTrue(message.hasOptionalForeignMessage()); + assertTrue(message.hasOptionalImportMessage ()); + + assertTrue(message.getOptionalGroup ().hasA()); + assertTrue(message.getOptionalNestedMessage ().hasBb()); + assertTrue(message.getOptionalForeignMessage().hasC()); + assertTrue(message.getOptionalImportMessage ().hasD()); + + assertTrue(message.hasOptionalNestedEnum ()); + assertTrue(message.hasOptionalForeignEnum()); + assertTrue(message.hasOptionalImportEnum ()); + + assertTrue(message.hasOptionalStringPiece()); + assertTrue(message.hasOptionalCord()); + + assertEquals(101 , message.getOptionalInt32 ()); + assertEquals(102 , message.getOptionalInt64 ()); + assertEquals(103 , message.getOptionalUint32 ()); + assertEquals(104 , message.getOptionalUint64 ()); + assertEquals(105 , message.getOptionalSint32 ()); + assertEquals(106 , message.getOptionalSint64 ()); + assertEquals(107 , message.getOptionalFixed32 ()); + assertEquals(108 , message.getOptionalFixed64 ()); + assertEquals(109 , message.getOptionalSfixed32()); + assertEquals(110 , message.getOptionalSfixed64()); + assertEquals(111 , message.getOptionalFloat (), 0.0); + assertEquals(112 , message.getOptionalDouble (), 0.0); + assertEquals(true , message.getOptionalBool ()); + assertEquals("115", message.getOptionalString ()); + assertEquals(toBytes("116"), message.getOptionalBytes()); + + assertEquals(117, message.getOptionalGroup ().getA()); + assertEquals(118, message.getOptionalNestedMessage ().getBb()); + assertEquals(119, message.getOptionalForeignMessage().getC()); + assertEquals(120, message.getOptionalImportMessage ().getD()); + + assertEquals(TestAllTypes.NestedEnum.BAZ, message.getOptionalNestedEnum()); + assertEquals(ForeignEnum.FOREIGN_BAZ, message.getOptionalForeignEnum()); + assertEquals(ImportEnum.IMPORT_BAZ, message.getOptionalImportEnum()); + + assertEquals("124", message.getOptionalStringPiece()); + assertEquals("125", message.getOptionalCord()); + + // ----------------------------------------------------------------- + + assertEquals(2, message.getRepeatedInt32List().size ()); + assertEquals(2, message.getRepeatedInt64List().size ()); + assertEquals(2, message.getRepeatedUint32List().size ()); + assertEquals(2, message.getRepeatedUint64List().size ()); + assertEquals(2, message.getRepeatedSint32List().size ()); + assertEquals(2, message.getRepeatedSint64List().size ()); + assertEquals(2, message.getRepeatedFixed32List().size ()); + assertEquals(2, message.getRepeatedFixed64List().size ()); + assertEquals(2, message.getRepeatedSfixed32List().size()); + assertEquals(2, message.getRepeatedSfixed64List().size()); + assertEquals(2, message.getRepeatedFloatList().size ()); + assertEquals(2, message.getRepeatedDoubleList().size ()); + assertEquals(2, message.getRepeatedBoolList().size ()); + assertEquals(2, message.getRepeatedStringList().size ()); + assertEquals(2, message.getRepeatedBytesList().size ()); + + assertEquals(2, message.getRepeatedGroupList().size ()); + assertEquals(2, message.getRepeatedNestedMessageList().size ()); + assertEquals(2, message.getRepeatedForeignMessageList().size()); + assertEquals(2, message.getRepeatedImportMessageList().size ()); + assertEquals(2, message.getRepeatedNestedEnumList().size ()); + assertEquals(2, message.getRepeatedForeignEnumList().size ()); + assertEquals(2, message.getRepeatedImportEnumList().size ()); + + assertEquals(2, message.getRepeatedStringPieceList().size()); + assertEquals(2, message.getRepeatedCordList().size()); + + assertEquals(201 , (int)message.getRepeatedInt32List().get(0)); + assertEquals(202 , (long)message.getRepeatedInt64List().get (0)); + assertEquals(203 , (int)message.getRepeatedUint32List().get (0)); + assertEquals(204 , (long)message.getRepeatedUint64List().get (0)); + assertEquals(205 , (int)message.getRepeatedSint32List().get (0)); + assertEquals(206 , (long)message.getRepeatedSint64List().get (0)); + assertEquals(207 , (int)message.getRepeatedFixed32List().get (0)); + assertEquals(208 , (long)message.getRepeatedFixed64List().get (0)); + assertEquals(209 , (int)message.getRepeatedSfixed32List().get(0)); + assertEquals(210 , (long)message.getRepeatedSfixed64List().get(0)); + assertEquals(211 , message.getRepeatedFloatList().get (0), 0.0); + assertEquals(212 , message.getRepeatedDoubleList().get (0), 0.0); + assertEquals(true , (boolean)message.getRepeatedBoolList().get (0)); + assertEquals("215", message.getRepeatedStringList().get (0)); + assertEquals(toBytes("216"), message.getRepeatedBytesList().get(0)); + + assertEquals(217, message.getRepeatedGroupList().get (0).getA()); + assertEquals(218, message.getRepeatedNestedMessageList().get (0).getBb()); + assertEquals(219, message.getRepeatedForeignMessageList().get(0).getC()); + assertEquals(220, message.getRepeatedImportMessageList().get (0).getD()); + + assertEquals(TestAllTypes.NestedEnum.BAR, message.getRepeatedNestedEnumList().get (0)); + assertEquals(ForeignEnum.FOREIGN_BAR, message.getRepeatedForeignEnumList().get(0)); + assertEquals(ImportEnum.IMPORT_BAR, message.getRepeatedImportEnumList().get(0)); + + assertEquals("224", message.getRepeatedStringPieceList().get(0)); + assertEquals("225", message.getRepeatedCordList().get(0)); + + assertEquals(301 , (int)message.getRepeatedInt32List().get (1)); + assertEquals(302 , (long)message.getRepeatedInt64List().get (1)); + assertEquals(303 , (int)message.getRepeatedUint32List().get (1)); + assertEquals(304 , (long)message.getRepeatedUint64List().get (1)); + assertEquals(305 , (int)message.getRepeatedSint32List().get (1)); + assertEquals(306 , (long)message.getRepeatedSint64List().get (1)); + assertEquals(307 , (int)message.getRepeatedFixed32List().get (1)); + assertEquals(308 , (long)message.getRepeatedFixed64List().get (1)); + assertEquals(309 , (int)message.getRepeatedSfixed32List().get(1)); + assertEquals(310 , (long)message.getRepeatedSfixed64List().get(1)); + assertEquals(311 , message.getRepeatedFloatList().get (1), 0.0); + assertEquals(312 , message.getRepeatedDoubleList().get (1), 0.0); + assertEquals(false, (boolean)message.getRepeatedBoolList().get (1)); + assertEquals("315", message.getRepeatedStringList().get (1)); + assertEquals(toBytes("316"), message.getRepeatedBytesList().get(1)); + + assertEquals(317, message.getRepeatedGroupList().get (1).getA()); + assertEquals(318, message.getRepeatedNestedMessageList().get (1).getBb()); + assertEquals(319, message.getRepeatedForeignMessageList().get(1).getC()); + assertEquals(320, message.getRepeatedImportMessageList().get (1).getD()); + + assertEquals(TestAllTypes.NestedEnum.BAZ, message.getRepeatedNestedEnumList().get (1)); + assertEquals(ForeignEnum.FOREIGN_BAZ, message.getRepeatedForeignEnumList().get(1)); + assertEquals(ImportEnum.IMPORT_BAZ, message.getRepeatedImportEnumList().get(1)); + + assertEquals("324", message.getRepeatedStringPieceList().get(1)); + assertEquals("325", message.getRepeatedCordList().get(1)); + + // ----------------------------------------------------------------- + + assertTrue(message.hasDefaultInt32 ()); + assertTrue(message.hasDefaultInt64 ()); + assertTrue(message.hasDefaultUint32 ()); + assertTrue(message.hasDefaultUint64 ()); + assertTrue(message.hasDefaultSint32 ()); + assertTrue(message.hasDefaultSint64 ()); + assertTrue(message.hasDefaultFixed32 ()); + assertTrue(message.hasDefaultFixed64 ()); + assertTrue(message.hasDefaultSfixed32()); + assertTrue(message.hasDefaultSfixed64()); + assertTrue(message.hasDefaultFloat ()); + assertTrue(message.hasDefaultDouble ()); + assertTrue(message.hasDefaultBool ()); + assertTrue(message.hasDefaultString ()); + assertTrue(message.hasDefaultBytes ()); + + assertTrue(message.hasDefaultNestedEnum ()); + assertTrue(message.hasDefaultForeignEnum()); + assertTrue(message.hasDefaultImportEnum ()); + + assertTrue(message.hasDefaultStringPiece()); + assertTrue(message.hasDefaultCord()); + + assertEquals(401 , message.getDefaultInt32 ()); + assertEquals(402 , message.getDefaultInt64 ()); + assertEquals(403 , message.getDefaultUint32 ()); + assertEquals(404 , message.getDefaultUint64 ()); + assertEquals(405 , message.getDefaultSint32 ()); + assertEquals(406 , message.getDefaultSint64 ()); + assertEquals(407 , message.getDefaultFixed32 ()); + assertEquals(408 , message.getDefaultFixed64 ()); + assertEquals(409 , message.getDefaultSfixed32()); + assertEquals(410 , message.getDefaultSfixed64()); + assertEquals(411 , message.getDefaultFloat (), 0.0); + assertEquals(412 , message.getDefaultDouble (), 0.0); + assertEquals(false, message.getDefaultBool ()); + assertEquals("415", message.getDefaultString ()); + assertEquals(toBytes("416"), message.getDefaultBytes()); + + assertEquals(TestAllTypes.NestedEnum.FOO, message.getDefaultNestedEnum ()); + assertEquals(ForeignEnum.FOREIGN_FOO, message.getDefaultForeignEnum()); + assertEquals(ImportEnum.IMPORT_FOO, message.getDefaultImportEnum()); + + assertEquals("424", message.getDefaultStringPiece()); + assertEquals("425", message.getDefaultCord()); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all fields of + * {@code message} are cleared, and that getting the fields returns their + * default values. + */ + public static void assertClear(TestAllTypes message) { + // hasBlah() should initially be false for all optional fields. + assertFalse(message.hasOptionalInt32 ()); + assertFalse(message.hasOptionalInt64 ()); + assertFalse(message.hasOptionalUint32 ()); + assertFalse(message.hasOptionalUint64 ()); + assertFalse(message.hasOptionalSint32 ()); + assertFalse(message.hasOptionalSint64 ()); + assertFalse(message.hasOptionalFixed32 ()); + assertFalse(message.hasOptionalFixed64 ()); + assertFalse(message.hasOptionalSfixed32()); + assertFalse(message.hasOptionalSfixed64()); + assertFalse(message.hasOptionalFloat ()); + assertFalse(message.hasOptionalDouble ()); + assertFalse(message.hasOptionalBool ()); + assertFalse(message.hasOptionalString ()); + assertFalse(message.hasOptionalBytes ()); + + assertFalse(message.hasOptionalGroup ()); + assertFalse(message.hasOptionalNestedMessage ()); + assertFalse(message.hasOptionalForeignMessage()); + assertFalse(message.hasOptionalImportMessage ()); + + assertFalse(message.hasOptionalNestedEnum ()); + assertFalse(message.hasOptionalForeignEnum()); + assertFalse(message.hasOptionalImportEnum ()); + + assertFalse(message.hasOptionalStringPiece()); + assertFalse(message.hasOptionalCord()); + + // Optional fields without defaults are set to zero or something like it. + assertEquals(0 , message.getOptionalInt32 ()); + assertEquals(0 , message.getOptionalInt64 ()); + assertEquals(0 , message.getOptionalUint32 ()); + assertEquals(0 , message.getOptionalUint64 ()); + assertEquals(0 , message.getOptionalSint32 ()); + assertEquals(0 , message.getOptionalSint64 ()); + assertEquals(0 , message.getOptionalFixed32 ()); + assertEquals(0 , message.getOptionalFixed64 ()); + assertEquals(0 , message.getOptionalSfixed32()); + assertEquals(0 , message.getOptionalSfixed64()); + assertEquals(0 , message.getOptionalFloat (), 0.0); + assertEquals(0 , message.getOptionalDouble (), 0.0); + assertEquals(false, message.getOptionalBool ()); + assertEquals(null , message.getOptionalString ()); + assertEquals(null, message.getOptionalBytes()); + assertEquals(null, message.getOptionalNestedEnum ()); + assertEquals(null, message.getOptionalForeignEnum()); + assertEquals(null, message.getOptionalImportEnum()); + assertEquals(null, message.getOptionalStringPiece()); + assertEquals(null, message.getOptionalCord()); + + // Embedded messages should also be clear. + assertFalse(message.getOptionalGroup ().hasA()); + assertFalse(message.getOptionalNestedMessage ().hasBb()); + assertFalse(message.getOptionalForeignMessage().hasC()); + assertFalse(message.getOptionalImportMessage ().hasD()); + + assertEquals(0, message.getOptionalGroup ().getA()); + assertEquals(0, message.getOptionalNestedMessage ().getBb()); + assertEquals(0, message.getOptionalForeignMessage().getC()); + assertEquals(0, message.getOptionalImportMessage ().getD()); + + + + // Repeated fields are empty. + assertEquals(0, message.getRepeatedInt32List().size ()); + assertEquals(0, message.getRepeatedInt64List().size ()); + assertEquals(0, message.getRepeatedUint32List().size ()); + assertEquals(0, message.getRepeatedUint64List().size ()); + assertEquals(0, message.getRepeatedSint32List().size ()); + assertEquals(0, message.getRepeatedSint64List().size ()); + assertEquals(0, message.getRepeatedFixed32List().size ()); + assertEquals(0, message.getRepeatedFixed64List().size ()); + assertEquals(0, message.getRepeatedSfixed32List().size()); + assertEquals(0, message.getRepeatedSfixed64List().size()); + assertEquals(0, message.getRepeatedFloatList().size ()); + assertEquals(0, message.getRepeatedDoubleList().size ()); + assertEquals(0, message.getRepeatedBoolList().size ()); + assertEquals(0, message.getRepeatedStringList().size ()); + assertEquals(0, message.getRepeatedBytesList().size ()); + + assertEquals(0, message.getRepeatedGroupList().size ()); + assertEquals(0, message.getRepeatedNestedMessageList().size ()); + assertEquals(0, message.getRepeatedForeignMessageList().size()); + assertEquals(0, message.getRepeatedImportMessageList().size ()); + assertEquals(0, message.getRepeatedNestedEnumList().size ()); + assertEquals(0, message.getRepeatedForeignEnumList().size ()); + assertEquals(0, message.getRepeatedImportEnumList().size ()); + + assertEquals(0, message.getRepeatedStringPieceList().size()); + assertEquals(0, message.getRepeatedCordList().size()); + + // hasBlah() should also be false for all default fields. + assertFalse(message.hasDefaultInt32 ()); + assertFalse(message.hasDefaultInt64 ()); + assertFalse(message.hasDefaultUint32 ()); + assertFalse(message.hasDefaultUint64 ()); + assertFalse(message.hasDefaultSint32 ()); + assertFalse(message.hasDefaultSint64 ()); + assertFalse(message.hasDefaultFixed32 ()); + assertFalse(message.hasDefaultFixed64 ()); + assertFalse(message.hasDefaultSfixed32()); + assertFalse(message.hasDefaultSfixed64()); + assertFalse(message.hasDefaultFloat ()); + assertFalse(message.hasDefaultDouble ()); + assertFalse(message.hasDefaultBool ()); + assertFalse(message.hasDefaultString ()); + assertFalse(message.hasDefaultBytes ()); + + assertFalse(message.hasDefaultNestedEnum ()); + assertFalse(message.hasDefaultForeignEnum()); + assertFalse(message.hasDefaultImportEnum ()); + + assertFalse(message.hasDefaultStringPiece()); + assertFalse(message.hasDefaultCord()); + + // Fields with defaults have their default values (duh). + assertEquals( 41 , message.getDefaultInt32 ()); + assertEquals( 42 , message.getDefaultInt64 ()); + assertEquals( 43 , message.getDefaultUint32 ()); + assertEquals( 44 , message.getDefaultUint64 ()); + assertEquals(-45 , message.getDefaultSint32 ()); + assertEquals( 46 , message.getDefaultSint64 ()); + assertEquals( 47 , message.getDefaultFixed32 ()); + assertEquals( 48 , message.getDefaultFixed64 ()); + assertEquals( 49 , message.getDefaultSfixed32()); + assertEquals(-50 , message.getDefaultSfixed64()); + assertEquals( 51.5 , message.getDefaultFloat (), 0.0); + assertEquals( 52e3 , message.getDefaultDouble (), 0.0); + assertEquals(true , message.getDefaultBool ()); + assertEquals("hello", message.getDefaultString ()); + assertEquals(toBytes("world"), message.getDefaultBytes()); + + assertEquals(TestAllTypes.NestedEnum.BAR, message.getDefaultNestedEnum ()); + assertEquals(ForeignEnum.FOREIGN_BAR, message.getDefaultForeignEnum()); + assertEquals(ImportEnum.IMPORT_BAR, message.getDefaultImportEnum()); + + assertEquals("abc", message.getDefaultStringPiece()); + assertEquals("123", message.getDefaultCord()); + } + + // ------------------------------------------------------------------- + + /** + * Assert (using {@code junit.framework.Assert}} that all fields of + * {@code message} are set to the values assigned by {@code setAllFields} + * followed by {@code modifyRepeatedFields}. + */ + public static void assertRepeatedFieldsModified(TestAllTypes message) { + // ModifyRepeatedFields only sets the second repeated element of each + // field. In addition to verifying this, we also verify that the first + // element and size were *not* modified. + assertEquals(2, message.getRepeatedInt32List().size ()); + assertEquals(2, message.getRepeatedInt64List().size ()); + assertEquals(2, message.getRepeatedUint32List().size ()); + assertEquals(2, message.getRepeatedUint64List().size ()); + assertEquals(2, message.getRepeatedSint32List().size ()); + assertEquals(2, message.getRepeatedSint64List().size ()); + assertEquals(2, message.getRepeatedFixed32List().size ()); + assertEquals(2, message.getRepeatedFixed64List().size ()); + assertEquals(2, message.getRepeatedSfixed32List().size()); + assertEquals(2, message.getRepeatedSfixed64List().size()); + assertEquals(2, message.getRepeatedFloatList().size ()); + assertEquals(2, message.getRepeatedDoubleList().size ()); + assertEquals(2, message.getRepeatedBoolList().size ()); + assertEquals(2, message.getRepeatedStringList().size ()); + assertEquals(2, message.getRepeatedBytesList().size ()); + + assertEquals(2, message.getRepeatedGroupList().size ()); + assertEquals(2, message.getRepeatedNestedMessageList().size ()); + assertEquals(2, message.getRepeatedForeignMessageList().size()); + assertEquals(2, message.getRepeatedImportMessageList().size ()); + assertEquals(2, message.getRepeatedNestedEnumList().size ()); + assertEquals(2, message.getRepeatedForeignEnumList().size ()); + assertEquals(2, message.getRepeatedImportEnumList().size ()); + + assertEquals(2, message.getRepeatedStringPieceList().size()); + assertEquals(2, message.getRepeatedCordList().size()); + + assertEquals(201 , (int)message.getRepeatedInt32List().get (0)); + assertEquals(202L , (long)message.getRepeatedInt64List().get (0)); + assertEquals(203 , (int)message.getRepeatedUint32List().get (0)); + assertEquals(204L , (long)message.getRepeatedUint64List().get (0)); + assertEquals(205 , (int)message.getRepeatedSint32List().get (0)); + assertEquals(206L , (long)message.getRepeatedSint64List().get (0)); + assertEquals(207 , (int)message.getRepeatedFixed32List().get (0)); + assertEquals(208L , (long)message.getRepeatedFixed64List().get (0)); + assertEquals(209 , (int)message.getRepeatedSfixed32List().get(0)); + assertEquals(210L , (long)message.getRepeatedSfixed64List().get(0)); + assertEquals(Float.valueOf(211F) , Float.valueOf(message.getRepeatedFloatList().get(0))); + assertEquals(Double.valueOf(212D), Double.valueOf(message.getRepeatedDoubleList().get(0))); + assertEquals(true , (boolean)message.getRepeatedBoolList().get (0)); + assertEquals("215", message.getRepeatedStringList().get (0)); + assertEquals(toBytes("216"), message.getRepeatedBytesList().get(0)); + + assertEquals(217, message.getRepeatedGroupList().get (0).getA()); + assertEquals(218, message.getRepeatedNestedMessageList().get (0).getBb()); + assertEquals(219, message.getRepeatedForeignMessageList().get(0).getC()); + assertEquals(220, message.getRepeatedImportMessageList().get (0).getD()); + + assertEquals(TestAllTypes.NestedEnum.BAR, message.getRepeatedNestedEnumList().get (0)); + assertEquals(ForeignEnum.FOREIGN_BAR, message.getRepeatedForeignEnumList().get(0)); + assertEquals(ImportEnum.IMPORT_BAR, message.getRepeatedImportEnumList().get(0)); + + assertEquals("224", message.getRepeatedStringPieceList().get(0)); + assertEquals("225", message.getRepeatedCordList().get(0)); + + // Actually verify the second (modified) elements now. + assertEquals(501 , (int)message.getRepeatedInt32List().get (1)); + assertEquals(502L , (long)message.getRepeatedInt64List().get (1)); + assertEquals(503 , (int)message.getRepeatedUint32List().get (1)); + assertEquals(504L , (long)message.getRepeatedUint64List().get (1)); + assertEquals(505 , (int)message.getRepeatedSint32List().get (1)); + assertEquals(506L , (long)message.getRepeatedSint64List().get (1)); + assertEquals(507 , (int)message.getRepeatedFixed32List().get (1)); + assertEquals(508L , (long)message.getRepeatedFixed64List().get (1)); + assertEquals(509 , (int)message.getRepeatedSfixed32List().get(1)); + assertEquals(510L , (long)message.getRepeatedSfixed64List().get(1)); + assertEquals(Float.valueOf(511F) , Float.valueOf(message.getRepeatedFloatList().get(1))); + assertEquals(Double.valueOf(512D) , Double.valueOf(message.getRepeatedDoubleList().get(1))); + assertEquals(true , (boolean)message.getRepeatedBoolList().get (1)); + assertEquals("515", message.getRepeatedStringList().get (1)); + assertEquals(toBytes("516"), message.getRepeatedBytesList().get(1)); + + assertEquals(517, message.getRepeatedGroupList().get (1).getA()); + assertEquals(518, message.getRepeatedNestedMessageList().get (1).getBb()); + assertEquals(519, message.getRepeatedForeignMessageList().get(1).getC()); + assertEquals(520, message.getRepeatedImportMessageList().get (1).getD()); + + assertEquals(TestAllTypes.NestedEnum.FOO, message.getRepeatedNestedEnumList().get (1)); + assertEquals(ForeignEnum.FOREIGN_FOO, message.getRepeatedForeignEnumList().get(1)); + assertEquals(ImportEnum.IMPORT_FOO, message.getRepeatedImportEnumList().get(1)); + + assertEquals("524", message.getRepeatedStringPieceList().get(1)); + assertEquals("525", message.getRepeatedCordList().get(1)); + } + + // =================================================================== + // Like above, but for extensions + + // Java gets confused with things like assertEquals(int, Integer): it can't + // decide whether to call assertEquals(int, int) or assertEquals(Object, + // Object). So we define these methods to help it. + private static void assertEqualsExactType(int a, int b) { + assertEquals(a, b); + } + private static void assertEqualsExactType(long a, long b) { + assertEquals(a, b); + } + private static void assertEqualsExactType(float a, float b) { + assertEquals(a, b, 0.0); + } + private static void assertEqualsExactType(double a, double b) { + assertEquals(a, b, 0.0); + } + private static void assertEqualsExactType(boolean a, boolean b) { + assertEquals(a, b); + } + private static void assertEqualsExactType(String a, String b) { + assertEquals(a, b); + } + private static void assertEqualsExactType(Buffer a, Buffer b) { + assertEquals(a, b); + } + private static void assertEqualsExactType(TestAllTypes.NestedEnum a, + TestAllTypes.NestedEnum b) { + assertEquals(a, b); + } + private static void assertEqualsExactType(ForeignEnum a, ForeignEnum b) { + assertEquals(a, b); + } + private static void assertEqualsExactType(ImportEnum a, ImportEnum b) { + assertEquals(a, b); + } + + /** + * @param filePath The path relative to + * {@link com.google.testing.util.TestUtil#getDefaultSrcDir}. + */ + public static String readTextFromFile(String filePath) { + return readBytesFromFile(filePath).toStringUtf8(); + } + + private static File getTestDataDir() { + // Search each parent directory looking for "src/google/protobuf". + File ancestor = new File("."); + try { + ancestor = ancestor.getCanonicalFile(); + } catch (IOException e) { + throw new RuntimeException( + "Couldn't get canonical name of working directory.", e); + } + while (ancestor != null && ancestor.exists()) { + if (new File(ancestor, "src/google/protobuf").exists()) { + return new File(ancestor, "src/google/protobuf/testdata"); + } + ancestor = ancestor.getParentFile(); + } + + throw new RuntimeException( + "Could not find golden files. This test must be run from within the " + + "protobuf source package so that it can read test data files from the " + + "C++ source tree."); + } + + /** + * @param filePath The path relative to + * {@link com.google.testing.util.TestUtil#getDefaultSrcDir}. + */ + public static Buffer readBytesFromFile(String filename) { + File fullPath = new File(getTestDataDir(), filename); + try(RandomAccessFile file = new RandomAccessFile(fullPath, "r")) { + byte[] content = new byte[(int) file.length()]; + file.readFully(content); + return new Buffer(content); + } catch (IOException e) { + // Throw a RuntimeException here so that we can call this function from + // static initializers. + throw new IllegalArgumentException( + "Couldn't read file: " + fullPath.getPath(), e); + } + } + + /** + * Get the bytes of the "golden message". This is a serialized TestAllTypes + * with all fields set as they would be by + * {@link setAllFields(TestAllTypes.Builder)}, but it is loaded from a file + * on disk rather than generated dynamically. The file is actually generated + * by C++ code, so testing against it verifies compatibility with C++. + */ + public static Buffer getGoldenMessage() { + if (goldenMessage == null) { + goldenMessage = readBytesFromFile("golden_message"); + } + return goldenMessage; + } + private static Buffer goldenMessage = null; +} diff --git a/activemq-protobuf-test/src/test/java/com/google/protobuf/WireFormatTest.java b/activemq-protobuf-test/src/test/java/com/google/protobuf/WireFormatTest.java new file mode 100644 index 0000000000..7724141ce7 --- /dev/null +++ b/activemq-protobuf-test/src/test/java/com/google/protobuf/WireFormatTest.java @@ -0,0 +1,60 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.protobuf; + +import junit.framework.TestCase; + +import org.apache.activemq.protobuf.Buffer; +import org.apache.activemq.protobuf.CodedInputStream; + +import protobuf_unittest.UnittestProto.TestAllTypes; + +/** + * Tests related to parsing and serialization. + * + * @author kenton@google.com (Kenton Varda) + */ +public class WireFormatTest extends TestCase { + public void testSerialization() throws Exception { + TestAllTypes message = TestUtil.getAllSet(); + + byte[] rawBytes = message.toUnframedByteArray(); + assertEquals(rawBytes.length, message.serializedSizeUnframed()); + + TestAllTypes message2 = TestAllTypes.parseUnframed(rawBytes); + + TestUtil.assertAllFieldsSet(message2); + } + + private void assertFieldsInOrder(Buffer data) throws Exception { + try(CodedInputStream input = new CodedInputStream(data)) { + int previousTag = 0; + + while (true) { + int tag = input.readTag(); + if (tag == 0) { + break; + } + + assertTrue(tag > previousTag); + input.skipField(tag); + } + } + } + +} + diff --git a/activemq-protobuf-test/src/test/java/com/google/protobuf/multiple_files_test.proto b/activemq-protobuf-test/src/test/java/com/google/protobuf/multiple_files_test.proto new file mode 100644 index 0000000000..1dbadfe0aa --- /dev/null +++ b/activemq-protobuf-test/src/test/java/com/google/protobuf/multiple_files_test.proto @@ -0,0 +1,53 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// 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. + +// Author: kenton@google.com (Kenton Varda) +// +// A proto file which tests the java_multiple_files option. + + +import "google/protobuf/unittest.proto"; + +package protobuf_unittest; + +option java_multiple_files = true; +option java_outer_classname = "MultipleFilesTestProto"; + +message MessageWithNoOuter { + message NestedMessage { + optional int32 i = 1; + } + enum NestedEnum { + BAZ = 3; + } + optional NestedMessage nested = 1; + repeated TestAllTypes foreign = 2; + optional NestedEnum nested_enum = 3; + optional EnumWithNoOuter foreign_enum = 4; +} + +enum EnumWithNoOuter { + FOO = 1; + BAR = 2; +} + +service ServiceWithNoOuter { + rpc Foo(MessageWithNoOuter) returns(TestAllTypes); +} + +extend TestAllExtensions { + optional int32 extension_with_outer = 1234567; +} diff --git a/activemq-protobuf-test/src/test/java/org/apache/activemq/protobuf/DeferredUnmarshalTest.java b/activemq-protobuf-test/src/test/java/org/apache/activemq/protobuf/DeferredUnmarshalTest.java new file mode 100644 index 0000000000..d269253a0c --- /dev/null +++ b/activemq-protobuf-test/src/test/java/org/apache/activemq/protobuf/DeferredUnmarshalTest.java @@ -0,0 +1,75 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf; + +import junit.framework.TestCase; + +import org.apache.activemq.protobuf.DeferredUnmarshal.Bar; +import org.apache.activemq.protobuf.DeferredUnmarshal.Foo; + +public class DeferredUnmarshalTest extends TestCase { + + public void testDeferredDecoding() throws InvalidProtocolBufferException { + + Foo foo = new Foo(); + foo.setField1(5); + foo.setField2(20); + + Bar bar = new Bar(); + + // There is no decoding pending so its' considered decoded. + assertTrue(bar.isDecoded()); + + bar.setField1(25); + bar.setField2(220); + bar.setField3(foo); + + // The message should not be encoded yet. + assertFalse(bar.isEncoded()); + + // The message should be encoded now.. + byte[] encodedForm = bar.toUnframedByteArray(); + assertTrue(bar.isEncoded()); + + // Repeated encoding operations should just give back the same byte[] + assertTrue(encodedForm == bar.toUnframedByteArray()); + + // Decoding does not occur until a field is accessed. The new message should + // still be considered encoded. + Bar bar2 = Bar.parseUnframed(encodedForm); + assertTrue(bar2.isEncoded()); + assertFalse(bar2.isDecoded()); + + // This should now decode the message. + assertEquals(25, bar2.getField1()); + assertTrue(bar2.isDecoded()); + + // Since bar2 still has not been modified it should still spit out the same + // byte[] + assertTrue(encodedForm == bar2.toUnframedByteArray()); + + // Nested messages should remain un-decoded. + assertFalse(bar2.getField3().isDecoded()); + + // Changing a field should remove the encoding. + bar2.setField1(35); + assertFalse(bar2.isEncoded()); + assertTrue(bar2.isDecoded()); + + } + +} diff --git a/activemq-protobuf-test/src/test/java/org/apache/activemq/protobuf/EqualsTest.java b/activemq-protobuf-test/src/test/java/org/apache/activemq/protobuf/EqualsTest.java new file mode 100644 index 0000000000..992b0822ef --- /dev/null +++ b/activemq-protobuf-test/src/test/java/org/apache/activemq/protobuf/EqualsTest.java @@ -0,0 +1,57 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf; + +import org.apache.activemq.protobuf.DeferredUnmarshal.Bar; +import org.apache.activemq.protobuf.DeferredUnmarshal.Foo; + +import junit.framework.TestCase; + +public class EqualsTest extends TestCase { + + public void testDeferredUnmarshal() { + + Bar bar1 = createBar(); + Bar bar2 = createBar(); + + // They should have the same hash and equal the same value. + assertTrue(bar1.hashCode() == bar2.hashCode()); + assertTrue(bar1.equals(bar2)); + + // Change bar2 a little. + + bar2.setField2(35); + + assertFalse(bar1.hashCode() == bar2.hashCode()); + assertFalse(bar1.equals(bar2)); + + } + + private Bar createBar() { + Bar bar; + Foo foo = new Foo(); + foo.setField1(5); + foo.setField2(20); + + bar = new Bar(); + bar.setField1(25); + bar.setField2(220); + bar.setField3(foo); + return bar; + } + +} diff --git a/activemq-protobuf/README.txt b/activemq-protobuf/README.txt new file mode 100644 index 0000000000..e467c04c63 --- /dev/null +++ b/activemq-protobuf/README.txt @@ -0,0 +1,25 @@ +======================================================================= +The AcitveMQ Protocol Buffers Java Implementation +======================================================================= + +Protocol Buffers is a data interchange format developed by +Google. You can get more information about Protocol Buffers +at: + + http://code.google.com/apis/protocolbuffers/ + + +Unfortunately the the main Protocol Buffer's project made the +Java API cumbersome to use since the messages are immutable. They +Justify this decision by highlighting the fact it reduces end user +error that occur with Mutable messages. + +This module brings you a slimmed down lean and mean API to accessing +Protocol Buffer data structures. It provides little protection +from end users 'hurting themselves', but it does give power user +and easier to use API. + +In addition, this module provides a Java based code generator so +that it's easier to code generate your Protocol Buffer classes in +a java based build system like Ant or Maven. + diff --git a/activemq-protobuf/pom.xml b/activemq-protobuf/pom.xml new file mode 100644 index 0000000000..c1d10fe71f --- /dev/null +++ b/activemq-protobuf/pom.xml @@ -0,0 +1,90 @@ + + + + + 4.0.0 + + org.apache.activemq + activemq-parent + 6.1.3-SNAPSHOT + + + org.apache.activemq.protobuf + activemq-protobuf + maven-plugin + + ActiveMQ :: Protocol Buffers and Compiler + + + A Simpler Protocol Buffer Java API. Includes a Proto to Java compiler. + + + + + org.apache.maven + maven-plugin-api + ${maven-core-version} + true + + + org.apache.maven + maven-core + ${maven-core-version} + true + + + + junit + junit + test + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + + amqprotobuf + + + + maven-surefire-plugin + + + **/*Test.java + + + + + org.codehaus.mojo + javacc-maven-plugin + + + javacc + + javacc + + + + + + + + diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/AsciiBuffer.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/AsciiBuffer.java new file mode 100644 index 0000000000..8248b1d83d --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/AsciiBuffer.java @@ -0,0 +1,91 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf; + + +final public class AsciiBuffer extends Buffer { + + private int hashCode; + + public AsciiBuffer(Buffer other) { + super(other); + } + + public AsciiBuffer(byte[] data, int offset, int length) { + super(data, offset, length); + } + + public AsciiBuffer(byte[] data) { + super(data); + } + + public AsciiBuffer(String input) { + super(encode(input)); + } + + public AsciiBuffer compact() { + if (length != data.length) { + return new AsciiBuffer(toByteArray()); + } + return this; + } + + public String toString() + { + return decode(this); + } + + @Override + public boolean equals(Object obj) { + if( obj==this ) + return true; + + if( obj==null || obj.getClass()!=AsciiBuffer.class ) + return false; + + return equals((Buffer)obj); + } + + @Override + public int hashCode() { + if( hashCode==0 ) { + hashCode = super.hashCode();; + } + return hashCode; + } + + static public byte[] encode(String value) + { + int size = value.length(); + byte rc[] = new byte[size]; + for( int i=0; i < size; i++ ) { + rc[i] = (byte)(value.charAt(i)&0xFF); + } + return rc; + } + static public String decode(Buffer value) + { + int size = value.getLength(); + char rc[] = new char[size]; + for( int i=0; i < size; i++ ) { + rc[i] = (char)(value.byteAt(i) & 0xFF ); + } + return new String(rc); + } + + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/BaseMessage.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/BaseMessage.java new file mode 100644 index 0000000000..9f10bfac2e --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/BaseMessage.java @@ -0,0 +1,334 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf; + +import static org.apache.activemq.protobuf.WireFormat.WIRETYPE_END_GROUP; +import static org.apache.activemq.protobuf.WireFormat.WIRETYPE_LENGTH_DELIMITED; +import static org.apache.activemq.protobuf.WireFormat.WIRETYPE_START_GROUP; +import static org.apache.activemq.protobuf.WireFormat.makeTag; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +abstract public class BaseMessage implements Message { + + protected int memoizedSerializedSize = -1; + + abstract public T clone() throws CloneNotSupportedException; + + public void clear() { + memoizedSerializedSize = -1; + } + + public boolean isInitialized() { + return missingFields().isEmpty(); + } + + @SuppressWarnings("unchecked") + public T assertInitialized() throws UninitializedMessageException { + java.util.ArrayList missingFields = missingFields(); + if (!missingFields.isEmpty()) { + throw new UninitializedMessageException(missingFields); + } + return getThis(); + } + + @SuppressWarnings("unchecked") + protected T checktInitialized() throws InvalidProtocolBufferException { + java.util.ArrayList missingFields = missingFields(); + if (!missingFields.isEmpty()) { + throw new UninitializedMessageException(missingFields).asInvalidProtocolBufferException(); + } + return getThis(); + } + + public ArrayList missingFields() { + load(); + return new ArrayList(); + } + + protected void loadAndClear() { + memoizedSerializedSize = -1; + } + + protected void load() { + } + + @SuppressWarnings("unchecked") + public T mergeFrom(T other) { + return getThis(); + } + + public void writeUnframed(CodedOutputStream output) throws java.io.IOException { + // if (encodedForm == null) { + // encodedForm = new byte[serializedSizeUnframed()]; + // com.google.protobuf.CodedOutputStream original = output; + // output = + // com.google.protobuf.CodedOutputStream.newInstance(encodedForm); + // if (hasField1()) { + // output.writeInt32(1, getField1()); + // } + // if (hasField2()) { + // output.writeInt64(2, getField2()); + // } + // if (hasField3()) { + // writeMessage(output, 3, getField3()); + // } + // output.checkNoSpaceLeft(); + // output = original; + // } + // output.writeRawBytes(encodedForm); + } + + // ///////////////////////////////////////////////////////////////// + // Write related helpers. + // ///////////////////////////////////////////////////////////////// + + public void writeFramed(CodedOutputStream output) throws IOException { + output.writeRawVarint32(serializedSizeUnframed()); + writeUnframed(output); + } + + public Buffer toUnframedBuffer() { + try { + int size = serializedSizeUnframed(); + BufferOutputStream baos = new BufferOutputStream(size); + CodedOutputStream output = new CodedOutputStream(baos); + writeUnframed(output); + Buffer rc = baos.toBuffer(); + if( rc.length != size ) { + throw new IllegalStateException("Did not write as much data as expected."); + } + return rc; + } catch (IOException e) { + throw new RuntimeException("Serializing to a byte array threw an IOException " + "(should never happen).", e); + } + } + + public Buffer toFramedBuffer() { + try { + int size = serializedSizeFramed(); + BufferOutputStream baos = new BufferOutputStream(size); + CodedOutputStream output = new CodedOutputStream(baos); + writeFramed(output); + Buffer rc = baos.toBuffer(); + if( rc.length != size ) { + throw new IllegalStateException("Did not write as much data as expected."); + } + return rc; + } catch (IOException e) { + throw new RuntimeException("Serializing to a byte array threw an IOException " + "(should never happen).", e); + } + } + + public byte[] toUnframedByteArray() { + return toUnframedBuffer().toByteArray(); + } + + public byte[] toFramedByteArray() { + return toFramedBuffer().toByteArray(); + } + + public void writeFramed(OutputStream output) throws IOException { + CodedOutputStream codedOutput = new CodedOutputStream(output); + writeFramed(codedOutput); + codedOutput.flush(); + } + + public void writeUnframed(OutputStream output) throws IOException { + CodedOutputStream codedOutput = new CodedOutputStream(output); + writeUnframed(codedOutput); + codedOutput.flush(); + } + + public int serializedSizeFramed() { + int t = serializedSizeUnframed(); + return CodedOutputStream.computeRawVarint32Size(t) + t; + + } + + // ///////////////////////////////////////////////////////////////// + // Read related helpers. + // ///////////////////////////////////////////////////////////////// + + public T mergeFramed(CodedInputStream input) throws IOException { + int length = input.readRawVarint32(); + int oldLimit = input.pushLimit(length); + T rc = mergeUnframed(input); + input.checkLastTagWas(0); + input.popLimit(oldLimit); + return rc; + } + + public T mergeUnframed(Buffer data) throws InvalidProtocolBufferException { + try { + CodedInputStream input = new CodedInputStream(data); + mergeUnframed(input); + input.checkLastTagWas(0); + return getThis(); + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException("An IOException was thrown (should never happen in this method).", e); + } + } + + @SuppressWarnings("unchecked") + private T getThis() { + return (T) this; + } + + public T mergeFramed(Buffer data) throws InvalidProtocolBufferException { + try { + CodedInputStream input = new CodedInputStream(data); + mergeFramed(input); + input.checkLastTagWas(0); + return getThis(); + } catch (InvalidProtocolBufferException e) { + throw e; + } catch (IOException e) { + throw new RuntimeException("An IOException was thrown (should never happen in this method).", e); + } + } + + public T mergeUnframed(byte[] data) throws InvalidProtocolBufferException { + return mergeUnframed(new Buffer(data)); + } + + public T mergeFramed(byte[] data) throws InvalidProtocolBufferException { + return mergeFramed(new Buffer(data)); + } + + public T mergeUnframed(InputStream input) throws IOException { + CodedInputStream codedInput = new CodedInputStream(input); + mergeUnframed(codedInput); + return getThis(); + } + + public T mergeFramed(InputStream input) throws IOException { + int length = readRawVarint32(input); + byte[] data = new byte[length]; + int pos = 0; + while (pos < length) { + int r = input.read(data, pos, length - pos); + if (r < 0) { + throw new InvalidProtocolBufferException("Input stream ended before a full message frame could be read."); + } + pos += r; + } + return mergeUnframed(data); + } + + // ///////////////////////////////////////////////////////////////// + // Internal implementation methods. + // ///////////////////////////////////////////////////////////////// + static protected void addAll(Iterable values, Collection list) { + if (values instanceof Collection) { + @SuppressWarnings("unsafe") + Collection collection = (Collection) values; + list.addAll(collection); + } else { + for (T value : values) { + list.add(value); + } + } + } + + static protected void writeGroup(CodedOutputStream output, int tag, BaseMessage message) throws IOException { + output.writeTag(tag, WIRETYPE_START_GROUP); + message.writeUnframed(output); + output.writeTag(tag, WIRETYPE_END_GROUP); + } + + static protected T readGroup(CodedInputStream input, int tag, T group) throws IOException { + group.mergeUnframed(input); + input.checkLastTagWas(makeTag(tag, WIRETYPE_END_GROUP)); + return group; + } + + static protected int computeGroupSize(int tag, BaseMessage message) { + return CodedOutputStream.computeTagSize(tag) * 2 + message.serializedSizeUnframed(); + } + + static protected void writeMessage(CodedOutputStream output, int tag, BaseMessage message) throws IOException { + output.writeTag(tag, WIRETYPE_LENGTH_DELIMITED); + message.writeFramed(output); + } + + static protected int computeMessageSize(int tag, BaseMessage message) { + return CodedOutputStream.computeTagSize(tag) + message.serializedSizeFramed(); + } + + protected List prefix(List missingFields, String prefix) { + ArrayList rc = new ArrayList(missingFields.size()); + for (String v : missingFields) { + rc.add(prefix + v); + } + return rc; + } + + /** + * Read a raw Varint from the stream. If larger than 32 bits, discard the + * upper bits. + */ + static public int readRawVarint32(InputStream is) throws IOException { + byte tmp = readRawByte(is); + if (tmp >= 0) { + return tmp; + } + int result = tmp & 0x7f; + if ((tmp = readRawByte(is)) >= 0) { + result |= tmp << 7; + } else { + result |= (tmp & 0x7f) << 7; + if ((tmp = readRawByte(is)) >= 0) { + result |= tmp << 14; + } else { + result |= (tmp & 0x7f) << 14; + if ((tmp = readRawByte(is)) >= 0) { + result |= tmp << 21; + } else { + result |= (tmp & 0x7f) << 21; + result |= (tmp = readRawByte(is)) << 28; + if (tmp < 0) { + // Discard upper 32 bits. + for (int i = 0; i < 5; i++) { + if (readRawByte(is) >= 0) + return result; + } + throw new InvalidProtocolBufferException("CodedInputStream encountered a malformed varint."); + } + } + } + } + return result; + } + + static protected byte readRawByte(InputStream is) throws IOException { + int rc = is.read(); + if (rc == -1) { + throw 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."); + } + return (byte) rc; + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/Buffer.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/Buffer.java new file mode 100644 index 0000000000..e274b0982c --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/Buffer.java @@ -0,0 +1,211 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.activemq.protobuf; + +import java.util.List; + +public class Buffer implements Comparable { + + final public byte[] data; + final public int offset; + final public int length; + + public Buffer(Buffer other) { + this(other.data, other.offset, other.length); + } + + public Buffer(byte data[]) { + this(data, 0, data.length); + } + + public Buffer(byte data[], int offset, int length) { + this.data = data; + this.offset = offset; + this.length = length; + } + + @Deprecated + public Buffer(String value) { + this(UTF8Buffer.encode(value)); + } + + public final Buffer slice(int low, int high) { + int sz; + + if (high < 0) { + sz = length + high; + } else { + sz = high - low; + } + + if (sz < 0) { + sz = 0; + } + + return new Buffer(data, offset + low, sz); + } + + public final byte[] getData() { + return data; + } + + public final int getLength() { + return length; + } + + public final int getOffset() { + return offset; + } + + public Buffer compact() { + if (length != data.length) { + return new Buffer(toByteArray()); + } + return this; + } + + final public byte[] toByteArray() { + byte[] data = this.data; + int length = this.length; + if (length != data.length) { + byte t[] = new byte[length]; + System.arraycopy(data, offset, t, 0, length); + data = t; + } + return data; + } + + public byte byteAt(int i) { + return data[offset + i]; + } + + + @Override + public int hashCode() { + byte[] target = new byte[4]; + for (int i = 0; i < length; i++) { + target[i % 4] ^= data[offset + i]; + } + return target[0] << 24 | target[1] << 16 | target[2] << 8 | target[3]; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + + if (obj == null || obj.getClass() != Buffer.class) + return false; + + return equals((Buffer) obj); + } + + final public boolean equals(Buffer obj) { + if (length != obj.length) { + return false; + } + for (int i = 0; i < length; i++) { + if (obj.data[obj.offset + i] != data[offset + i]) { + return false; + } + } + return true; + } + + final public BufferInputStream newInput() { + return new BufferInputStream(this); + } + + final public BufferOutputStream newOutput() { + return new BufferOutputStream(this); + } + + final public boolean isEmpty() { + return length == 0; + } + + final public boolean contains(byte value) { + return indexOf(value, 0) >= 0; + } + + final public int indexOf(byte value, int pos) { + for (int i = pos; i < length; i++) { + if (data[offset + i] == value) { + return i; + } + } + return -1; + } + + final public static Buffer join(List items, Buffer seperator) { + if (items.isEmpty()) + return new Buffer(seperator.data, 0, 0); + + int size = 0; + for (Buffer item : items) { + size += item.length; + } + size += seperator.length * (items.size() - 1); + + int pos = 0; + byte data[] = new byte[size]; + for (Buffer item : items) { + if (pos != 0) { + System.arraycopy(seperator.data, seperator.offset, data, pos, seperator.length); + pos += seperator.length; + } + System.arraycopy(item.data, item.offset, data, pos, item.length); + pos += item.length; + } + + return new Buffer(data, 0, size); + } + + @Deprecated + public String toStringUtf8() { + return UTF8Buffer.decode(this); + } + + public int compareTo(Buffer o) { + int minLength = Math.min(length, o.length); + if (offset == o.offset) { + int pos = offset; + int limit = minLength + offset; + while (pos < limit) { + byte b1 = data[pos]; + byte b2 = o.data[pos]; + if (b1 != b2) { + return b1 - b2; + } + pos++; + } + } else { + int offset1 = offset; + int offset2 = o.offset; + while ( minLength-- != 0) { + byte b1 = data[offset1++]; + byte b2 = o.data[offset2++]; + if (b1 != b2) { + return b1 - b2; + } + } + } + return length - o.length; + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/BufferInputStream.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/BufferInputStream.java new file mode 100644 index 0000000000..c13093ec8f --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/BufferInputStream.java @@ -0,0 +1,109 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Very similar to the java.io.ByteArrayInputStream but this version is not + * thread safe. + */ +final public class BufferInputStream extends InputStream { + + byte buffer[]; + int limit; + int pos; + int mark; + + public BufferInputStream(byte data[]) { + this(data, 0, data.length); + } + + public BufferInputStream(Buffer sequence) { + this(sequence.getData(), sequence.getOffset(), sequence.getLength()); + } + + public BufferInputStream(byte data[], int offset, int size) { + this.buffer = data; + this.mark = offset; + this.pos = offset; + this.limit = offset + size; + } + + public int read() throws IOException { + if (pos < limit) { + return buffer[pos++] & 0xff; + } else { + return -1; + } + } + + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + public int read(byte b[], int off, int len) { + if (pos < limit) { + len = Math.min(len, limit - pos); + System.arraycopy(buffer, pos, b, off, len); + pos += len; + return len; + } else { + return -1; + } + } + + public Buffer readBuffer(int len) { + Buffer rc=null; + if (pos < limit) { + len = Math.min(len, limit - pos); + rc = new Buffer(buffer, pos, len); + pos += len; + } + return rc; + } + + public long skip(long len) throws IOException { + if (pos < limit) { + len = Math.min(len, limit - pos); + if (len > 0) { + pos += len; + } + return len; + } else { + return -1; + } + } + + public int available() { + return limit - pos; + } + + public boolean markSupported() { + return true; + } + + public void mark(int markpos) { + mark = pos; + } + + public void reset() { + pos = mark; + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/BufferOutputStream.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/BufferOutputStream.java new file mode 100644 index 0000000000..d6b1d1816a --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/BufferOutputStream.java @@ -0,0 +1,101 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf; + +import java.io.EOFException; +import java.io.IOException; +import java.io.OutputStream; + + +/** + * Very similar to the java.io.ByteArrayOutputStream but this version + * is not thread safe and the resulting data is returned in a Buffer + * to avoid an extra byte[] allocation. It also does not re-grow it's + * internal buffer. + */ +final public class BufferOutputStream extends OutputStream { + + byte buffer[]; + int offset; + int limit; + int pos; + + public BufferOutputStream(int size) { + this(new byte[size]); + } + + public BufferOutputStream(byte[] buffer) { + this.buffer = buffer; + this.limit = buffer.length; + } + + public BufferOutputStream(Buffer data) { + this.buffer = data.data; + this.pos = this.offset = data.offset; + this.limit = data.offset+data.length; + } + + + public void write(int b) throws IOException { + int newPos = pos + 1; + checkCapacity(newPos); + buffer[pos] = (byte) b; + pos = newPos; + } + + public void write(byte b[], int off, int len) throws IOException { + int newPos = pos + len; + checkCapacity(newPos); + System.arraycopy(b, off, buffer, pos, len); + pos = newPos; + } + + public Buffer getNextBuffer(int len) throws IOException { + int newPos = pos + len; + checkCapacity(newPos); + return new Buffer(buffer, pos, len); + } + + /** + * Ensures the the buffer has at least the minimumCapacity specified. + * @param i + * @throws EOFException + */ + private void checkCapacity(int minimumCapacity) throws IOException { + if( minimumCapacity > limit ) { + throw new EOFException("Buffer limit reached."); + } + } + + public void reset() { + pos = offset; + } + + public Buffer toBuffer() { + return new Buffer(buffer, offset, pos); + } + + public byte[] toByteArray() { + return toBuffer().toByteArray(); + } + + public int size() { + return offset-pos; + } + + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/CodedInputStream.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/CodedInputStream.java new file mode 100644 index 0000000000..b8bb699253 --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/CodedInputStream.java @@ -0,0 +1,418 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.activemq.protobuf; + +import java.io.EOFException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * 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 extends FilterInputStream { + + private int lastTag = 0; + private int limit = Integer.MAX_VALUE; + private int pos; + private BufferInputStream bis; + + public CodedInputStream(InputStream in) { + super(in); + if( in.getClass() == BufferInputStream.class ) { + bis = (BufferInputStream)in; + } + } + + public CodedInputStream(Buffer data) { + this(new BufferInputStream(data)); + limit = data.length; + } + + public CodedInputStream(byte[] data) { + this(new BufferInputStream(data)); + limit = data.length; + } + + /** + * 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( pos >= limit ) { + lastTag=0; + return 0; + } + try { + lastTag = readRawVarint32(); + if (lastTag == 0) { + // If we actually read zero, that's not a valid tag. + throw InvalidProtocolBufferException.invalidTag(); + } + return lastTag; + } catch (EOFException e) { + lastTag=0; + return 0; + } + } + + + /** + * 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(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(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) { + 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 { + int size = readRawVarint32(); + Buffer data = readRawBytes(size); + return new String(data.data, data.offset, data.length, "UTF-8"); + } + + /** Read a {@code bytes} field value from the stream. */ + public Buffer readBytes() throws IOException { + int size = readRawVarint32(); + return 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; + } + + /** Read a raw Varint from the stream. */ + public long readRawVarint64() throws IOException { + int shift = 0; + long result = 0; + while (shift < 64) { + 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 { + byte b1 = readRawByte(); + byte b2 = readRawByte(); + byte b3 = readRawByte(); + 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 { + byte b1 = readRawByte(); + byte b2 = readRawByte(); + byte b3 = readRawByte(); + byte b4 = readRawByte(); + byte b5 = readRawByte(); + byte b6 = readRawByte(); + byte b7 = readRawByte(); + 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(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(long n) { + return (n >>> 1) ^ -(n & 1); + } + + /** + * 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( pos >= limit ) { + throw new EOFException(); + } + int rc = in.read(); + if( rc < 0 ) { + throw new EOFException(); + } + pos++; + return (byte)( rc & 0xFF); + } + + /** + * Read a fixed size of bytes from the input. + * + * @throws InvalidProtocolBufferException + * The end of the stream or the current limit was reached. + */ + public Buffer readRawBytes(int size) throws IOException { + if( size == 0) { + return new Buffer(new byte[]{}); + } + if( this.pos+size > limit ) { + throw new EOFException(); + } + + // If the underlying stream is a ByteArrayInputStream + // then we can avoid an array copy. + if( bis!=null ) { + Buffer rc = bis.readBuffer(size); + if( rc==null || rc.getLength() < size ) { + throw new EOFException(); + } + this.pos += rc.getLength(); + return rc; + } + + // Otherwise we, have to do it the old fasioned way + byte[] rc = new byte[size]; + int c; + int pos=0; + while( pos < size ) { + c = in.read(rc, pos, size-pos); + if( c < 0 ) { + throw new EOFException(); + } + this.pos += c; + pos += c; + } + + return new Buffer(rc); + } + + /** + * Reads and discards {@code size} bytes. + * + * @throws InvalidProtocolBufferException + * The end of the stream or the current limit was reached. + */ + public void skipRawBytes(int size) throws IOException { + int pos = 0; + while (pos < size) { + int n = (int) in.skip(size - pos); + pos += n; + } + } + + public int pushLimit(int limit) { + int rc = this.limit; + this.limit = pos+limit; + return rc; + } + + public void popLimit(int limit) { + this.limit = limit; + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/CodedOutputStream.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/CodedOutputStream.java new file mode 100644 index 0000000000..f705c83bbe --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/CodedOutputStream.java @@ -0,0 +1,481 @@ +//Protocol Buffers - Google's data interchange format +//Copyright 2008 Google Inc. +//http://code.google.com/p/protobuf/ +// +//Licensed under the Apache License, Version 2.0 (the "License"); +//you may not use this file except in compliance with the License. +//You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +//Unless required by applicable law or agreed to in writing, software +//distributed under the License is distributed on an "AS IS" BASIS, +//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//See the License for the specific language governing permissions and +//limitations under the License. +package org.apache.activemq.protobuf; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * Encodes and writes protocol message fields. + * + *

+ * This class contains two kinds of methods: methods that write specific + * protocol message constructs and field types (e.g. {@link #writeTag} and + * {@link #writeInt32}) and methods that write low-level values (e.g. + * {@link #writeRawVarint32} and {@link #writeRawBytes}). If you are writing + * encoded protocol messages, you should use the former methods, but if you are + * writing some other format of your own design, use the latter. + * + *

+ * This class is totally unsynchronized. + * + * @author kneton@google.com Kenton Varda + */ +public final class CodedOutputStream extends FilterOutputStream { + + private BufferOutputStream bos; + + public CodedOutputStream(OutputStream os) { + super(os); + if( os instanceof BufferOutputStream ) { + bos = (BufferOutputStream)os; + } + } + + public CodedOutputStream(byte[] data) { + super(new BufferOutputStream(data)); + } + + public CodedOutputStream(Buffer data) { + super(new BufferOutputStream(data)); + } + + // ----------------------------------------------------------------- + + /** Write a {@code double} field, including tag, to the stream. */ + public void writeDouble(int fieldNumber, double value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + writeRawLittleEndian64(Double.doubleToRawLongBits(value)); + } + + /** Write a {@code float} field, including tag, to the stream. */ + public void writeFloat(int fieldNumber, float value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + writeRawLittleEndian32(Float.floatToRawIntBits(value)); + } + + /** Write a {@code uint64} field, including tag, to the stream. */ + public void writeUInt64(int fieldNumber, long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint64(value); + } + + /** Write an {@code int64} field, including tag, to the stream. */ + public void writeInt64(int fieldNumber, long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint64(value); + } + + /** Write an {@code int32} field, including tag, to the stream. */ + public void writeInt32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + if (value >= 0) { + writeRawVarint32(value); + } else { + // Must sign-extend. + writeRawVarint64(value); + } + } + + /** Write a {@code fixed64} field, including tag, to the stream. */ + public void writeFixed64(int fieldNumber, long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + writeRawLittleEndian64(value); + } + + /** Write a {@code fixed32} field, including tag, to the stream. */ + public void writeFixed32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + writeRawLittleEndian32(value); + } + + /** Write a {@code bool} field, including tag, to the stream. */ + public void writeBool(int fieldNumber, boolean value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawByte(value ? 1 : 0); + } + + /** Write a {@code string} field, including tag, to the stream. */ + public void writeString(int fieldNumber, String value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + // Unfortunately there does not appear to be any way to tell Java to + // encode + // UTF-8 directly into our buffer, so we have to let it create its own + // byte + // array and then copy. + byte[] bytes = value.getBytes("UTF-8"); + writeRawVarint32(bytes.length); + writeRawBytes(bytes); + } + + /** Write a {@code bytes} field, including tag, to the stream. */ + public void writeBytes(int fieldNumber, Buffer value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_LENGTH_DELIMITED); + writeRawVarint32(value.length); + writeRawBytes(value.data, value.offset, value.length); + } + + /** Write a {@code uint32} field, including tag, to the stream. */ + public void writeUInt32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint32(value); + } + + /** + * Write an enum field, including tag, to the stream. Caller is responsible + * for converting the enum value to its numeric value. + */ + public void writeEnum(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint32(value); + } + + /** Write an {@code sfixed32} field, including tag, to the stream. */ + public void writeSFixed32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED32); + writeRawLittleEndian32(value); + } + + /** Write an {@code sfixed64} field, including tag, to the stream. */ + public void writeSFixed64(int fieldNumber, long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_FIXED64); + writeRawLittleEndian64(value); + } + + /** Write an {@code sint32} field, including tag, to the stream. */ + public void writeSInt32(int fieldNumber, int value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint32(encodeZigZag32(value)); + } + + /** Write an {@code sint64} field, including tag, to the stream. */ + public void writeSInt64(int fieldNumber, long value) throws IOException { + writeTag(fieldNumber, WireFormat.WIRETYPE_VARINT); + writeRawVarint64(encodeZigZag64(value)); + } + + // ================================================================= + + /** + * Compute the number of bytes that would be needed to encode a {@code + * double} field, including tag. + */ + public static int computeDoubleSize(int fieldNumber, double value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a {@code + * float} field, including tag. + */ + public static int computeFloatSize(int fieldNumber, float value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a {@code + * uint64} field, including tag. + */ + public static int computeUInt64Size(int fieldNumber, long value) { + return computeTagSize(fieldNumber) + computeRawVarint64Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an {@code + * int64} field, including tag. + */ + public static int computeInt64Size(int fieldNumber, long value) { + return computeTagSize(fieldNumber) + computeRawVarint64Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an {@code + * int32} field, including tag. + */ + public static int computeInt32Size(int fieldNumber, int value) { + if (value >= 0) { + return computeTagSize(fieldNumber) + computeRawVarint32Size(value); + } else { + // Must sign-extend. + return computeTagSize(fieldNumber) + 10; + } + } + + /** + * Compute the number of bytes that would be needed to encode a {@code + * fixed64} field, including tag. + */ + public static int computeFixed64Size(int fieldNumber, long value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a {@code + * fixed32} field, including tag. + */ + public static int computeFixed32Size(int fieldNumber, int value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode a {@code bool} + * field, including tag. + */ + public static int computeBoolSize(int fieldNumber, boolean value) { + return computeTagSize(fieldNumber) + 1; + } + + /** + * Compute the number of bytes that would be needed to encode a {@code + * string} field, including tag. + */ + public static int computeStringSize(int fieldNumber, String value) { + try { + byte[] bytes = value.getBytes("UTF-8"); + return computeTagSize(fieldNumber) + computeRawVarint32Size(bytes.length) + bytes.length; + } catch (java.io.UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 not supported.", e); + } + } + + /** + * Compute the number of bytes that would be needed to encode a {@code + * bytes} field, including tag. + */ + public static int computeBytesSize(int fieldNumber, Buffer value) { + return computeTagSize(fieldNumber) + computeRawVarint32Size(value.length) + value.length; + } + + /** + * Compute the number of bytes that would be needed to encode a {@code + * uint32} field, including tag. + */ + public static int computeUInt32Size(int fieldNumber, int value) { + return computeTagSize(fieldNumber) + computeRawVarint32Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an enum field, + * including tag. Caller is responsible for converting the enum value to its + * numeric value. + */ + public static int computeEnumSize(int fieldNumber, int value) { + return computeTagSize(fieldNumber) + computeRawVarint32Size(value); + } + + /** + * Compute the number of bytes that would be needed to encode an {@code + * sfixed32} field, including tag. + */ + public static int computeSFixed32Size(int fieldNumber, int value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_32_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode an {@code + * sfixed64} field, including tag. + */ + public static int computeSFixed64Size(int fieldNumber, long value) { + return computeTagSize(fieldNumber) + LITTLE_ENDIAN_64_SIZE; + } + + /** + * Compute the number of bytes that would be needed to encode an {@code + * sint32} field, including tag. + */ + public static int computeSInt32Size(int fieldNumber, int value) { + return computeTagSize(fieldNumber) + computeRawVarint32Size(encodeZigZag32(value)); + } + + /** + * Compute the number of bytes that would be needed to encode an {@code + * sint64} field, including tag. + */ + public static int computeSInt64Size(int fieldNumber, long value) { + return computeTagSize(fieldNumber) + computeRawVarint64Size(encodeZigZag64(value)); + } + + /** Write a single byte. */ + public void writeRawByte(byte value) throws IOException { + out.write(value); + } + + /** Write a single byte, represented by an integer value. */ + public void writeRawByte(int value) throws IOException { + writeRawByte((byte) value); + } + + /** Write an array of bytes. */ + public void writeRawBytes(byte[] value) throws IOException { + writeRawBytes(value, 0, value.length); + } + + /** Write part of an array of bytes. */ + public void writeRawBytes(byte[] value, int offset, int length) throws IOException { + out.write(value, offset, length); + } + + public void writeRawBytes(Buffer data) throws IOException { + out.write(data.data, data.offset, data.length); + } + + /** Encode and write a tag. */ + public void writeTag(int fieldNumber, int wireType) throws IOException { + writeRawVarint32(WireFormat.makeTag(fieldNumber, wireType)); + } + + /** Compute the number of bytes that would be needed to encode a tag. */ + public static int computeTagSize(int fieldNumber) { + return computeRawVarint32Size(WireFormat.makeTag(fieldNumber, 0)); + } + + /** + * Encode and write a varint. {@code value} is treated as unsigned, so it + * won't be sign-extended if negative. + */ + public void writeRawVarint32(int value) throws IOException { + while (true) { + if ((value & ~0x7F) == 0) { + writeRawByte(value); + return; + } else { + writeRawByte((value & 0x7F) | 0x80); + value >>>= 7; + } + } + } + + /** + * Compute the number of bytes that would be needed to encode a varint. + * {@code value} is treated as unsigned, so it won't be sign-extended if + * negative. + */ + public static int computeRawVarint32Size(int value) { + if ((value & (0xffffffff << 7)) == 0) + return 1; + if ((value & (0xffffffff << 14)) == 0) + return 2; + if ((value & (0xffffffff << 21)) == 0) + return 3; + if ((value & (0xffffffff << 28)) == 0) + return 4; + return 5; + } + + /** Encode and write a varint. */ + public void writeRawVarint64(long value) throws IOException { + while (true) { + if ((value & ~0x7FL) == 0) { + writeRawByte((int) value); + return; + } else { + writeRawByte(((int) value & 0x7F) | 0x80); + value >>>= 7; + } + } + } + + /** Compute the number of bytes that would be needed to encode a varint. */ + public static int computeRawVarint64Size(long value) { + if ((value & (0xffffffffffffffffL << 7)) == 0) + return 1; + if ((value & (0xffffffffffffffffL << 14)) == 0) + return 2; + if ((value & (0xffffffffffffffffL << 21)) == 0) + return 3; + if ((value & (0xffffffffffffffffL << 28)) == 0) + return 4; + if ((value & (0xffffffffffffffffL << 35)) == 0) + return 5; + if ((value & (0xffffffffffffffffL << 42)) == 0) + return 6; + if ((value & (0xffffffffffffffffL << 49)) == 0) + return 7; + if ((value & (0xffffffffffffffffL << 56)) == 0) + return 8; + if ((value & (0xffffffffffffffffL << 63)) == 0) + return 9; + return 10; + } + + /** Write a little-endian 32-bit integer. */ + public void writeRawLittleEndian32(int value) throws IOException { + writeRawByte((value) & 0xFF); + writeRawByte((value >> 8) & 0xFF); + writeRawByte((value >> 16) & 0xFF); + writeRawByte((value >> 24) & 0xFF); + } + + public static final int LITTLE_ENDIAN_32_SIZE = 4; + + /** Write a little-endian 64-bit integer. */ + public void writeRawLittleEndian64(long value) throws IOException { + writeRawByte((int) (value) & 0xFF); + writeRawByte((int) (value >> 8) & 0xFF); + writeRawByte((int) (value >> 16) & 0xFF); + writeRawByte((int) (value >> 24) & 0xFF); + writeRawByte((int) (value >> 32) & 0xFF); + writeRawByte((int) (value >> 40) & 0xFF); + writeRawByte((int) (value >> 48) & 0xFF); + writeRawByte((int) (value >> 56) & 0xFF); + } + + public static final int LITTLE_ENDIAN_64_SIZE = 8; + + /** + * Encode 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 + * A signed 32-bit integer. + * @return An unsigned 32-bit integer, stored in a signed int because Java + * has no explicit unsigned support. + */ + public static int encodeZigZag32(int n) { + // Note: the right-shift must be arithmetic + return (n << 1) ^ (n >> 31); + } + + /** + * Encode 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 + * A signed 64-bit integer. + * @return An unsigned 64-bit integer, stored in a signed int because Java + * has no explicit unsigned support. + */ + public static long encodeZigZag64(long n) { + // Note: the right-shift must be arithmetic + return (n << 1) ^ (n >> 63); + } + + public void checkNoSpaceLeft() { + } + + public Buffer getNextBuffer(int size) throws IOException { + if( bos==null ) { + return null; + } + return bos.getNextBuffer(size); + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/DeferredDecodeMessage.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/DeferredDecodeMessage.java new file mode 100644 index 0000000000..2815d98701 --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/DeferredDecodeMessage.java @@ -0,0 +1,90 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf; + +import java.io.IOException; + +abstract public class DeferredDecodeMessage extends BaseMessage { + + protected Buffer encodedForm; + protected boolean decoded = true; + + @Override + public T mergeFramed(CodedInputStream input) throws IOException { + int length = input.readRawVarint32(); + int oldLimit = input.pushLimit(length); + T rc = mergeUnframed(input.readRawBytes(length)); + input.popLimit(oldLimit); + return rc; + } + + @SuppressWarnings("unchecked") + @Override + public T mergeUnframed(Buffer data) throws InvalidProtocolBufferException { + encodedForm = data; + decoded = false; + return (T) this; + } + + @Override + public Buffer toUnframedBuffer() { + if (encodedForm == null) { + encodedForm = super.toUnframedBuffer(); + } + return encodedForm; + } + + protected void load() { + if (!decoded) { + decoded = true; + try { + Buffer originalForm = encodedForm; + encodedForm=null; + CodedInputStream input = new CodedInputStream(originalForm); + mergeUnframed(input); + input.checkLastTagWas(0); + // We need to reset the encoded form because the mergeUnframed + // from a stream clears it out. + encodedForm = originalForm; + checktInitialized(); + } catch (Throwable e) { + throw new RuntimeException("Deferred message decoding failed: " + e.getMessage(), e); + } + } + } + + protected void loadAndClear() { + super.loadAndClear(); + load(); + encodedForm = null; + } + + public void clear() { + super.clear(); + encodedForm = null; + decoded = true; + } + + public boolean isDecoded() { + return decoded; + } + + public boolean isEncoded() { + return encodedForm != null; + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/InvalidProtocolBufferException.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/InvalidProtocolBufferException.java new file mode 100644 index 0000000000..f6246056d0 --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/InvalidProtocolBufferException.java @@ -0,0 +1,66 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.activemq.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 = 5685337441004132240L; + + public InvalidProtocolBufferException(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."); + } +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/Message.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/Message.java new file mode 100644 index 0000000000..9a6fbfddc3 --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/Message.java @@ -0,0 +1,72 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public interface Message { + + public T clone() throws CloneNotSupportedException; + + public int serializedSizeUnframed(); + + public int serializedSizeFramed(); + + public void clear(); + + public T assertInitialized() throws UninitializedMessageException; + + public T mergeFrom(T other); + + + public T mergeUnframed(byte[] data) throws InvalidProtocolBufferException; + + public T mergeFramed(byte[] data) throws InvalidProtocolBufferException; + + public T mergeUnframed(Buffer buffer) throws InvalidProtocolBufferException; + + public T mergeFramed(Buffer buffer) throws InvalidProtocolBufferException; + + public T mergeUnframed(InputStream input) throws IOException; + + public T mergeFramed(InputStream input) throws IOException; + + public T mergeUnframed(CodedInputStream input) throws IOException; + + public T mergeFramed(CodedInputStream input) throws IOException; + + + public Buffer toUnframedBuffer(); + + public Buffer toFramedBuffer(); + + public byte[] toUnframedByteArray(); + + public byte[] toFramedByteArray(); + + public void writeUnframed(CodedOutputStream output) throws java.io.IOException; + + public void writeFramed(CodedOutputStream output) throws java.io.IOException; + + public void writeUnframed(OutputStream output) throws IOException; + + public void writeFramed(OutputStream output) throws java.io.IOException; + + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/MessageBuffer.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/MessageBuffer.java new file mode 100644 index 0000000000..d95700d749 --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/MessageBuffer.java @@ -0,0 +1,44 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf; + +import java.io.IOException; +import java.io.OutputStream; + +public interface MessageBuffer extends PBMessage { + + public int serializedSizeUnframed(); + + public int serializedSizeFramed(); + + public Buffer toUnframedBuffer(); + + public Buffer toFramedBuffer(); + + public byte[] toUnframedByteArray(); + + public byte[] toFramedByteArray(); + + public void writeUnframed(CodedOutputStream output) throws java.io.IOException; + + public void writeFramed(CodedOutputStream output) throws java.io.IOException; + + public void writeUnframed(OutputStream output) throws IOException; + + public void writeFramed(OutputStream output) throws java.io.IOException; + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/MessageBufferSupport.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/MessageBufferSupport.java new file mode 100644 index 0000000000..f49a0e1cd5 --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/MessageBufferSupport.java @@ -0,0 +1,140 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf; + +import static org.apache.activemq.protobuf.WireFormat.WIRETYPE_LENGTH_DELIMITED; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collection; + + +final public class MessageBufferSupport { + + public static final String FORZEN_ERROR_MESSAGE = "Modification not allowed after object has been fozen. Try modifying a copy of this object."; + + static public Buffer toUnframedBuffer(MessageBuffer message) { + try { + int size = message.serializedSizeUnframed(); + BufferOutputStream baos = new BufferOutputStream(size); + CodedOutputStream output = new CodedOutputStream(baos); + message.writeUnframed(output); + Buffer rc = baos.toBuffer(); + assert rc.length == size : "Did not write as much data as expected."; + return rc; + } catch (IOException e) { + throw new RuntimeException("Serializing to a byte array threw an IOException " + "(should never happen).", e); + } + } + + static public Buffer toFramedBuffer(MessageBuffer message) { + try { + int size = message.serializedSizeFramed(); + BufferOutputStream baos = new BufferOutputStream(size); + CodedOutputStream output = new CodedOutputStream(baos); + message.writeFramed(output); + Buffer rc = baos.toBuffer(); + assert rc.length==size : "Did not write as much data as expected."; + return rc; + } catch (IOException e) { + throw new RuntimeException("Serializing to a byte array threw an IOException " + "(should never happen).", e); + } + } + + public static void writeMessage(CodedOutputStream output, int tag, MessageBuffer message) throws IOException { + output.writeTag(tag, WIRETYPE_LENGTH_DELIMITED); + message.writeFramed(output); + } + + public static int computeMessageSize(int tag, MessageBuffer message) { + return CodedOutputStream.computeTagSize(tag) + message.serializedSizeFramed(); + } + + public static Buffer readFrame(java.io.InputStream input) throws IOException { + int length = readRawVarint32(input); + byte[] data = new byte[length]; + int pos = 0; + while (pos < length) { + int r = input.read(data, pos, length - pos); + if (r < 0) { + throw new InvalidProtocolBufferException("Input stream ended before a full message frame could be read."); + } + pos += r; + } + return new Buffer(data); + } + + /** + * Read a raw Varint from the stream. If larger than 32 bits, discard the + * upper bits. + */ + static public int readRawVarint32(InputStream is) throws IOException { + byte tmp = readRawByte(is); + if (tmp >= 0) { + return tmp; + } + int result = tmp & 0x7f; + if ((tmp = readRawByte(is)) >= 0) { + result |= tmp << 7; + } else { + result |= (tmp & 0x7f) << 7; + if ((tmp = readRawByte(is)) >= 0) { + result |= tmp << 14; + } else { + result |= (tmp & 0x7f) << 14; + if ((tmp = readRawByte(is)) >= 0) { + result |= tmp << 21; + } else { + result |= (tmp & 0x7f) << 21; + result |= (tmp = readRawByte(is)) << 28; + if (tmp < 0) { + // Discard upper 32 bits. + for (int i = 0; i < 5; i++) { + if (readRawByte(is) >= 0) + return result; + } + throw new InvalidProtocolBufferException("CodedInputStream encountered a malformed varint."); + } + } + } + } + return result; + } + + static public byte readRawByte(InputStream is) throws IOException { + int rc = is.read(); + if (rc == -1) { + throw 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."); + } + return (byte) rc; + } + + static public void addAll(Iterable values, Collection list) { + if (values instanceof Collection) { + @SuppressWarnings("unsafe") + Collection collection = (Collection) values; + list.addAll(collection); + } else { + for (T value : values) { + list.add(value); + } + } + } + + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/PBMessage.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/PBMessage.java new file mode 100644 index 0000000000..6996f67eec --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/PBMessage.java @@ -0,0 +1,23 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf; + +public interface PBMessage { + public Bean copy(); + public boolean frozen(); + public Buffer freeze(); +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/UTF8Buffer.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/UTF8Buffer.java new file mode 100644 index 0000000000..62c0deeb0b --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/UTF8Buffer.java @@ -0,0 +1,101 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf; + +import java.io.UnsupportedEncodingException; + +final public class UTF8Buffer extends Buffer { + + int hashCode; + String value; + + public UTF8Buffer(Buffer other) { + super(other); + } + + public UTF8Buffer(byte[] data, int offset, int length) { + super(data, offset, length); + } + + public UTF8Buffer(byte[] data) { + super(data); + } + + public UTF8Buffer(String input) { + super(encode(input)); + } + + public UTF8Buffer compact() { + if (length != data.length) { + return new UTF8Buffer(toByteArray()); + } + return this; + } + + public String toString() + { + if( value==null ) { + value = decode(this); + } + return value; + } + + @Override + public int compareTo(Buffer other) { + // Do a char comparison.. not a byte for byte comparison. + return toString().compareTo(other.toString()); + } + + @Override + public boolean equals(Object obj) { + if( obj==this ) + return true; + + if( obj==null || obj.getClass()!=UTF8Buffer.class ) + return false; + + return equals((Buffer)obj); + } + + @Override + public int hashCode() { + if( hashCode==0 ) { + hashCode = super.hashCode();; + } + return hashCode; + } + + static public byte[] encode(String value) + { + try { + return value.getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("A UnsupportedEncodingException was thrown for teh UTF-8 encoding. (This should never happen)"); + } + } + + static public String decode(Buffer buffer) + { + try { + return new String(buffer.getData(), buffer.getOffset(), buffer.getLength(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("A UnsupportedEncodingException was thrown for teh UTF-8 encoding. (This should never happen)"); + } + } + + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/UninitializedMessageException.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/UninitializedMessageException.java new file mode 100644 index 0000000000..3e17dae0f2 --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/UninitializedMessageException.java @@ -0,0 +1,76 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.activemq.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 do not 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 { + + public UninitializedMessageException(List missingFields) { + super(buildDescription(missingFields)); + this.missingFields = missingFields; + } + + private final List 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". + */ + public List 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(List missingFields) { + StringBuilder description = new StringBuilder("Message missing required fields: "); + boolean first = true; + for (String field : missingFields) { + if (first) { + first = false; + } else { + description.append(", "); + } + description.append(field); + } + return description.toString(); + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/WireFormat.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/WireFormat.java new file mode 100644 index 0000000000..3018bb778a --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/WireFormat.java @@ -0,0 +1,70 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.activemq.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 protocol2} 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() { + } + + public static final int WIRETYPE_VARINT = 0; + public static final int WIRETYPE_FIXED64 = 1; + public static final int WIRETYPE_LENGTH_DELIMITED = 2; + public static final int WIRETYPE_START_GROUP = 3; + public static final int WIRETYPE_END_GROUP = 4; + public static final int WIRETYPE_FIXED32 = 5; + + public static final int TAG_TYPE_BITS = 3; + public 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(int tag) { + return tag & TAG_TYPE_MASK; + } + + /** Given a tag value, determines the field number (the upper 29 bits). */ + public static int getTagFieldNumber(int tag) { + return tag >>> TAG_TYPE_BITS; + } + + /** Makes a tag value given a field number and wire type. */ + public static int makeTag(int fieldNumber, int wireType) { + return (fieldNumber << TAG_TYPE_BITS) | wireType; + } + + // Field numbers for feilds in MessageSet wire format. + public static final int MESSAGE_SET_ITEM = 1; + public static final int MESSAGE_SET_TYPE_ID = 2; + public static final int MESSAGE_SET_MESSAGE = 3; + + // Tag numbers. + public static final int MESSAGE_SET_ITEM_TAG = makeTag(MESSAGE_SET_ITEM, WIRETYPE_START_GROUP); + public static final int MESSAGE_SET_ITEM_END_TAG = makeTag(MESSAGE_SET_ITEM, WIRETYPE_END_GROUP); + public static final int MESSAGE_SET_TYPE_ID_TAG = makeTag(MESSAGE_SET_TYPE_ID, WIRETYPE_VARINT); + public static final int MESSAGE_SET_MESSAGE_TAG = makeTag(MESSAGE_SET_MESSAGE, WIRETYPE_LENGTH_DELIMITED); +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/AltJavaGenerator.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/AltJavaGenerator.java new file mode 100644 index 0000000000..8bba8bc98f --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/AltJavaGenerator.java @@ -0,0 +1,2405 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler; + +import static org.apache.activemq.protobuf.WireFormat.WIRETYPE_FIXED32; +import static org.apache.activemq.protobuf.WireFormat.WIRETYPE_FIXED64; +import static org.apache.activemq.protobuf.WireFormat.WIRETYPE_LENGTH_DELIMITED; +import static org.apache.activemq.protobuf.WireFormat.WIRETYPE_VARINT; +import static org.apache.activemq.protobuf.WireFormat.makeTag; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.StringTokenizer; + +import org.apache.activemq.protobuf.compiler.parser.ParseException; +import org.apache.activemq.protobuf.compiler.parser.ProtoParser; + +public class AltJavaGenerator { + + private File out = new File("."); + private File[] path = new File[]{new File(".")}; + + private ProtoDescriptor proto; + private String javaPackage; + private String outerClassName; + private PrintWriter w; + private int indent; + private ArrayList errors = new ArrayList(); + private boolean multipleFiles; + private boolean auto_clear_optional_fields; + + public static void main(String[] args) { + + AltJavaGenerator generator = new AltJavaGenerator(); + args = CommandLineSupport.setOptions(generator, args); + + if (args.length == 0) { + System.out.println("No proto files specified."); + } + for (int i = 0; i < args.length; i++) { + try { + System.out.println("Compiling: "+args[i]); + generator.compile(new File(args[i])); + } catch (CompilerException e) { + System.out.println("Protocol Buffer Compiler failed with the following error(s):"); + for (String error : e.getErrors() ) { + System.out.println(""); + System.out.println(error); + } + System.out.println(""); + System.out.println("Compile failed. For more details see error messages listed above."); + return; + } + } + + } + + interface Closure { + void execute() throws CompilerException; + } + + public void compile(File file) throws CompilerException { + + // Parse the proto file + FileInputStream is=null; + try { + is = new FileInputStream(file); + ProtoParser parser = new ProtoParser(is); + proto = parser.ProtoDescriptor(); + proto.setName(file.getName()); + loadImports(proto, file.getParentFile()); + proto.validate(errors); + } catch (FileNotFoundException e) { + errors.add("Failed to open: "+file.getPath()+":"+e.getMessage()); + } catch (ParseException e) { + errors.add("Failed to parse: "+file.getPath()+":"+e.getMessage()); + } finally { + try { is.close(); } catch (Throwable ignore){} + } + + if (!errors.isEmpty()) { + throw new CompilerException(errors); + } + + // Load the options.. + javaPackage = javaPackage(proto); + outerClassName = javaClassName(proto); +// optimizeFor = getOption(proto.getOptions(), "optimize_for", "SPEED"); + multipleFiles = isMultipleFilesEnabled(proto); +// deferredDecode = Boolean.parseBoolean(getOption(proto.getOptions(), "deferred_decode", "false")); + auto_clear_optional_fields = Boolean.parseBoolean(getOption(proto.getOptions(), "auto_clear_optional_fields", "false")); + + if( multipleFiles ) { + generateProtoFile(); + } else { + writeFile(outerClassName, new Closure(){ + public void execute() throws CompilerException { + generateProtoFile(); + } + }); + } + + if (!errors.isEmpty()) { + throw new CompilerException(errors); + } + + } + + private void writeFile(String className, Closure closure) throws CompilerException { + PrintWriter oldWriter = w; + // Figure out the java file name.. + File outputFile = out; + if (javaPackage != null) { + String packagePath = javaPackage.replace('.', '/'); + outputFile = new File(outputFile, packagePath); + } + outputFile = new File(outputFile, className + ".java"); + + // Start writing the output file.. + outputFile.getParentFile().mkdirs(); + + FileOutputStream fos=null; + try { + fos = new FileOutputStream(outputFile); + w = new PrintWriter(fos); + closure.execute(); + w.flush(); + } catch (FileNotFoundException e) { + errors.add("Failed to write to: "+outputFile.getPath()+":"+e.getMessage()); + } finally { + try { fos.close(); } catch (Throwable ignore){} + w = oldWriter; + } + } + + private void loadImports(ProtoDescriptor proto, File protoDir) { + LinkedHashMap children = new LinkedHashMap(); + for (String imp : proto.getImports()) { + File file = new File(protoDir, imp); + for (int i = 0; i < path.length && !file.exists(); i++) { + file = new File(path[i], imp); + } + if ( !file.exists() ) { + errors.add("Cannot load import: "+imp); + } + + FileInputStream is=null; + try { + is = new FileInputStream(file); + ProtoParser parser = new ProtoParser(is); + ProtoDescriptor child = parser.ProtoDescriptor(); + child.setName(file.getName()); + loadImports(child, file.getParentFile()); + children.put(imp, child); + } catch (ParseException e) { + errors.add("Failed to parse: "+file.getPath()+":"+e.getMessage()); + } catch (FileNotFoundException e) { + errors.add("Failed to open: "+file.getPath()+":"+e.getMessage()); + } finally { + try { is.close(); } catch (Throwable ignore){} + } + } + proto.setImportProtoDescriptors(children); + } + + + private void generateProtoFile() throws CompilerException { + if( multipleFiles ) { + for (EnumDescriptor value : proto.getEnums().values()) { + final EnumDescriptor o = value; + String className = uCamel(o.getName()); + writeFile(className, new Closure(){ + public void execute() throws CompilerException { + generateFileHeader(); + generateEnum(o); + } + }); + } + for (MessageDescriptor value : proto.getMessages().values()) { + final MessageDescriptor o = value; + String className = uCamel(o.getName()); + writeFile(className, new Closure(){ + public void execute() throws CompilerException { + generateFileHeader(); + generateMessageBean(o); + } + }); + } + + } else { + generateFileHeader(); + + p("public class " + outerClassName + " {"); + indent(); + + for (EnumDescriptor enumType : proto.getEnums().values()) { + generateEnum(enumType); + } + for (MessageDescriptor m : proto.getMessages().values()) { + generateMessageBean(m); + } + + unindent(); + p("}"); + } + } + + private void generateFileHeader() { + p("//"); + p("// Generated by protoc, do not edit by hand."); + p("//"); + if (javaPackage != null) { + p("package " + javaPackage + ";"); + p(""); + } + } + + private void generateMessageBean(MessageDescriptor m) { + + String className = uCamel(m.getName()); + String beanClassName = className+"Bean"; + String bufferClassName = className+"Buffer"; + p(); + + String staticOption = "static "; + if( multipleFiles && m.getParent()==null ) { + staticOption=""; + } + + String extendsClause = " extends org.apache.activemq.protobuf.PBMessage<"+className+"."+beanClassName+", "+className+"."+bufferClassName+">"; + for (EnumFieldDescriptor enumFeild : m.getAssociatedEnumFieldDescriptors()) { + String name = uCamel(enumFeild.getParent().getName()); + name = name+"."+name+"Creatable"; + extendsClause += ", "+name; + } + + p(staticOption+"public interface " + className + extendsClause +" {"); + p(); + indent(); + + for (EnumDescriptor enumType : m.getEnums().values()) { + generateEnum(enumType); + } + + // Generate the Nested Messages. + for (MessageDescriptor subMessage : m.getMessages().values()) { + generateMessageBean(subMessage); + } + + // Generate the Group Messages + for (FieldDescriptor field : m.getFields().values()) { + if( field.isGroup() ) { + generateMessageBean(field.getGroup()); + } + } + + // Generate the field getters + for (FieldDescriptor field : m.getFields().values()) { + generateFieldGetterSignatures(field); + } + + p("public "+beanClassName+" copy();"); + p("public "+bufferClassName+" freeze();"); + p("public java.lang.StringBuilder toString(java.lang.StringBuilder sb, String prefix);"); + + + p(); + p("static public final class "+beanClassName+" implements "+className+" {"); + p(); + indent(); + + p(""+bufferClassName+" frozen;"); + p(""+beanClassName+" bean;"); + p(); + p("public "+beanClassName+"() {"); + indent(); + p("this.bean = this;"); + unindent(); + p("}"); + p(); + p("public "+beanClassName+"("+beanClassName+" copy) {"); + indent(); + p("this.bean = copy;"); + unindent(); + p("}"); + p(); + p("public "+beanClassName+" copy() {"); + indent(); + p("return new "+beanClassName+"(bean);"); + unindent(); + p("}"); + p(); + + generateMethodFreeze(m, bufferClassName); + + p("private void copyCheck() {"); + indent(); + p("assert frozen==null : org.apache.activemq.protobuf.MessageBufferSupport.FORZEN_ERROR_MESSAGE;"); + p("if (bean != this) {"); + indent(); + p("copy(bean);"); + unindent(); + p("}"); + unindent(); + p("}"); + p(); + + generateMethodCopyFromBean(m, beanClassName); + + // Generate the field accessors.. + for (FieldDescriptor field : m.getFields().values()) { + generateFieldAccessor(beanClassName, field); + } + + generateMethodToString(m); + + generateMethodMergeFromStream(m, beanClassName); + + generateBeanEquals(m, beanClassName); + + generateMethodMergeFromBean(m, className); + + generateMethodClear(m); + + generateReadWriteExternal(m); + + for (EnumFieldDescriptor enumFeild : m.getAssociatedEnumFieldDescriptors()) { + String enumName = uCamel(enumFeild.getParent().getName()); + p("public "+enumName+" to"+enumName+"() {"); + indent(); + p("return "+enumName+"."+enumFeild.getName()+";"); + unindent(); + p("}"); + p(); + } + + unindent(); + p("}"); + p(); + + p("static public final class "+bufferClassName+" implements org.apache.activemq.protobuf.MessageBuffer<"+className+"."+beanClassName+", "+className+"."+bufferClassName+">, "+className+" {"); + p(); + indent(); + + p("private "+beanClassName+" bean;"); + p("private org.apache.activemq.protobuf.Buffer buffer;"); + p("private int size=-1;"); + p("private int hashCode;"); + p(); + p("private "+bufferClassName+"(org.apache.activemq.protobuf.Buffer buffer) {"); + indent(); + p("this.buffer = buffer;"); + unindent(); + p("}"); + p(); + p("private "+bufferClassName+"("+beanClassName+" bean) {"); + indent(); + p("this.bean = bean;"); + unindent(); + p("}"); + p(); + p("public "+beanClassName+" copy() {"); + indent(); + p("return bean().copy();"); + unindent(); + p("}"); + p(); + p("public "+bufferClassName+" freeze() {"); + indent(); + p("return this;"); + unindent(); + p("}"); + p(); + p("private "+beanClassName+" bean() {"); + indent(); + p("if (bean == null) {"); + indent(); + p("try {"); + indent(); + p("bean = new "+beanClassName+"().mergeUnframed(new org.apache.activemq.protobuf.CodedInputStream(buffer));"); + p("bean.frozen=this;"); + unindent(); + p("} catch (org.apache.activemq.protobuf.InvalidProtocolBufferException e) {"); + indent(); + p("throw new RuntimeException(e);"); + unindent(); + p("} catch (java.io.IOException e) {"); + indent(); + p("throw new RuntimeException(\"An IOException was thrown (should never happen in this method).\", e);"); + unindent(); + p("}"); + unindent(); + p("}"); + p("return bean;"); + unindent(); + p("}"); + p(); + + p("public String toString() {"); + indent(); + p("return bean().toString();"); + unindent(); + p("}"); + p(); + p("public java.lang.StringBuilder toString(java.lang.StringBuilder sb, String prefix) {"); + indent(); + p("return bean().toString(sb, prefix);"); + unindent(); + p("}"); + p(); + + for (FieldDescriptor field : m.getFields().values()) { + generateBufferGetters(field); + } + + generateMethodWrite(m); + + generateMethodSerializedSize(m); + + generateMethodParseFrom(m, bufferClassName, beanClassName); + + generateBufferEquals(m, bufferClassName); + + p("public boolean frozen() {"); + indent(); + p("return true;"); + unindent(); + p("}"); + + for (EnumFieldDescriptor enumFeild : m.getAssociatedEnumFieldDescriptors()) { + String enumName = uCamel(enumFeild.getParent().getName()); + p("public "+enumName+" to"+enumName+"() {"); + indent(); + p("return "+enumName+"."+enumFeild.getName()+";"); + unindent(); + p("}"); + p(); + } + + unindent(); + p("}"); + p(); + + +// generateMethodVisitor(m); +// generateMethodType(m, className); + + + unindent(); + p("}"); + p(); + + } + + + private void generateMethodFreeze(MessageDescriptor m, String bufferClassName) { + p("public boolean frozen() {"); + indent(); + p("return frozen!=null;"); + unindent(); + p("}"); + p(); + p("public "+bufferClassName+" freeze() {"); + indent(); + p("if( frozen==null ) {"); + indent(); + p("frozen = new "+bufferClassName+"(bean);"); + p("assert deepFreeze();"); + unindent(); + p("}"); + p("return frozen;"); + unindent(); + p("}"); + p(); + p("private boolean deepFreeze() {"); + indent(); + p("frozen.serializedSizeUnframed();"); + p("return true;"); + unindent(); + p("}"); + p(); + + } + + /** + * @param m + * @param className + */ + private void generateMethodCopyFromBean(MessageDescriptor m, String className) { + p("private void copy("+className+" other) {"); + indent(); + p("this.bean = this;"); + for (FieldDescriptor field : m.getFields().values()) { + String lname = lCamel(field.getName()); + String type = field.getRule()==FieldDescriptor.REPEATED_RULE ? javaCollectionType(field):javaType(field); + boolean primitive = field.getTypeDescriptor()==null || field.getTypeDescriptor().isEnum(); + if( field.isRepeated() ) { + if( primitive ) { + p("this.f_" + lname + " = other.f_" + lname + ";"); + p("if( this.f_" + lname + " !=null && !other.frozen()) {"); + indent(); + p("this.f_" + lname + " = new java.util.ArrayList<"+type+">(this.f_" + lname + ");"); + unindent(); + p("}"); + } else { + p("this.f_" + lname + " = other.f_" + lname + ";"); + p("if( this.f_" + lname + " !=null) {"); + indent(); + p("this.f_" + lname + " = new java.util.ArrayList<"+type+">(other.f_" + lname + ".size());"); + p("for( "+type+" e : other.f_" + lname + ") {"); + indent(); + p("this.f_" + lname + ".add(e.copy());"); + unindent(); + p("}"); + unindent(); + p("}"); + } + } else { + if( primitive ) { + p("this.f_" + lname + " = other.f_" + lname + ";"); + p("this.b_" + lname + " = other.b_" + lname + ";"); + } else { + p("this.f_" + lname + " = other.f_" + lname + ";"); + p("if( this.f_" + lname + " !=null ) {"); + indent(); + p("this.f_" + lname + " = this.f_" + lname + ".copy();"); + unindent(); + p("}"); + } + } + } + unindent(); + p("}"); + p(); + } + + + /** + * If the java_visitor message option is set, then this method generates a visitor method. The option + * speifiies the class name of the visitor and optionally the return value and exceptions thrown by the visitor. + * + * Examples: + * + * option java_visitor = "org.apache.kahadb.store.Visitor"; + * generates: + * public void visit(org.apache.kahadb.store.Visitor visitor) { + * visitor.visit(this); + * } + * + * option java_visitor = "org.apache.kahadb.store.Visitor:int:java.io.IOException"; + * generates: + * public int visit(org.apache.kahadb.store.Visitor visitor) throws java.io.IOException { + * return visitor.visit(this); + * } + * + * @param m + */ + private void generateMethodVisitor(MessageDescriptor m) { + String javaVisitor = getOption(m.getOptions(), "java_visitor", null); + if( javaVisitor!=null ) { + String returns = "void"; + String throwsException = null; + + StringTokenizer st = new StringTokenizer(javaVisitor, ":"); + String vistorClass = st.nextToken(); + if( st.hasMoreTokens() ) { + returns = st.nextToken(); + } + if( st.hasMoreTokens() ) { + throwsException = st.nextToken(); + } + + String throwsClause = ""; + if( throwsException!=null ) { + throwsClause = "throws "+throwsException+" "; + } + + p("public "+returns+" visit("+vistorClass+" visitor) "+throwsClause+ "{"); + indent(); + if( "void".equals(returns) ) { + p("visitor.visit(this);"); + } else { + p("return visitor.visit(this);"); + } + unindent(); + p("}"); + p(); + } + } + + private void generateMethodType(MessageDescriptor m, String className) { + String typeEnum = getOption(m.getOptions(), "java_type_method", null); + if( typeEnum!=null ) { + + TypeDescriptor typeDescriptor = m.getType(typeEnum); + if( typeDescriptor == null ) { + typeDescriptor = m.getProtoDescriptor().getType(typeEnum); + } + if( typeDescriptor == null || !typeDescriptor.isEnum() ) { + errors.add("The java_type_method option on the "+m.getName()+" message does not point to valid enum type"); + return; + } + + + String constant = constantCase(className); + EnumDescriptor enumDescriptor = (EnumDescriptor)typeDescriptor; + if( enumDescriptor.getFields().get(constant) == null ) { + errors.add("The java_type_method option on the "+m.getName()+" message does not points to the "+typeEnum+" enum but it does not have an entry for "+constant); + } + + String type = javaType(typeDescriptor); + + p("public "+type+" type() {"); + indent(); + p("return "+type+"."+constant+";"); + unindent(); + p("}"); + p(); + } + } + + private void generateMethodParseFrom(MessageDescriptor m, String bufferClassName, String beanClassName) { + p("public static "+beanClassName+" parseUnframed(org.apache.activemq.protobuf.CodedInputStream data) throws org.apache.activemq.protobuf.InvalidProtocolBufferException, java.io.IOException {"); + indent(); + p("return new "+beanClassName+"().mergeUnframed(data);"); + unindent(); + p("}"); + p(); + p("public static "+beanClassName+" parseUnframed(java.io.InputStream data) throws org.apache.activemq.protobuf.InvalidProtocolBufferException, java.io.IOException {"); + indent(); + p("return parseUnframed(new org.apache.activemq.protobuf.CodedInputStream(data));"); + unindent(); + p("}"); + p(); + p("public static "+bufferClassName+" parseUnframed(org.apache.activemq.protobuf.Buffer data) throws org.apache.activemq.protobuf.InvalidProtocolBufferException {"); + indent(); + p("return new "+bufferClassName+"(data);"); + unindent(); + p("}"); + p(); + p("public static "+bufferClassName+" parseUnframed(byte[] data) throws org.apache.activemq.protobuf.InvalidProtocolBufferException {"); + indent(); + p("return parseUnframed(new org.apache.activemq.protobuf.Buffer(data));"); + unindent(); + p("}"); + p(); + p("public static "+bufferClassName+" parseFramed(org.apache.activemq.protobuf.CodedInputStream data) throws org.apache.activemq.protobuf.InvalidProtocolBufferException, java.io.IOException {"); + indent(); + p("int length = data.readRawVarint32();"); + p("int oldLimit = data.pushLimit(length);"); + p(""+bufferClassName+" rc = parseUnframed(data.readRawBytes(length));"); + p("data.popLimit(oldLimit);"); + p("return rc;"); + unindent(); + p("}"); + p(); + p("public static "+bufferClassName+" parseFramed(org.apache.activemq.protobuf.Buffer data) throws org.apache.activemq.protobuf.InvalidProtocolBufferException {"); + indent(); + p("try {"); + indent(); + p("org.apache.activemq.protobuf.CodedInputStream input = new org.apache.activemq.protobuf.CodedInputStream(data);"); + p(""+bufferClassName+" rc = parseFramed(input);"); + p("input.checkLastTagWas(0);"); + p("return rc;"); + unindent(); + p("} catch (org.apache.activemq.protobuf.InvalidProtocolBufferException e) {"); + indent(); + p("throw e;"); + unindent(); + p("} catch (java.io.IOException e) {"); + indent(); + p("throw new RuntimeException(\"An IOException was thrown (should never happen in this method).\", e);"); + unindent(); + p("}"); + unindent(); + p("}"); + p(); + p("public static "+bufferClassName+" parseFramed(byte[] data) throws org.apache.activemq.protobuf.InvalidProtocolBufferException {"); + indent(); + p("return parseFramed(new org.apache.activemq.protobuf.Buffer(data));"); + unindent(); + p("}"); + p(); + p("public static "+bufferClassName+" parseFramed(java.io.InputStream data) throws org.apache.activemq.protobuf.InvalidProtocolBufferException, java.io.IOException {"); + indent(); + p("return parseUnframed(org.apache.activemq.protobuf.MessageBufferSupport.readFrame(data));"); + unindent(); + p("}"); + p(); + + } + + private void generateBeanEquals(MessageDescriptor m, String className) { + p("public boolean equals(Object obj) {"); + indent(); + p("if( obj==this )"); + p(" return true;"); + p(""); + p("if( obj==null || obj.getClass()!="+className+".class )"); + p(" return false;"); + p(""); + p("return equals(("+className+")obj);"); + unindent(); + p("}"); + p(""); + + p("public boolean equals("+className+" obj) {"); + indent(); + for (FieldDescriptor field : m.getFields().values()) { + String uname = uCamel(field.getName()); + String getterMethod="get"+uname+"()"; + String hasMethod = "has"+uname+"()"; + + if( field.getRule() == FieldDescriptor.REPEATED_RULE ) { + getterMethod = "get"+uname+"List()"; + } + + p("if ("+hasMethod+" ^ obj."+hasMethod+" ) "); + p(" return false;"); + + + + if( field.getRule() != FieldDescriptor.REPEATED_RULE && (field.isNumberType() || field.getType()==FieldDescriptor.BOOL_TYPE) ) { + p("if ("+hasMethod+" && ( "+getterMethod+"!=obj."+getterMethod+" ))"); + } else { + p("if ("+hasMethod+" && ( !"+getterMethod+".equals(obj."+getterMethod+") ))"); + } + p(" return false;"); + } + p("return true;"); + unindent(); + p("}"); + p(""); + p("public int hashCode() {"); + indent(); + int hc = className.hashCode(); + p("int rc="+hc+";"); + int counter=0; + for (FieldDescriptor field : m.getFields().values()) { + counter++; + + String uname = uCamel(field.getName()); + String getterMethod="get"+uname+"()"; + String hasMethod = "has"+uname+"()"; + + if( field.getRule() == FieldDescriptor.REPEATED_RULE ) { + getterMethod = "get"+uname+"List()"; + } + + p("if ("+hasMethod+") {"); + indent(); + + if( field.getRule() == FieldDescriptor.REPEATED_RULE ) { + p("rc ^= ( "+uname.hashCode()+"^"+getterMethod+".hashCode() );"); + } else if( field.isInteger32Type() ) { + p("rc ^= ( "+uname.hashCode()+"^"+getterMethod+" );"); + } else if( field.isInteger64Type() ) { + p("rc ^= ( "+uname.hashCode()+"^(new Long("+getterMethod+")).hashCode() );"); + } else if( field.getType()==FieldDescriptor.DOUBLE_TYPE ) { + p("rc ^= ( "+uname.hashCode()+"^(new Double("+getterMethod+")).hashCode() );"); + } else if( field.getType()==FieldDescriptor.FLOAT_TYPE ) { + p("rc ^= ( "+uname.hashCode()+"^(new Double("+getterMethod+")).hashCode() );"); + } else if( field.getType()==FieldDescriptor.BOOL_TYPE ) { + p("rc ^= ( "+uname.hashCode()+"^ ("+getterMethod+"? "+counter+":-"+counter+") );"); + } else { + p("rc ^= ( "+uname.hashCode()+"^"+getterMethod+".hashCode() );"); + } + unindent(); + p("}"); + } + p("return rc;"); + unindent(); + p("}"); + p(""); + } + + private void generateBufferEquals(MessageDescriptor m, String className) { + p("public boolean equals(Object obj) {"); + indent(); + p("if( obj==this )"); + p(" return true;"); + p(""); + p("if( obj==null || obj.getClass()!="+className+".class )"); + p(" return false;"); + p(""); + p("return equals(("+className+")obj);"); + unindent(); + p("}"); + p(""); + + p("public boolean equals("+className+" obj) {"); + indent(); + p("return toUnframedBuffer().equals(obj.toUnframedBuffer());"); + unindent(); + p("}"); + p(""); + p("public int hashCode() {"); + indent(); + int hc = className.hashCode(); + p("if( hashCode==0 ) {"); + p("hashCode="+hc+" ^ toUnframedBuffer().hashCode();"); + p("}"); + p("return hashCode;"); + unindent(); + p("}"); + p(""); + } + + /** + * @param m + */ + private void generateMethodSerializedSize(MessageDescriptor m) { + + p("public int serializedSizeFramed() {"); + indent(); + p("int t = serializedSizeUnframed();"); + p("return org.apache.activemq.protobuf.CodedOutputStream.computeRawVarint32Size(t) + t;"); + unindent(); + p("}"); + p(); + p("public int serializedSizeUnframed() {"); + indent(); + p("if (buffer != null) {"); + indent(); + p("return buffer.length;"); + unindent(); + p("}"); + p("if (size != -1)"); + p(" return size;"); + p(); + p("size = 0;"); + for (FieldDescriptor field : m.getFields().values()) { + + String uname = uCamel(field.getName()); + String getter="get"+uname+"()"; + String type = javaType(field); + + if( !field.isRequired() ) { + p("if (has"+uname+"()) {"); + indent(); + } + + if( field.getRule() == FieldDescriptor.REPEATED_RULE ) { + p("for ("+type+" i : get"+uname+"List()) {"); + indent(); + getter = "i"; + } + + if( field.getType()==FieldDescriptor.STRING_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeStringSize("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.BYTES_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeBytesSize("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.BOOL_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeBoolSize("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.DOUBLE_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeDoubleSize("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.FLOAT_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeFloatSize("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.INT32_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeInt32Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.INT64_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeInt64Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.SINT32_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeSInt32Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.SINT64_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeSInt64Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.UINT32_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeUInt32Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.UINT64_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeUInt64Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.FIXED32_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeFixed32Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.FIXED64_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeFixed64Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.SFIXED32_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeSFixed32Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.SFIXED64_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeSFixed64Size("+field.getTag()+", "+getter+");"); + } else if( field.getTypeDescriptor().isEnum() ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeEnumSize("+field.getTag()+", "+getter+".getNumber());"); + } else if ( field.getGroup()!=null ) { + errors.add("This code generator does not support group fields."); +// p("size += org.apache.activemq.protobuf.MessageBufferSupport.computeGroupSize("+field.getTag()+", ("+type+"Buffer)"+getter+");"); + } else { + p("size += org.apache.activemq.protobuf.MessageBufferSupport.computeMessageSize("+field.getTag()+", "+getter+".freeze());"); + } + if( field.getRule() == FieldDescriptor.REPEATED_RULE ) { + unindent(); + p("}"); + } + + if( !field.isRequired() ) { + unindent(); + p("}"); + } + + } + // TODO: handle unknown fields + // size += getUnknownFields().getSerializedSize();"); + p("return size;"); + unindent(); + p("}"); + p(); + } + + /** + * @param m + */ + private void generateMethodWrite(MessageDescriptor m) { + + p("public org.apache.activemq.protobuf.Buffer toUnframedBuffer() {"); + indent(); + p("if( buffer !=null ) {"); + indent(); + p("return buffer;"); + unindent(); + p("}"); + p("return org.apache.activemq.protobuf.MessageBufferSupport.toUnframedBuffer(this);"); + unindent(); + p("}"); + p(); + p("public org.apache.activemq.protobuf.Buffer toFramedBuffer() {"); + indent(); + p("return org.apache.activemq.protobuf.MessageBufferSupport.toFramedBuffer(this);"); + unindent(); + p("}"); + p(); + p("public byte[] toUnframedByteArray() {"); + indent(); + p("return toUnframedBuffer().toByteArray();"); + unindent(); + p("}"); + p(); + p("public byte[] toFramedByteArray() {"); + indent(); + p("return toFramedBuffer().toByteArray();"); + unindent(); + p("}"); + p(); + p("public void writeFramed(org.apache.activemq.protobuf.CodedOutputStream output) throws java.io.IOException {"); + indent(); + p("output.writeRawVarint32(serializedSizeUnframed());"); + p("writeUnframed(output);"); + unindent(); + p("}"); + p(); + p("public void writeFramed(java.io.OutputStream output) throws java.io.IOException {"); + indent(); + p("org.apache.activemq.protobuf.CodedOutputStream codedOutput = new org.apache.activemq.protobuf.CodedOutputStream(output);"); + p("writeFramed(codedOutput);"); + p("codedOutput.flush();"); + unindent(); + p("}"); + p(); + + p("public void writeUnframed(java.io.OutputStream output) throws java.io.IOException {"); + indent(); + p("org.apache.activemq.protobuf.CodedOutputStream codedOutput = new org.apache.activemq.protobuf.CodedOutputStream(output);"); + p("writeUnframed(codedOutput);"); + p("codedOutput.flush();"); + unindent(); + p("}"); + p(); + + p("public void writeUnframed(org.apache.activemq.protobuf.CodedOutputStream output) throws java.io.IOException {"); + indent(); + + p("if (buffer == null) {"); + indent(); + p("int size = serializedSizeUnframed();"); + p("buffer = output.getNextBuffer(size);"); + p("org.apache.activemq.protobuf.CodedOutputStream original=null;"); + p("if( buffer == null ) {"); + indent(); + p("buffer = new org.apache.activemq.protobuf.Buffer(new byte[size]);"); + p("original = output;"); + p("output = new org.apache.activemq.protobuf.CodedOutputStream(buffer);"); + unindent(); + p("}"); + + for (FieldDescriptor field : m.getFields().values()) { + String uname = uCamel(field.getName()); + String getter="bean.get"+uname+"()"; + String type = javaType(field); + + if( !field.isRequired() ) { + p("if (bean.has"+uname+"()) {"); + indent(); + } + + if( field.getRule() == FieldDescriptor.REPEATED_RULE ) { + p("for ("+type+" i : bean.get"+uname+"List()) {"); + indent(); + getter = "i"; + } + + if( field.getType()==FieldDescriptor.STRING_TYPE ) { + p("output.writeString("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.BYTES_TYPE ) { + p("output.writeBytes("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.BOOL_TYPE ) { + p("output.writeBool("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.DOUBLE_TYPE ) { + p("output.writeDouble("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.FLOAT_TYPE ) { + p("output.writeFloat("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.INT32_TYPE ) { + p("output.writeInt32("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.INT64_TYPE ) { + p("output.writeInt64("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.SINT32_TYPE ) { + p("output.writeSInt32("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.SINT64_TYPE ) { + p("output.writeSInt64("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.UINT32_TYPE ) { + p("output.writeUInt32("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.UINT64_TYPE ) { + p("output.writeUInt64("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.FIXED32_TYPE ) { + p("output.writeFixed32("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.FIXED64_TYPE ) { + p("output.writeFixed64("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.SFIXED32_TYPE ) { + p("output.writeSFixed32("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.SFIXED64_TYPE ) { + p("output.writeSFixed64("+field.getTag()+", "+getter+");"); + } else if( field.getTypeDescriptor().isEnum() ) { + p("output.writeEnum("+field.getTag()+", "+getter+".getNumber());"); + } else if ( field.getGroup()!=null ) { + errors.add("This code generator does not support group fields."); +// p("writeGroup(output, "+field.getTag()+", "+getter+");"); + } else { + p("org.apache.activemq.protobuf.MessageBufferSupport.writeMessage(output, "+field.getTag()+", "+getter+".freeze());"); + } + + if( field.getRule() == FieldDescriptor.REPEATED_RULE ) { + unindent(); + p("}"); + } + + if( !field.isRequired() ) { + unindent(); + p("}"); + } + } + + p("if( original !=null ) {"); + indent(); + p("output.checkNoSpaceLeft();"); + p("output = original;"); + p("output.writeRawBytes(buffer);"); + unindent(); + p("}"); + unindent(); + p("} else {"); + indent(); + p("output.writeRawBytes(buffer);"); + unindent(); + p("}"); + + unindent(); + p("}"); + p(); + } + + /** + * @param m + * @param className + */ + private void generateMethodMergeFromStream(MessageDescriptor m, String className) { + p("public "+className+" mergeUnframed(java.io.InputStream input) throws java.io.IOException {"); + indent(); + p("return mergeUnframed(new org.apache.activemq.protobuf.CodedInputStream(input));"); + unindent(); + p("}"); + p(); + p("public "+className+" mergeUnframed(org.apache.activemq.protobuf.CodedInputStream input) throws java.io.IOException {"); + indent(); + { + p("copyCheck();"); + p("while (true) {"); + indent(); + { + p("int tag = input.readTag();"); + p("if ((tag & 0x07) == 4) {"); + p(" return this;"); + p("}"); + + p("switch (tag) {"); + p("case 0:"); + p(" return this;"); + p("default: {"); + + p(" break;"); + p("}"); + + for (FieldDescriptor field : m.getFields().values()) { + String uname = uCamel(field.getName()); + String setter = "set" + uname; + boolean repeated = field.getRule() == FieldDescriptor.REPEATED_RULE; + if (repeated) { + setter = "create" + uname + "List().add"; + } + if (field.getType() == FieldDescriptor.STRING_TYPE) { + p("case " + + makeTag(field.getTag(), + WIRETYPE_LENGTH_DELIMITED) + ":"); + indent(); + p(setter + "(input.readString());"); + } else if (field.getType() == FieldDescriptor.BYTES_TYPE) { + p("case "+ makeTag(field.getTag(), WIRETYPE_LENGTH_DELIMITED) + ":"); + indent(); + String override = getOption(field.getOptions(), "java_override_type", null); + if( "AsciiBuffer".equals(override) ) { + p(setter + "(new org.apache.activemq.protobuf.AsciiBuffer(input.readBytes()));"); + } else if( "UTF8Buffer".equals(override) ) { + p(setter + "(new org.apache.activemq.protobuf.UTF8Buffer(input.readBytes()));"); + } else { + p(setter + "(input.readBytes());"); + } + } else if (field.getType() == FieldDescriptor.BOOL_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_VARINT)+ ":"); + indent(); + p(setter + "(input.readBool());"); + } else if (field.getType() == FieldDescriptor.DOUBLE_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_FIXED64) + + ":"); + indent(); + p(setter + "(input.readDouble());"); + } else if (field.getType() == FieldDescriptor.FLOAT_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_FIXED32) + + ":"); + indent(); + p(setter + "(input.readFloat());"); + } else if (field.getType() == FieldDescriptor.INT32_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_VARINT) + + ":"); + indent(); + p(setter + "(input.readInt32());"); + } else if (field.getType() == FieldDescriptor.INT64_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_VARINT) + + ":"); + indent(); + p(setter + "(input.readInt64());"); + } else if (field.getType() == FieldDescriptor.SINT32_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_VARINT) + + ":"); + indent(); + p(setter + "(input.readSInt32());"); + } else if (field.getType() == FieldDescriptor.SINT64_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_VARINT) + + ":"); + indent(); + p(setter + "(input.readSInt64());"); + } else if (field.getType() == FieldDescriptor.UINT32_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_VARINT) + + ":"); + indent(); + p(setter + "(input.readUInt32());"); + } else if (field.getType() == FieldDescriptor.UINT64_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_VARINT) + + ":"); + indent(); + p(setter + "(input.readUInt64());"); + } else if (field.getType() == FieldDescriptor.FIXED32_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_FIXED32) + + ":"); + indent(); + p(setter + "(input.readFixed32());"); + } else if (field.getType() == FieldDescriptor.FIXED64_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_FIXED64) + + ":"); + indent(); + p(setter + "(input.readFixed64());"); + } else if (field.getType() == FieldDescriptor.SFIXED32_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_FIXED32) + + ":"); + indent(); + p(setter + "(input.readSFixed32());"); + } else if (field.getType() == FieldDescriptor.SFIXED64_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_FIXED64) + + ":"); + indent(); + p(setter + "(input.readSFixed64());"); + } else if (field.getTypeDescriptor().isEnum()) { + p("case " + makeTag(field.getTag(), WIRETYPE_VARINT) + + ":"); + indent(); + String type = javaType(field); + p("{"); + indent(); + p("int t = input.readEnum();"); + p("" + type + " value = " + type + ".valueOf(t);"); + p("if( value !=null ) {"); + indent(); + p(setter + "(value);"); + unindent(); + p("}"); + // TODO: else store it as an known + + unindent(); + p("}"); + + } else if (field.getGroup() != null) { + errors.add("This code generator does not support group fields."); +// p("case "+ makeTag(field.getTag(), WIRETYPE_START_GROUP)+ ":"); +// indent(); +// String type = javaType(field); +// if (repeated) { +// p(setter + "(readGroup(input, " + field.getTag()+ ", new " + type + "()));"); +// } else { +// p("if (has" + uname + "()) {"); +// indent(); +// p("readGroup(input, " + field.getTag() + ", get" +// + uname + "());"); +// unindent(); +// p("} else {"); +// indent(); +// p(setter + "(readGroup(input, " + field.getTag() +// + ",new " + type + "()));"); +// unindent(); +// p("}"); +// } +// p(""); + } else { + p("case "+ makeTag(field.getTag(),WIRETYPE_LENGTH_DELIMITED) + ":"); + indent(); + String type = javaType(field); + if (repeated) { + p(setter + "("+javaRelatedType(type, "Buffer")+".parseFramed(input));"); + } else { + p("if (has" + uname + "()) {"); + indent(); + p("set" + uname + "(get" + uname + "().copy().mergeFrom("+javaRelatedType(type, "Buffer")+".parseFramed(input)));"); + unindent(); + p("} else {"); + indent(); + p(setter + "("+javaRelatedType(type, "Buffer")+".parseFramed(input));"); + unindent(); + p("}"); + } + } + p("break;"); + unindent(); + } + p("}"); + } + unindent(); + p("}"); + } + unindent(); + p("}"); + } + + /** + * @param m + * @param className + */ + private void generateMethodMergeFromBean(MessageDescriptor m, String className) { + p("public "+className+"Bean mergeFrom("+className+" other) {"); + indent(); + p("copyCheck();"); + for (FieldDescriptor field : m.getFields().values()) { + String uname = uCamel(field.getName()); + p("if (other.has"+uname+"()) {"); + indent(); + + if( field.isScalarType() || field.getTypeDescriptor().isEnum() ) { + if( field.isRepeated() ) { + p("get"+uname+"List().addAll(other.get"+uname+"List());"); + } else { + p("set"+uname+"(other.get"+uname+"());"); + } + } else { + + String type = javaType(field); + // It's complex type... + if( field.isRepeated() ) { + p("for("+type+" element: other.get"+uname+"List() ) {"); + indent(); + p("get"+uname+"List().add(element.copy());"); + unindent(); + p("}"); + } else { + p("if (has"+uname+"()) {"); + indent(); + p("set"+uname+"(get"+uname+"().copy().mergeFrom(other.get"+uname+"()));"); + unindent(); + p("} else {"); + indent(); + p("set"+uname+"(other.get"+uname+"().copy());"); + unindent(); + p("}"); + } + } + unindent(); + p("}"); + } + p("return this;"); + unindent(); + p("}"); + p(); + } + + /** + * @param m + */ + private void generateMethodClear(MessageDescriptor m) { + p("public void clear() {"); + indent(); + for (FieldDescriptor field : m.getFields().values()) { + String uname = uCamel(field.getName()); + p("clear" + uname + "();"); + } + unindent(); + p("}"); + p(); + } + + private void generateReadWriteExternal(MessageDescriptor m) { + + p("public void readExternal(java.io.DataInput in) throws java.io.IOException {"); + indent(); + p("assert frozen==null : org.apache.activemq.protobuf.MessageBufferSupport.FORZEN_ERROR_MESSAGE;"); + p("bean = this;"); + p("frozen = null;"); + + for (FieldDescriptor field : m.getFields().values()) { + String lname = lCamel(field.getName()); + String type = javaType(field); + boolean repeated = field.getRule()==FieldDescriptor.REPEATED_RULE; + + // Create the fields.. + if( repeated ) { + p("{"); + indent(); + p("int size = in.readShort();"); + p("if( size>=0 ) {"); + indent(); + p("f_"+lname+" = new java.util.ArrayList<" + javaCollectionType(field) + ">(size);"); + p("for(int i=0; i=0 ) {"); + indent(); + p("byte b[] = new byte[size];"); + p("in.readFully(b);"); + p("f_"+lname+" = new "+type+"(b);"); + p("b_"+lname+" = true;"); + unindent(); + p("} else {"); + indent(); + p("f_"+lname+" = null;"); + p("b_"+lname+" = false;"); + unindent(); + p("}"); + unindent(); + p("}"); + } else if (field.getTypeDescriptor().isEnum() ) { + p("if( in.readBoolean() ) {"); + indent(); + p("f_"+lname+" = " + type + ".valueOf(in.readShort());"); + p("b_"+lname+" = true;"); + unindent(); + p("} else {"); + indent(); + p("f_"+lname+" = null;"); + p("b_"+lname+" = false;"); + unindent(); + p("}"); + } else { + p("if( in.readBoolean() ) {"); + indent(); + p(""+javaRelatedType(type, "Bean")+" o = new "+javaRelatedType(type, "Bean")+"();"); + p("o.readExternal(in);"); + p("f_"+lname+" = o;"); + unindent(); + p("} else {"); + indent(); + p("f_"+lname+" = null;"); + unindent(); + p("}"); + } + } + } + + unindent(); + p("}"); + p(); + p("public void writeExternal(java.io.DataOutput out) throws java.io.IOException {"); + indent(); + for (FieldDescriptor field : m.getFields().values()) { + String lname = lCamel(field.getName()); + boolean repeated = field.getRule()==FieldDescriptor.REPEATED_RULE; + + // Create the fields.. + if( repeated ) { + p("if( bean.f_"+lname+"!=null ) {"); + indent(); + p("out.writeShort(bean.f_"+lname+".size());"); + p("for(" + javaCollectionType(field) + " o : bean.f_"+lname+") {"); + indent(); + + if( field.isInteger32Type() ) { + p("out.writeInt(o);"); + } else if( field.isInteger64Type() ) { + p("out.writeLong(o);"); + } else if( field.getType() == FieldDescriptor.DOUBLE_TYPE ) { + p("out.writeDouble(o);"); + } else if( field.getType() == FieldDescriptor.FLOAT_TYPE ) { + p("out.writeFloat(o);"); + } else if( field.getType() == FieldDescriptor.BOOL_TYPE ) { + p("out.writeBoolean(o);"); + } else if( field.getType() == FieldDescriptor.STRING_TYPE ) { + p("out.writeUTF(o);"); + } else if( field.getType() == FieldDescriptor.BYTES_TYPE ) { + p("out.writeInt(o.getLength());"); + p("out.write(o.getData(), o.getOffset(), o.getLength());"); + } else if (field.getTypeDescriptor().isEnum() ) { + p("out.writeShort(o.getNumber());"); + } else { + p("o.copy().writeExternal(out);"); + } + unindent(); + p("}"); + unindent(); + p("} else {"); + indent(); + p("out.writeShort(-1);"); + unindent(); + p("}"); + + } else { + if( field.isInteger32Type() ) { + p("out.writeInt(bean.f_"+lname+");"); + } else if( field.isInteger64Type() ) { + p("out.writeLong(bean.f_"+lname+");"); + } else if( field.getType() == FieldDescriptor.DOUBLE_TYPE ) { + p("out.writeDouble(bean.f_"+lname+");"); + } else if( field.getType() == FieldDescriptor.FLOAT_TYPE ) { + p("out.writeFloat(bean.f_"+lname+");"); + } else if( field.getType() == FieldDescriptor.BOOL_TYPE ) { + p("out.writeBoolean(bean.f_"+lname+");"); + } else if( field.getType() == FieldDescriptor.STRING_TYPE ) { + p("if( bean.f_"+lname+"!=null ) {"); + indent(); + p("out.writeBoolean(true);"); + p("out.writeUTF(bean.f_"+lname+");"); + unindent(); + p("} else {"); + indent(); + p("out.writeBoolean(false);"); + unindent(); + p("}"); + } else if( field.getType() == FieldDescriptor.BYTES_TYPE ) { + p("if( bean.f_"+lname+"!=null ) {"); + indent(); + p("out.writeInt(bean.f_"+lname+".getLength());"); + p("out.write(bean.f_"+lname+".getData(), bean.f_"+lname+".getOffset(), bean.f_"+lname+".getLength());"); + unindent(); + p("} else {"); + indent(); + p("out.writeInt(-1);"); + unindent(); + p("}"); + } else if (field.getTypeDescriptor().isEnum() ) { + p("if( bean.f_"+lname+"!=null ) {"); + indent(); + p("out.writeBoolean(true);"); + p("out.writeShort(bean.f_"+lname+".getNumber());"); + unindent(); + p("} else {"); + indent(); + p("out.writeBoolean(false);"); + unindent(); + p("}"); + } else { + p("if( bean.f_"+lname+"!=null ) {"); + indent(); + p("out.writeBoolean(true);"); + p("bean.f_"+lname+".copy().writeExternal(out);"); + unindent(); + p("} else {"); + indent(); + p("out.writeBoolean(false);"); + unindent(); + p("}"); + } + } + } + + unindent(); + p("}"); + p(); + + } + + +// private void generateMethodAssertInitialized(MessageDescriptor m, String className) { +// +// p("public java.util.ArrayList missingFields() {"); +// indent(); +// p("java.util.ArrayList missingFields = super.missingFields();"); +// +// for (FieldDescriptor field : m.getFields().values()) { +// String uname = uCamel(field.getName()); +// if( field.isRequired() ) { +// p("if( !has" + uname + "() ) {"); +// indent(); +// p("missingFields.add(\""+field.getName()+"\");"); +// unindent(); +// p("}"); +// } +// } +// +// if( !deferredDecode ) { +// for (FieldDescriptor field : m.getFields().values()) { +// if( field.getTypeDescriptor()!=null && !field.getTypeDescriptor().isEnum()) { +// String uname = uCamel(field.getName()); +// p("if( has" + uname + "() ) {"); +// indent(); +// if( !field.isRepeated() ) { +// p("try {"); +// indent(); +// p("get" + uname + "().assertInitialized();"); +// unindent(); +// p("} catch (org.apache.activemq.protobuf.UninitializedMessageException e){"); +// indent(); +// p("missingFields.addAll(prefix(e.getMissingFields(),\""+field.getName()+".\"));"); +// unindent(); +// p("}"); +// } else { +// String type = javaCollectionType(field); +// p("java.util.List<"+type+"> l = get" + uname + "List();"); +// p("for( int i=0; i < l.size(); i++ ) {"); +// indent(); +// p("try {"); +// indent(); +// p("l.get(i).assertInitialized();"); +// unindent(); +// p("} catch (org.apache.activemq.protobuf.UninitializedMessageException e){"); +// indent(); +// p("missingFields.addAll(prefix(e.getMissingFields(),\""+field.getName()+"[\"+i+\"]\"));"); +// unindent(); +// p("}"); +// unindent(); +// p("}"); +// } +// unindent(); +// p("}"); +// } +// } +// } +// p("return missingFields;"); +// unindent(); +// p("}"); +// p(); +// } + + private void generateMethodToString(MessageDescriptor m) { + + p("public String toString() {"); + indent(); + p("return toString(new java.lang.StringBuilder(), \"\").toString();"); + unindent(); + p("}"); + p(); + + p("public java.lang.StringBuilder toString(java.lang.StringBuilder sb, String prefix) {"); + indent(); + for (FieldDescriptor field : m.getFields().values()) { + String uname = uCamel(field.getName()); + p("if( has" + uname + "() ) {"); + indent(); + if( field.isRepeated() ) { + String type = javaCollectionType(field); + p("java.util.List<"+type+"> l = get" + uname + "List();"); + p("for( int i=0; i < l.size(); i++ ) {"); + indent(); + if( field.getTypeDescriptor()!=null && !field.getTypeDescriptor().isEnum()) { + p("sb.append(prefix+\""+field.getName()+"[\"+i+\"] {\\n\");"); + p("l.get(i).toString(sb, prefix+\" \");"); + p("sb.append(prefix+\"}\\n\");"); + } else { + p("sb.append(prefix+\""+field.getName()+"[\"+i+\"]: \");"); + p("sb.append(l.get(i));"); + p("sb.append(\"\\n\");"); + } + unindent(); + p("}"); + } else { + if( field.getTypeDescriptor()!=null && !field.getTypeDescriptor().isEnum()) { + p("sb.append(prefix+\""+field.getName()+" {\\n\");"); + p("get" + uname + "().toString(sb, prefix+\" \");"); + p("sb.append(prefix+\"}\\n\");"); + } else { + p("sb.append(prefix+\""+field.getName()+": \");"); + p("sb.append(get" + uname + "());"); + p("sb.append(\"\\n\");"); + } + } + unindent(); + p("}"); + } + p("return sb;"); + unindent(); + p("}"); + p(); + + } + + /** + * @param field + * @param className + */ + private void generateBufferGetters(FieldDescriptor field) { + String uname = uCamel(field.getName()); + String type = field.getRule()==FieldDescriptor.REPEATED_RULE ? javaCollectionType(field):javaType(field); + boolean repeated = field.getRule()==FieldDescriptor.REPEATED_RULE; + + // Create the fields.. + p("// " + field.getRule() + " " + field.getType() + " " + field.getName() + " = " + field.getTag() + ";"); + if( repeated ) { + // Create the field accessors + p("public boolean has" + uname + "() {"); + indent(); + p("return bean().has" + uname + "();"); + unindent(); + p("}"); + p(); + p("public java.util.List<" + type + "> get" + uname + "List() {"); + indent(); + p("return bean().get" + uname + "List();"); + unindent(); + p("}"); + p(); + p("public int get" + uname + "Count() {"); + indent(); + p("return bean().get" + uname + "Count();"); + unindent(); + p("}"); + p(); + p("public " + type + " get" + uname + "(int index) {"); + indent(); + p("return bean().get" + uname + "(index);"); + unindent(); + p("}"); + p(); + } else { + // Create the field accessors + p("public boolean has" + uname + "() {"); + indent(); + p("return bean().has" + uname + "();"); + unindent(); + p("}"); + p(); + p("public " + type + " get" + uname + "() {"); + indent(); + p("return bean().get" + uname + "();"); + unindent(); + p("}"); + p(); + } + } + + /** + * @param field + * @param className + */ + private void generateFieldGetterSignatures(FieldDescriptor field) { + String uname = uCamel(field.getName()); + String type = field.getRule()==FieldDescriptor.REPEATED_RULE ? javaCollectionType(field):javaType(field); + boolean repeated = field.getRule()==FieldDescriptor.REPEATED_RULE; + + // Create the fields.. + p("// " + field.getRule() + " " + field.getType() + " " + field.getName() + " = " + field.getTag() + ";"); + if( repeated ) { + // Create the field accessors + p("public boolean has" + uname + "();"); + p("public java.util.List<" + type + "> get" + uname + "List();"); + p("public int get" + uname + "Count();"); + p("public " + type + " get" + uname + "(int index);"); + } else { + // Create the field accessors + p("public boolean has" + uname + "();"); + p("public " + type + " get" + uname + "();"); + } + } + + + /** + * @param field + * @param className + */ + private void generateFieldAccessor(String beanClassName, FieldDescriptor field) { + + String lname = lCamel(field.getName()); + String uname = uCamel(field.getName()); + String type = field.getRule()==FieldDescriptor.REPEATED_RULE ? javaCollectionType(field):javaType(field); + String typeDefault = javaTypeDefault(field); + boolean primitive = field.getTypeDescriptor()==null || field.getTypeDescriptor().isEnum(); + boolean repeated = field.getRule()==FieldDescriptor.REPEATED_RULE; + + // Create the fields.. + p("// " + field.getRule() + " " + field.getType() + " " + field.getName() + " = " + field.getTag() + ";"); + + if( repeated ) { + p("private java.util.List<" + type + "> f_" + lname + ";"); + p(); + + // Create the field accessors + p("public boolean has" + uname + "() {"); + indent(); + p("return bean.f_" + lname + "!=null && !bean.f_" + lname + ".isEmpty();"); + unindent(); + p("}"); + p(); + + p("public java.util.List<" + type + "> get" + uname + "List() {"); + indent(); + p("return bean.f_" + lname + ";"); + unindent(); + p("}"); + p(); + + p("public java.util.List<" + type + "> create" + uname + "List() {"); + indent(); + p("copyCheck();"); + p("if( this.f_" + lname + " == null ) {"); + indent(); + p("this.f_" + lname + " = new java.util.ArrayList<" + type + ">();"); + unindent(); + p("}"); + p("return bean.f_" + lname + ";"); + unindent(); + p("}"); + p(); + + p("public "+beanClassName+" set" + uname + "List(java.util.List<" + type + "> " + lname + ") {"); + indent(); + p("copyCheck();"); + p("this.f_" + lname + " = " + lname + ";"); + p("return this;"); + unindent(); + p("}"); + p(); + + p("public int get" + uname + "Count() {"); + indent(); + p("if( bean.f_" + lname + " == null ) {"); + indent(); + p("return 0;"); + unindent(); + p("}"); + p("return bean.f_" + lname + ".size();"); + unindent(); + p("}"); + p(); + + p("public " + type + " get" + uname + "(int index) {"); + indent(); + p("if( bean.f_" + lname + " == null ) {"); + indent(); + p("return null;"); + unindent(); + p("}"); + p("return bean.f_" + lname + ".get(index);"); + unindent(); + p("}"); + p(); + + p("public "+beanClassName+" set" + uname + "(int index, " + type + " value) {"); + indent(); + p("this.create" + uname + "List().set(index, value);"); + p("return this;"); + unindent(); + p("}"); + p(); + + p("public "+beanClassName+" add" + uname + "(" + type + " value) {"); + indent(); + p("this.create" + uname + "List().add(value);"); + p("return this;"); + unindent(); + p("}"); + p(); + + p("public "+beanClassName+" addAll" + uname + "(java.lang.Iterable collection) {"); + indent(); + p("org.apache.activemq.protobuf.MessageBufferSupport.addAll(collection, this.create" + uname + "List());"); + p("return this;"); + unindent(); + p("}"); + p(); + + p("public void clear" + uname + "() {"); + indent(); + p("copyCheck();"); + p("this.f_" + lname + " = null;"); + unindent(); + p("}"); + p(); + + } else { + + p("private " + type + " f_" + lname + " = "+typeDefault+";"); + if (primitive) { + p("private boolean b_" + lname + ";"); + } + p(); + + // Create the field accessors + p("public boolean has" + uname + "() {"); + indent(); + if (primitive) { + p("return bean.b_" + lname + ";"); + } else { + p("return bean.f_" + lname + "!=null;"); + } + unindent(); + p("}"); + p(); + + p("public " + type + " get" + uname + "() {"); + indent(); + p("return bean.f_" + lname + ";"); + unindent(); + p("}"); + p(); + + p("public "+beanClassName+" set" + uname + "(" + type + " " + lname + ") {"); + indent(); + p("copyCheck();"); + if (primitive) { + if( auto_clear_optional_fields && field.isOptional() ) { + if( field.isStringType() && !"null".equals(typeDefault)) { + p("this.b_" + lname + " = ("+lname+" != "+typeDefault+");"); + } else { + p("this.b_" + lname + " = ("+lname+" != "+typeDefault+");"); + } + } else { + p("this.b_" + lname + " = true;"); + } + } + p("this.f_" + lname + " = " + lname + ";"); + p("return this;"); + unindent(); + p("}"); + p(); + + p("public void clear" + uname + "() {"); + indent(); + p("copyCheck();"); + if (primitive) { + p("this.b_" + lname + " = false;"); + } + p("this.f_" + lname + " = " + typeDefault + ";"); + unindent(); + p("}"); + p(); + } + + } + + private String javaTypeDefault(FieldDescriptor field) { + OptionDescriptor defaultOption = field.getOptions().get("default"); + if( defaultOption!=null ) { + if( field.isStringType() ) { + return asJavaString(defaultOption.getValue()); + } else if( field.getType() == FieldDescriptor.BYTES_TYPE ) { + return "new "+javaType(field)+"("+asJavaString(defaultOption.getValue())+")"; + } else if( field.isInteger32Type() ) { + int v; + if( field.getType() == FieldDescriptor.UINT32_TYPE ) { + v = TextFormat.parseUInt32(defaultOption.getValue()); + } else { + v = TextFormat.parseInt32(defaultOption.getValue()); + } + return ""+v; + } else if( field.isInteger64Type() ) { + long v; + if( field.getType() == FieldDescriptor.UINT64_TYPE ) { + v = TextFormat.parseUInt64(defaultOption.getValue()); + } else { + v = TextFormat.parseInt64(defaultOption.getValue()); + } + return ""+v+"l"; + } else if( field.getType() == FieldDescriptor.DOUBLE_TYPE ) { + double v = Double.valueOf(defaultOption.getValue()); + return ""+v+"d"; + } else if( field.getType() == FieldDescriptor.FLOAT_TYPE ) { + float v = Float.valueOf(defaultOption.getValue()); + return ""+v+"f"; + } else if( field.getType() == FieldDescriptor.BOOL_TYPE ) { + boolean v = Boolean.valueOf(defaultOption.getValue()); + return ""+v; + } else if( field.getTypeDescriptor()!=null && field.getTypeDescriptor().isEnum() ) { + return javaType(field)+"."+defaultOption.getValue(); + } + return defaultOption.getValue(); + } else { + if( field.isNumberType() ) { + return "0"; + } + if( field.getType() == FieldDescriptor.BOOL_TYPE ) { + return "false"; + } + return "null"; + } + } + + static final char HEX_TABLE[] = new char[]{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + private String asJavaString(String value) { + StringBuilder sb = new StringBuilder(value.length()+2); + sb.append("\""); + for (int i = 0; i < value.length(); i++) { + + char b = value.charAt(i); + switch (b) { + // Java does not recognize \a or \v, apparently. + case '\b': sb.append("\\b" ); break; + case '\f': sb.append("\\f" ); break; + case '\n': sb.append("\\n" ); break; + case '\r': sb.append("\\r" ); break; + case '\t': sb.append("\\t" ); break; + case '\\': sb.append("\\\\"); break; + case '\'': sb.append("\\\'"); break; + case '"' : sb.append("\\\""); break; + default: + if (b >= 0x20 && b <'Z') { + sb.append((char) b); + } else { + sb.append("\\u"); + sb.append(HEX_TABLE[(b >>> 12) & 0x0F] ); + sb.append(HEX_TABLE[(b >>> 8) & 0x0F] ); + sb.append(HEX_TABLE[(b >>> 4) & 0x0F] ); + sb.append(HEX_TABLE[b & 0x0F] ); + } + break; + } + + } + sb.append("\""); + return sb.toString(); + } + + private void generateEnum(EnumDescriptor ed) { + String uname = uCamel(ed.getName()); + + String staticOption = "static "; + if( multipleFiles && ed.getParent()==null ) { + staticOption=""; + } + + // TODO Auto-generated method stub + p(); + p("public "+staticOption+"enum " +uname + " {"); + indent(); + + + p(); + int counter=0; + for (EnumFieldDescriptor field : ed.getFields().values()) { + boolean last = counter+1 == ed.getFields().size(); + p(field.getName()+"(\""+field.getName()+"\", "+field.getValue()+")"+(last?";":",")); + counter++; + } + p(); + p("private final String name;"); + p("private final int value;"); + p(); + p("private "+uname+"(String name, int value) {"); + p(" this.name = name;"); + p(" this.value = value;"); + p("}"); + p(); + p("public final int getNumber() {"); + p(" return value;"); + p("}"); + p(); + p("public final String toString() {"); + p(" return name;"); + p("}"); + p(); + p("public static "+uname+" valueOf(int value) {"); + p(" switch (value) {"); + + // It's possible to define multiple ENUM fields with the same value.. + // we only want to put the first one into the switch statement. + HashSet values = new HashSet(); + for (EnumFieldDescriptor field : ed.getFields().values()) { + if( !values.contains(field.getValue()) ) { + p(" case "+field.getValue()+":"); + p(" return "+field.getName()+";"); + values.add(field.getValue()); + } + + } + p(" default:"); + p(" return null;"); + p(" }"); + p("}"); + p(); + + + String createMessage = getOption(ed.getOptions(), "java_create_message", null); + if( "true".equals(createMessage) ) { + + p("public interface "+uname+"Creatable {"); + indent(); + p(""+uname+" to"+uname+"();"); + unindent(); + p("}"); + p(); + + p("public "+uname+"Creatable createBean() {"); + indent(); + p("switch (this) {"); + indent(); + for (EnumFieldDescriptor field : ed.getFields().values()) { + p("case "+field.getName()+":"); + String type = field.getAssociatedType().getName(); + p(" return new "+javaRelatedType(type, "Bean")+"();"); + } + p("default:"); + p(" return null;"); + unindent(); + p("}"); + unindent(); + p("}"); + p(); + + generateParseDelegate(ed, "parseUnframed", "org.apache.activemq.protobuf.Buffer", "org.apache.activemq.protobuf.InvalidProtocolBufferException"); + generateParseDelegate(ed, "parseFramed", "org.apache.activemq.protobuf.Buffer", "org.apache.activemq.protobuf.InvalidProtocolBufferException"); + generateParseDelegate(ed, "parseUnframed", "byte[]", "org.apache.activemq.protobuf.InvalidProtocolBufferException"); + generateParseDelegate(ed, "parseFramed", "byte[]", "org.apache.activemq.protobuf.InvalidProtocolBufferException"); + generateParseDelegate(ed, "parseFramed", "org.apache.activemq.protobuf.CodedInputStream", "org.apache.activemq.protobuf.InvalidProtocolBufferException, java.io.IOException"); + generateParseDelegate(ed, "parseFramed", "java.io.InputStream", "org.apache.activemq.protobuf.InvalidProtocolBufferException, java.io.IOException"); + } + + unindent(); + p("}"); + p(); + } + + private void generateParseDelegate(EnumDescriptor descriptor, String methodName, String inputType, String exceptions) { + p("public org.apache.activemq.protobuf.MessageBuffer " + methodName + "(" + inputType + " data) throws " + exceptions + " {"); + indent(); + p("switch (this) {"); + indent(); + for (EnumFieldDescriptor field : descriptor.getFields().values()) { + p("case "+field.getName()+":"); + String type = constantToUCamelCase(field.getName()); + p(" return "+javaRelatedType(type, "Buffer")+"."+methodName+"(data);"); + } + p("default:"); + p(" return null;"); + unindent(); + p("}"); + unindent(); + p("}"); + p(); + } + + + + private String javaCollectionType(FieldDescriptor field) { + if( field.isInteger32Type() ) { + return "java.lang.Integer"; + } + if( field.isInteger64Type() ) { + return "java.lang.Long"; + } + if( field.getType() == FieldDescriptor.DOUBLE_TYPE ) { + return "java.lang.Double"; + } + if( field.getType() == FieldDescriptor.FLOAT_TYPE ) { + return "java.lang.Float"; + } + if( field.getType() == FieldDescriptor.STRING_TYPE ) { + // TODO: support handling string fields as buffers. +// String override = getOption(field.getOptions(), "java_override_type", null); +// if( "AsciiBuffer".equals(override) ) { +// return "org.apache.activemq.protobuf.AsciiBuffer"; +// } else if( "UTF8Buffer".equals(override) ) { +// return "org.apache.activemq.protobuf.UTF8Buffer"; +// } else if( "Buffer".equals(override) ) { +// return "org.apache.activemq.protobuf.Buffer"; +// } else { + return "java.lang.String"; +// } + } + if( field.getType() == FieldDescriptor.BYTES_TYPE ) { + String override = getOption(field.getOptions(), "java_override_type", null); + if( "AsciiBuffer".equals(override) ) { + return "org.apache.activemq.protobuf.AsciiBuffer"; + } else if( "UTF8Buffer".equals(override) ) { + return "org.apache.activemq.protobuf.UTF8Buffer"; + } else { + return "org.apache.activemq.protobuf.Buffer"; + } + } + if( field.getType() == FieldDescriptor.BOOL_TYPE ) { + return "java.lang.Boolean"; + } + + TypeDescriptor descriptor = field.getTypeDescriptor(); + return javaType(descriptor); + } + + private String javaType(FieldDescriptor field) { + if( field.isInteger32Type() ) { + return "int"; + } + if( field.isInteger64Type() ) { + return "long"; + } + if( field.getType() == FieldDescriptor.DOUBLE_TYPE ) { + return "double"; + } + if( field.getType() == FieldDescriptor.FLOAT_TYPE ) { + return "float"; + } + if( field.getType() == FieldDescriptor.STRING_TYPE ) { + // TODO: support handling string fields as buffers. +// String override = getOption(field.getOptions(), "java_override_type", null); +// if( "AsciiBuffer".equals(override) ) { +// return "org.apache.activemq.protobuf.AsciiBuffer"; +// } else if( "UTF8Buffer".equals(override) ) { +// return "org.apache.activemq.protobuf.UTF8Buffer"; +// } else if( "Buffer".equals(override) ) { +// return "org.apache.activemq.protobuf.Buffer"; +// } else { + return "java.lang.String"; +// } + } + if( field.getType() == FieldDescriptor.BYTES_TYPE ) { + String override = getOption(field.getOptions(), "java_override_type", null); + if( "AsciiBuffer".equals(override) ) { + return "org.apache.activemq.protobuf.AsciiBuffer"; + } else if( "UTF8Buffer".equals(override) ) { + return "org.apache.activemq.protobuf.UTF8Buffer"; + } else { + return "org.apache.activemq.protobuf.Buffer"; + } + } + if( field.getType() == FieldDescriptor.BOOL_TYPE ) { + return "boolean"; + } + + TypeDescriptor descriptor = field.getTypeDescriptor(); + return javaType(descriptor); + } + + private String javaType(TypeDescriptor descriptor) { + ProtoDescriptor p = descriptor.getProtoDescriptor(); + if( p != proto ) { + // Try to keep it short.. + String othePackage = javaPackage(p); + if( equals(othePackage,javaPackage(proto) ) ) { + return javaClassName(p)+"."+descriptor.getQName(); + } + // Use the fully qualified class name. + return othePackage+"."+javaClassName(p)+"."+descriptor.getQName(); + } + return descriptor.getQName(); + } + + private String javaRelatedType(String type, String suffix) { + int ix = type.lastIndexOf("."); + if (ix == -1) { + // type = Foo, result = Foo.FooBean + return type+"."+type+suffix; + } + // type = Foo.Bar, result = Foo.Bar.BarBean + return type+"."+type.substring(ix+1)+suffix; + } + + private boolean equals(String o1, String o2) { + if( o1==o2 ) + return true; + if( o1==null || o2==null ) + return false; + return o1.equals(o2); + } + + private String javaClassName(ProtoDescriptor proto) { + return getOption(proto.getOptions(), "java_outer_classname", uCamel(removeFileExtension(proto.getName()))); + } + + private boolean isMultipleFilesEnabled(ProtoDescriptor proto) { + return "true".equals(getOption(proto.getOptions(), "java_multiple_files", "false")); + } + + + private String javaPackage(ProtoDescriptor proto) { + String name = proto.getPackageName(); + if( name!=null ) { + name = name.replace('-', '.'); + name = name.replace('/', '.'); + } + return getOption(proto.getOptions(), "java_package", name); + } + + + // ---------------------------------------------------------------- + // Internal Helper methods + // ---------------------------------------------------------------- + + private void indent() { + indent++; + } + + private void unindent() { + indent--; + } + + private void p(String line) { + // Indent... + for (int i = 0; i < indent; i++) { + w.print(" "); + } + // Then print. + w.println(line); + } + + private void p() { + w.println(); + } + + private String getOption(Map options, String optionName, String defaultValue) { + OptionDescriptor optionDescriptor = options.get(optionName); + if (optionDescriptor == null) { + return defaultValue; + } + return optionDescriptor.getValue(); + } + + static private String removeFileExtension(String name) { + return name.replaceAll("\\..*", ""); + } + + static private String uCamel(String name) { + boolean upNext=true; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if( Character.isJavaIdentifierPart(c) && Character.isLetterOrDigit(c)) { + if( upNext ) { + c = Character.toUpperCase(c); + upNext=false; + } + sb.append(c); + } else { + upNext=true; + } + } + return sb.toString(); + } + + static private String lCamel(String name) { + if( name == null || name.length()<1 ) + return name; + String uCamel = uCamel(name); + return uCamel.substring(0,1).toLowerCase()+uCamel.substring(1); + } + + + private String constantToUCamelCase(String name) { + boolean upNext=true; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if( Character.isJavaIdentifierPart(c) && Character.isLetterOrDigit(c)) { + if( upNext ) { + c = Character.toUpperCase(c); + upNext=false; + } else { + c = Character.toLowerCase(c); + } + sb.append(c); + } else { + upNext=true; + } + } + return sb.toString(); + } + + + private String constantCase(String name) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if( i!=0 && Character.isUpperCase(c) ) { + sb.append("_"); + } + sb.append(Character.toUpperCase(c)); + } + return sb.toString(); + } + + public File getOut() { + return out; + } + + public void setOut(File outputDirectory) { + this.out = outputDirectory; + } + + public File[] getPath() { + return path; + } + + public void setPath(File[] path) { + this.path = path; + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/CommandLineSupport.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/CommandLineSupport.java new file mode 100644 index 0000000000..1d217ff3ae --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/CommandLineSupport.java @@ -0,0 +1,115 @@ +/** + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler; + +import java.util.ArrayList; + +/** + * Support utility that can be used to set the properties on any object + * using command line arguments. + * + * @author Hiram Chirino + */ +public class CommandLineSupport { + + /** + * Sets the properties of an object given the command line args. + * + * if args contains: --ack-mode=AUTO --url=tcp://localhost:61616 --persistent + * + * then it will try to call the following setters on the target object. + * + * target.setAckMode("AUTO"); + * target.setURL(new URI("tcp://localhost:61616") ); + * target.setPersistent(true); + * + * Notice the the proper conversion for the argument is determined by examining the + * setter argument type. + * + * @param target the object that will have it's properties set + * @param args the command line options + * @return any arguments that are not valid options for the target + */ + static public String[] setOptions(Object target, String []args) { + ArrayList rc = new ArrayList(); + + for (int i = 0; i < args.length; i++) { + if( args[i] == null ) + continue; + + if( args[i].startsWith("--") ) { + + // --options without a specified value are considered boolean flags that are enabled. + String value="true"; + String name = args[i].substring(2); + + // if --option=value case + int p = name.indexOf("="); + if( p > 0 ) { + value = name.substring(p+1); + name = name.substring(0,p); + } + + // name not set, then it's an unrecognized option + if( name.length()==0 ) { + rc.add(args[i]); + continue; + } + + String propName = convertOptionToPropertyName(name); + if( !IntrospectionSupport.setProperty(target, propName, value) ) { + rc.add(args[i]); + continue; + } + } else { + rc.add(args[i]); + } + + } + + String r[] = new String[rc.size()]; + rc.toArray(r); + return r; + } + + /** + * converts strings like: test-enabled to testEnabled + * @param name + * @return + */ + private static String convertOptionToPropertyName(String name) { + String rc=""; + + // Look for '-' and strip and then convert the subsequent char to uppercase + int p = name.indexOf("-"); + while( p > 0 ) { + // strip + rc += name.substring(0, p); + name = name.substring(p+1); + + // can I convert the next char to upper? + if( name.length() >0 ) { + rc += name.substring(0,1).toUpperCase(); + name = name.substring(1); + } + + p = name.indexOf("-"); + } + return rc+name; + } +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/CompilerException.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/CompilerException.java new file mode 100644 index 0000000000..f1c24f39f2 --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/CompilerException.java @@ -0,0 +1,31 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler; + +import java.util.List; + +public class CompilerException extends Exception { + private final List errors; + + public CompilerException(List errors) { + this.errors = errors; + } + + public List getErrors() { + return errors; + } +} \ No newline at end of file diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/EnumDescriptor.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/EnumDescriptor.java new file mode 100644 index 0000000000..3e6e195aab --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/EnumDescriptor.java @@ -0,0 +1,136 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class EnumDescriptor implements TypeDescriptor { + + private String name; + private Map fields= new LinkedHashMap(); + private final ProtoDescriptor protoDescriptor; + private final MessageDescriptor parent; + private Map options = new LinkedHashMap(); + + public EnumDescriptor(ProtoDescriptor protoDescriptor, MessageDescriptor parent) { + this.protoDescriptor = protoDescriptor; + this.parent = parent; + } + + public String getName() { + return name; + } + + public Map getFields() { + return fields; + } + + public void setName(String name) { + this.name = name; + } + + public void setFields(Map fields) { + this.fields = fields; + } + public ProtoDescriptor getProtoDescriptor() { + return protoDescriptor; + } + + private String getOption(Map options, String optionName, String defaultValue) { + OptionDescriptor optionDescriptor = options.get(optionName); + if (optionDescriptor == null) { + return defaultValue; + } + return optionDescriptor.getValue(); + } + + private String constantToUCamelCase(String name) { + boolean upNext=true; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if( Character.isJavaIdentifierPart(c) && Character.isLetterOrDigit(c)) { + if( upNext ) { + c = Character.toUpperCase(c); + upNext=false; + } else { + c = Character.toLowerCase(c); + } + sb.append(c); + } else { + upNext=true; + } + } + return sb.toString(); + } + + public void validate(List errors) { + String createMessage = getOption(getOptions(), "java_create_message", null); + if( "true".equals(createMessage) ) { + for (EnumFieldDescriptor field : getFields().values()) { + String type = constantToUCamelCase(field.getName()); + + TypeDescriptor typeDescriptor=null; + // Find the type def for that guy.. + if( parent!=null ) { + typeDescriptor = parent.getType(type); + } + if( typeDescriptor == null ) { + typeDescriptor = protoDescriptor.getType(type); + } + if( typeDescriptor == null ) { + errors.add("ENUM constant '"+field.getName()+"' did not find expected associated message: "+type); + } else { + field.associate(typeDescriptor); + typeDescriptor.associate(field); + } + } + } + } + + public MessageDescriptor getParent() { + return parent; + } + + public String getQName() { + if( parent==null ) { + return name; + } else { + return parent.getQName()+"."+name; + } + } + + public boolean isEnum() { + return true; + } + + public Map getOptions() { + return options; + } + + public void setOptions(Map options) { + this.options = options; + } + + public void associate(EnumFieldDescriptor desc) { + throw new RuntimeException("not supported."); + } + + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/EnumFieldDescriptor.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/EnumFieldDescriptor.java new file mode 100644 index 0000000000..694492ab2f --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/EnumFieldDescriptor.java @@ -0,0 +1,58 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler; + +public class EnumFieldDescriptor { + + private String name; + private int value; + private final EnumDescriptor parent; + private TypeDescriptor associatedType; + + public EnumFieldDescriptor(EnumDescriptor parent) { + this.parent = parent; + } + + public void setName(String name) { + this.name = name; + } + + public void setValue(int value) { + this.value = value; + } + + public String getName() { + return name; + } + + public int getValue() { + return value; + } + + public EnumDescriptor getParent() { + return parent; + } + + public TypeDescriptor getAssociatedType() { + return associatedType; + } + + public void associate(TypeDescriptor associatedType) { + this.associatedType = associatedType; + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ExtensionsDescriptor.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ExtensionsDescriptor.java new file mode 100644 index 0000000000..7c5f21a676 --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ExtensionsDescriptor.java @@ -0,0 +1,50 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler; + +public class ExtensionsDescriptor { + + private int first; + private int last; + private final MessageDescriptor parent; + + public ExtensionsDescriptor(MessageDescriptor parent) { + this.parent = parent; + } + + public void setFirst(int first) { + this.first = first; + } + + public void setLast(int last) { + this.last = last; + } + + public int getFirst() { + return first; + } + + public int getLast() { + return last; + } + + public MessageDescriptor getParent() { + return parent; + } + + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/FieldDescriptor.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/FieldDescriptor.java new file mode 100644 index 0000000000..3471c413b9 --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/FieldDescriptor.java @@ -0,0 +1,207 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class FieldDescriptor { + + public static final String STRING_TYPE = "string".intern(); + public static final String BOOL_TYPE = "bool".intern(); + public static final String BYTES_TYPE = "bytes".intern(); + public static final String DOUBLE_TYPE = "double".intern(); + public static final String FLOAT_TYPE = "float".intern(); + + public static final String INT32_TYPE = "int32".intern(); + public static final String INT64_TYPE = "int64".intern(); + public static final String UINT32_TYPE = "uint32".intern(); + public static final String UINT64_TYPE = "uint64".intern(); + public static final String SINT32_TYPE = "sint32".intern(); + public static final String SINT64_TYPE = "sint64".intern(); + public static final String FIXED32_TYPE = "fixed32".intern(); + public static final String FIXED64_TYPE = "fixed64".intern(); + public static final String SFIXED32_TYPE = "sfixed32".intern(); + public static final String SFIXED64_TYPE = "sfixed64".intern(); + + public static final String REQUIRED_RULE = "required".intern(); + public static final String OPTIONAL_RULE= "optional".intern(); + public static final String REPEATED_RULE = "repeated".intern(); + + public static final Set INT32_TYPES = new HashSet(); + public static final Set INT64_TYPES = new HashSet(); + public static final Set INTEGER_TYPES = new HashSet(); + public static final Set NUMBER_TYPES = new HashSet(); + public static final Set SCALAR_TYPES = new HashSet(); + + public static final Set SIGNED_TYPES = new HashSet(); + public static final Set UNSIGNED_TYPES = new HashSet(); + + static { + INT32_TYPES.add(INT32_TYPE); + INT32_TYPES.add(UINT32_TYPE); + INT32_TYPES.add(SINT32_TYPE); + INT32_TYPES.add(FIXED32_TYPE); + INT32_TYPES.add(SFIXED32_TYPE); + + INT64_TYPES.add(INT64_TYPE); + INT64_TYPES.add(UINT64_TYPE); + INT64_TYPES.add(SINT64_TYPE); + INT64_TYPES.add(FIXED64_TYPE); + INT64_TYPES.add(SFIXED64_TYPE); + + INTEGER_TYPES.addAll(INT32_TYPES); + INTEGER_TYPES.addAll(INT64_TYPES); + + NUMBER_TYPES.addAll(INTEGER_TYPES); + NUMBER_TYPES.add(DOUBLE_TYPE); + NUMBER_TYPES.add(FLOAT_TYPE); + + SCALAR_TYPES.addAll(NUMBER_TYPES); + SCALAR_TYPES.add(STRING_TYPE); + SCALAR_TYPES.add(BOOL_TYPE); + SCALAR_TYPES.add(BYTES_TYPE); + } + + + private String name; + private String type; + private String rule; + private int tag; + private Map options; + private TypeDescriptor typeDescriptor; + private final MessageDescriptor parent; + private MessageDescriptor group; + + public FieldDescriptor(MessageDescriptor parent) { + this.parent = parent; + } + + public void validate(List errors) { + if( group!=null ) { + typeDescriptor=group; + } + if( !SCALAR_TYPES.contains(type) ) { + // Find the type def for that guy.. + if( typeDescriptor==null ) { + typeDescriptor = parent.getType(type); + } + if( typeDescriptor == null ) { + typeDescriptor = parent.getProtoDescriptor().getType(type); + } + if( typeDescriptor == null ) { + errors.add("Field type not found: "+type); + } + } + } + + public boolean isGroup() { + return group!=null; + } + + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + + public String getRule() { + return rule; + } + public void setRule(String rule) { + this.rule = rule.intern(); + } + + public boolean isOptional() { + return this.rule == OPTIONAL_RULE; + } + public boolean isRequired() { + return this.rule == REQUIRED_RULE; + } + public boolean isRepeated() { + return this.rule == REPEATED_RULE; + } + + public int getTag() { + return tag; + } + public void setTag(int tag) { + this.tag = tag; + } + + public Map getOptions() { + return options; + } + public void setOptions(Map options) { + this.options = options; + } + + public String getType() { + return type; + } + public void setType(String type) { + this.type = type.intern(); + } + + public boolean isMessageType() { + return !SCALAR_TYPES.contains(type); + } + + public boolean isScalarType() { + return SCALAR_TYPES.contains(type); + } + + public boolean isNumberType() { + return NUMBER_TYPES.contains(type); + } + + public boolean isIntegerType() { + return INTEGER_TYPES.contains(type); + } + + public boolean isInteger32Type() { + return INT32_TYPES.contains(type); + } + + public boolean isInteger64Type() { + return INT64_TYPES.contains(type); + } + + public boolean isStringType() { + return type==STRING_TYPE; + } + + public TypeDescriptor getTypeDescriptor() { + return typeDescriptor; + } + + public void setTypeDescriptor(TypeDescriptor typeDescriptor) { + this.typeDescriptor = typeDescriptor; + } + + public MessageDescriptor getGroup() { + return group; + } + public void setGroup(MessageDescriptor group) { + this.group = group; + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/IntrospectionSupport.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/IntrospectionSupport.java new file mode 100755 index 0000000000..68382d063c --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/IntrospectionSupport.java @@ -0,0 +1,324 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler; + +import java.beans.PropertyEditor; +import java.beans.PropertyEditorManager; +import java.io.File; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; +import java.util.Map.Entry; + +/** + * Support class used to do introspection/reflection based setting and getting of properties on a Java Bean. + * + * @author Hiram Chirino + */ +public final class IntrospectionSupport { + + private IntrospectionSupport() { + } + + public static boolean getProperties(Object target, Map props, String optionPrefix) { + + boolean rc = false; + if (target == null) { + throw new IllegalArgumentException("target was null."); + } + if (props == null) { + throw new IllegalArgumentException("props was null."); + } + + if (optionPrefix == null) { + optionPrefix = ""; + } + + Class clazz = target.getClass(); + Method[] methods = clazz.getMethods(); + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + String name = method.getName(); + Class type = method.getReturnType(); + Class params[] = method.getParameterTypes(); + if (name.startsWith("get") && params.length == 0 && type != null && isSettableType(type)) { + + try { + + Object value = method.invoke(target, new Object[] {}); + if (value == null) { + continue; + } + + String strValue = convertToString(value, type); + if (strValue == null) { + continue; + } + + name = name.substring(3, 4).toLowerCase() + name.substring(4); + props.put(optionPrefix + name, strValue); + rc = true; + + } catch (Throwable ignore) { + } + + } + } + + return rc; + } + + public static boolean setProperties(Object target, Map props, String optionPrefix) { + boolean rc = false; + if (target == null) { + throw new IllegalArgumentException("target was null."); + } + if (props == null) { + throw new IllegalArgumentException("props was null."); + } + + for (Iterator iter = props.keySet().iterator(); iter.hasNext();) { + String name = iter.next(); + if (name.startsWith(optionPrefix)) { + Object value = props.get(name); + name = name.substring(optionPrefix.length()); + if (setProperty(target, name, value)) { + iter.remove(); + rc = true; + } + } + } + return rc; + } + + public static Map extractProperties(Map props, String optionPrefix) { + if (props == null) { + throw new IllegalArgumentException("props was null."); + } + + HashMap rc = new HashMap(props.size()); + + for (Iterator iter = props.keySet().iterator(); iter.hasNext();) { + String name = (String)iter.next(); + if (name.startsWith(optionPrefix)) { + Object value = props.get(name); + name = name.substring(optionPrefix.length()); + rc.put(name, value); + iter.remove(); + } + } + + return rc; + } + + public static boolean setProperties(Object target, Map props) { + boolean rc = false; + + if (target == null) { + throw new IllegalArgumentException("target was null."); + } + if (props == null) { + throw new IllegalArgumentException("props was null."); + } + + for (Iterator iter = props.entrySet().iterator(); iter.hasNext();) { + Map.Entry entry = (Entry)iter.next(); + if (setProperty(target, (String)entry.getKey(), entry.getValue())) { + iter.remove(); + rc = true; + } + } + + return rc; + } + + public static boolean setProperty(Object target, String name, Object value) { + try { + Class clazz = target.getClass(); + Method setter = findSetterMethod(clazz, name); + if (setter == null) { + return false; + } + + // If the type is null or it matches the needed type, just use the + // value directly + if (value == null || value.getClass() == setter.getParameterTypes()[0]) { + setter.invoke(target, new Object[] {value}); + } else { + // We need to convert it + setter.invoke(target, new Object[] {convert(value, setter.getParameterTypes()[0])}); + } + return true; + } catch (Throwable ignore) { + return false; + } + } + + private static Object convert(Object value, Class type) throws URISyntaxException { + PropertyEditor editor = PropertyEditorManager.findEditor(type); + if (editor != null) { + editor.setAsText(value.toString()); + return editor.getValue(); + } + if (type == URI.class) { + return new URI(value.toString()); + } + if (type == File.class) { + return new File(value.toString()); + } + if (type == File[].class) { + ArrayList files = new ArrayList(); + StringTokenizer st = new StringTokenizer(value.toString(), ":"); + while(st.hasMoreTokens()) { + String t = st.nextToken(); + if( t!=null && t.trim().length()>0 ) { + files.add(new File(t.trim())); + } + } + File rc[] = new File[files.size()]; + files.toArray(rc); + return rc; + } + return null; + } + + private static String convertToString(Object value, Class type) throws URISyntaxException { + PropertyEditor editor = PropertyEditorManager.findEditor(type); + if (editor != null) { + editor.setValue(value); + return editor.getAsText(); + } + if (type == URI.class) { + return ((URI)value).toString(); + } + return null; + } + + private static Method findSetterMethod(Class clazz, String name) { + // Build the method name. + name = "set" + name.substring(0, 1).toUpperCase() + name.substring(1); + Method[] methods = clazz.getMethods(); + for (int i = 0; i < methods.length; i++) { + Method method = methods[i]; + Class params[] = method.getParameterTypes(); + if (method.getName().equals(name) && params.length == 1 && isSettableType(params[0])) { + return method; + } + } + return null; + } + + private static boolean isSettableType(Class clazz) { + if (PropertyEditorManager.findEditor(clazz) != null) { + return true; + } + if (clazz == URI.class) { + return true; + } + if (clazz == File.class) { + return true; + } + if (clazz == File[].class) { + return true; + } + if (clazz == Boolean.class) { + return true; + } + return false; + } + + public static String toString(Object target) { + return toString(target, Object.class); + } + + public static String toString(Object target, Class stopClass) { + LinkedHashMap map = new LinkedHashMap(); + addFields(target, target.getClass(), stopClass, map); + StringBuffer buffer = new StringBuffer(simpleName(target.getClass())); + buffer.append(" {"); + Set entrySet = map.entrySet(); + boolean first = true; + for (Iterator iter = entrySet.iterator(); iter.hasNext();) { + Map.Entry entry = (Map.Entry)iter.next(); + if (first) { + first = false; + } else { + buffer.append(", "); + } + buffer.append(entry.getKey()); + buffer.append(" = "); + appendToString(buffer, entry.getValue()); + } + buffer.append("}"); + return buffer.toString(); + } + + protected static void appendToString(StringBuffer buffer, Object value) { + buffer.append(value); + } + + public static String simpleName(Class clazz) { + String name = clazz.getName(); + int p = name.lastIndexOf("."); + if (p >= 0) { + name = name.substring(p + 1); + } + return name; + } + + private static void addFields(Object target, Class startClass, Class stopClass, LinkedHashMap map) { + + if (startClass != stopClass) { + addFields(target, startClass.getSuperclass(), stopClass, map); + } + + Field[] fields = startClass.getDeclaredFields(); + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers()) + || Modifier.isPrivate(field.getModifiers())) { + continue; + } + + try { + field.setAccessible(true); + Object o = field.get(target); + if (o != null && o.getClass().isArray()) { + try { + o = Arrays.asList((Object[])o); + } catch (Throwable e) { + } + } + map.put(field.getName(), o); + } catch (Throwable e) { + e.printStackTrace(); + } + } + + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/JavaGenerator.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/JavaGenerator.java new file mode 100644 index 0000000000..e33929ff48 --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/JavaGenerator.java @@ -0,0 +1,1716 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler; + +import static org.apache.activemq.protobuf.WireFormat.WIRETYPE_FIXED32; +import static org.apache.activemq.protobuf.WireFormat.WIRETYPE_FIXED64; +import static org.apache.activemq.protobuf.WireFormat.WIRETYPE_LENGTH_DELIMITED; +import static org.apache.activemq.protobuf.WireFormat.WIRETYPE_START_GROUP; +import static org.apache.activemq.protobuf.WireFormat.WIRETYPE_VARINT; +import static org.apache.activemq.protobuf.WireFormat.makeTag; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.StringTokenizer; + +import org.apache.activemq.protobuf.Buffer; +import org.apache.activemq.protobuf.compiler.parser.ParseException; +import org.apache.activemq.protobuf.compiler.parser.ProtoParser; + +public class JavaGenerator { + + private File out = new File("."); + private File[] path = new File[]{new File(".")}; + + private ProtoDescriptor proto; + private String javaPackage; + private String outerClassName; + private PrintWriter w; + private int indent; + private ArrayList errors = new ArrayList(); + private boolean multipleFiles; + private boolean deferredDecode; + private boolean auto_clear_optional_fields; + + public static void main(String[] args) { + + JavaGenerator generator = new JavaGenerator(); + args = CommandLineSupport.setOptions(generator, args); + + if (args.length == 0) { + System.out.println("No proto files specified."); + } + for (int i = 0; i < args.length; i++) { + try { + System.out.println("Compiling: "+args[i]); + generator.compile(new File(args[i])); + } catch (CompilerException e) { + System.out.println("Protocol Buffer Compiler failed with the following error(s):"); + for (String error : e.getErrors() ) { + System.out.println(""); + System.out.println(error); + } + System.out.println(""); + System.out.println("Compile failed. For more details see error messages listed above."); + return; + } + } + + } + + interface Closure { + void execute() throws CompilerException; + } + + public void compile(File file) throws CompilerException { + + // Parse the proto file + FileInputStream is=null; + try { + is = new FileInputStream(file); + ProtoParser parser = new ProtoParser(is); + proto = parser.ProtoDescriptor(); + proto.setName(file.getName()); + loadImports(proto, file.getParentFile()); + proto.validate(errors); + } catch (FileNotFoundException e) { + errors.add("Failed to open: "+file.getPath()+":"+e.getMessage()); + } catch (ParseException e) { + errors.add("Failed to parse: "+file.getPath()+":"+e.getMessage()); + } finally { + try { is.close(); } catch (Throwable ignore){} + } + + if (!errors.isEmpty()) { + throw new CompilerException(errors); + } + + // Load the options.. + javaPackage = javaPackage(proto); + outerClassName = javaClassName(proto); +// optimizeFor = getOption(proto.getOptions(), "optimize_for", "SPEED"); + multipleFiles = isMultipleFilesEnabled(proto); + deferredDecode = Boolean.parseBoolean(getOption(proto.getOptions(), "deferred_decode", "false")); + auto_clear_optional_fields = Boolean.parseBoolean(getOption(proto.getOptions(), "auto_clear_optional_fields", "false")); + + if( multipleFiles ) { + generateProtoFile(); + } else { + writeFile(outerClassName, new Closure(){ + public void execute() throws CompilerException { + generateProtoFile(); + } + }); + } + + if (!errors.isEmpty()) { + throw new CompilerException(errors); + } + + } + + private void writeFile(String className, Closure closure) throws CompilerException { + PrintWriter oldWriter = w; + // Figure out the java file name.. + File outputFile = out; + if (javaPackage != null) { + String packagePath = javaPackage.replace('.', '/'); + outputFile = new File(outputFile, packagePath); + } + outputFile = new File(outputFile, className + ".java"); + + // Start writing the output file.. + outputFile.getParentFile().mkdirs(); + + FileOutputStream fos=null; + try { + fos = new FileOutputStream(outputFile); + w = new PrintWriter(fos); + closure.execute(); + w.flush(); + } catch (FileNotFoundException e) { + errors.add("Failed to write to: "+outputFile.getPath()+":"+e.getMessage()); + } finally { + try { fos.close(); } catch (Throwable ignore){} + w = oldWriter; + } + } + + private void loadImports(ProtoDescriptor proto, File protoDir) { + LinkedHashMap children = new LinkedHashMap(); + for (String imp : proto.getImports()) { + File file = new File(protoDir, imp); + for (int i = 0; i < path.length && !file.exists(); i++) { + file = new File(path[i], imp); + } + if ( !file.exists() ) { + errors.add("Cannot load import: "+imp); + } + + FileInputStream is=null; + try { + is = new FileInputStream(file); + ProtoParser parser = new ProtoParser(is); + ProtoDescriptor child = parser.ProtoDescriptor(); + child.setName(file.getName()); + loadImports(child, file.getParentFile()); + children.put(imp, child); + } catch (ParseException e) { + errors.add("Failed to parse: "+file.getPath()+":"+e.getMessage()); + } catch (FileNotFoundException e) { + errors.add("Failed to open: "+file.getPath()+":"+e.getMessage()); + } finally { + try { is.close(); } catch (Throwable ignore){} + } + } + proto.setImportProtoDescriptors(children); + } + + + private void generateProtoFile() throws CompilerException { + if( multipleFiles ) { + for (EnumDescriptor value : proto.getEnums().values()) { + final EnumDescriptor o = value; + String className = uCamel(o.getName()); + writeFile(className, new Closure(){ + public void execute() throws CompilerException { + generateFileHeader(); + generateEnum(o); + } + }); + } + for (MessageDescriptor value : proto.getMessages().values()) { + final MessageDescriptor o = value; + String className = uCamel(o.getName()); + writeFile(className, new Closure(){ + public void execute() throws CompilerException { + generateFileHeader(); + generateMessageBean(o); + } + }); + } + + } else { + generateFileHeader(); + + p("public class " + outerClassName + " {"); + indent(); + + for (EnumDescriptor enumType : proto.getEnums().values()) { + generateEnum(enumType); + } + for (MessageDescriptor m : proto.getMessages().values()) { + generateMessageBean(m); + } + + unindent(); + p("}"); + } + } + + private void generateFileHeader() { + p("//"); + p("// Generated by protoc, do not edit by hand."); + p("//"); + if (javaPackage != null) { + p("package " + javaPackage + ";"); + p(""); + } + } + + private void generateMessageBean(MessageDescriptor m) { + + String className = uCamel(m.getName()); + p(); + + String staticOption = "static "; + if( multipleFiles && m.getParent()==null ) { + staticOption=""; + } + + String javaImplements = getOption(m.getOptions(), "java_implments", null); + + String implementsExpression = ""; + if( javaImplements!=null ) { + implementsExpression = "implements "+javaImplements+" "; + } + + String baseClass = "org.apache.activemq.protobuf.BaseMessage"; + if( deferredDecode ) { + baseClass = "org.apache.activemq.protobuf.DeferredDecodeMessage"; + } + if( m.getBaseType()!=null ) { + baseClass = javaType(m.getBaseType())+"Base"; + } + + p(staticOption+"public final class " + className + " extends "+className+"Base<"+className+"> "+implementsExpression+"{"); + p(); + + indent(); + + for (EnumDescriptor enumType : m.getEnums().values()) { + generateEnum(enumType); + } + + // Generate the Nested Messages. + for (MessageDescriptor subMessage : m.getMessages().values()) { + generateMessageBean(subMessage); + } + + // Generate the Group Messages + for (FieldDescriptor field : m.getFields().values()) { + if( isInBaseClass(m, field) ) { + continue; + } + if( field.isGroup() ) { + generateMessageBean(field.getGroup()); + } + } + + + generateMethodAssertInitialized(m, className); + + generateMethodClear(m); + + p("public "+className+" clone() {"); + p(" return new "+className+"().mergeFrom(this);"); + p("}"); + p(); + + generateMethodMergeFromBean(m, className); + + generateMethodSerializedSize(m); + + generateMethodMergeFromStream(m, className); + + generateMethodWriteTo(m); + + generateMethodParseFrom(m, className); + + generateMethodToString(m); + + generateMethodVisitor(m); + + generateMethodType(m, className); + + generateMethodEquals(m, className); + + unindent(); + p("}"); + p(); + + p(staticOption+"abstract class " + className + "Base extends "+baseClass+" {"); + p(); + indent(); + + // Generate the field accessors.. + for (FieldDescriptor field : m.getFields().values()) { + if( isInBaseClass(m, field) ) { + continue; + } + generateFieldAccessor(field); + } + + unindent(); + p("}"); + p(); + } + + private boolean isInBaseClass(MessageDescriptor m, FieldDescriptor field) { + if( m.getBaseType() ==null ) + return false; + return m.getBaseType().getFields().containsKey(field.getName()); + } + + /** + * If the java_visitor message option is set, then this method generates a visitor method. The option + * speifiies the class name of the visitor and optionally the return value and exceptions thrown by the visitor. + * + * Examples: + * + * option java_visitor = "org.apache.kahadb.store.Visitor"; + * generates: + * public void visit(org.apache.kahadb.store.Visitor visitor) { + * visitor.visit(this); + * } + * + * option java_visitor = "org.apache.kahadb.store.Visitor:int:java.io.IOException"; + * generates: + * public int visit(org.apache.kahadb.store.Visitor visitor) throws java.io.IOException { + * return visitor.visit(this); + * } + * + * @param m + */ + private void generateMethodVisitor(MessageDescriptor m) { + String javaVisitor = getOption(m.getOptions(), "java_visitor", null); + if( javaVisitor!=null ) { + String returns = "void"; + String throwsException = null; + + StringTokenizer st = new StringTokenizer(javaVisitor, ":"); + String vistorClass = st.nextToken(); + if( st.hasMoreTokens() ) { + returns = st.nextToken(); + } + if( st.hasMoreTokens() ) { + throwsException = st.nextToken(); + } + + String throwsClause = ""; + if( throwsException!=null ) { + throwsClause = "throws "+throwsException+" "; + } + + p("public "+returns+" visit("+vistorClass+" visitor) "+throwsClause+ "{"); + indent(); + if( "void".equals(returns) ) { + p("visitor.visit(this);"); + } else { + p("return visitor.visit(this);"); + } + unindent(); + p("}"); + p(); + } + } + + private void generateMethodType(MessageDescriptor m, String className) { + String typeEnum = getOption(m.getOptions(), "java_type_method", null); + if( typeEnum!=null ) { + + TypeDescriptor typeDescriptor = m.getType(typeEnum); + if( typeDescriptor == null ) { + typeDescriptor = m.getProtoDescriptor().getType(typeEnum); + } + if( typeDescriptor == null || !typeDescriptor.isEnum() ) { + errors.add("The java_type_method option on the "+m.getName()+" message does not point to valid enum type"); + return; + } + + + String constant = constantCase(className); + EnumDescriptor enumDescriptor = (EnumDescriptor)typeDescriptor; + if( enumDescriptor.getFields().get(constant) == null ) { + errors.add("The java_type_method option on the "+m.getName()+" message does not points to the "+typeEnum+" enum but it does not have an entry for "+constant); + } + + String type = javaType(typeDescriptor); + + p("public "+type+" type() {"); + indent(); + p("return "+type+"."+constant+";"); + unindent(); + p("}"); + p(); + } + } + + private void generateMethodParseFrom(MessageDescriptor m, String className) { + + String postMergeProcessing = ".checktInitialized()"; + if( deferredDecode ) { + postMergeProcessing=""; + } + + p("public static "+className+" parseUnframed(org.apache.activemq.protobuf.CodedInputStream data) throws org.apache.activemq.protobuf.InvalidProtocolBufferException, java.io.IOException {"); + indent(); + p("return new "+className+"().mergeUnframed(data)"+postMergeProcessing+";"); + unindent(); + p("}"); + p(); + + p("public static "+className+" parseUnframed(org.apache.activemq.protobuf.Buffer data) throws org.apache.activemq.protobuf.InvalidProtocolBufferException {"); + indent(); + p("return new "+className+"().mergeUnframed(data)"+postMergeProcessing+";"); + unindent(); + p("}"); + p(); + + p("public static "+className+" parseUnframed(byte[] data) throws org.apache.activemq.protobuf.InvalidProtocolBufferException {"); + indent(); + p("return new "+className+"().mergeUnframed(data)"+postMergeProcessing+";"); + unindent(); + p("}"); + p(); + + p("public static "+className+" parseUnframed(java.io.InputStream data) throws org.apache.activemq.protobuf.InvalidProtocolBufferException, java.io.IOException {"); + indent(); + p("return new "+className+"().mergeUnframed(data)"+postMergeProcessing+";"); + unindent(); + p("}"); + p(); + + p("public static "+className+" parseFramed(org.apache.activemq.protobuf.CodedInputStream data) throws org.apache.activemq.protobuf.InvalidProtocolBufferException, java.io.IOException {"); + indent(); + p("return new "+className+"().mergeFramed(data)"+postMergeProcessing+";"); + unindent(); + p("}"); + p(); + + p("public static "+className+" parseFramed(org.apache.activemq.protobuf.Buffer data) throws org.apache.activemq.protobuf.InvalidProtocolBufferException {"); + indent(); + p("return new "+className+"().mergeFramed(data)"+postMergeProcessing+";"); + unindent(); + p("}"); + p(); + + p("public static "+className+" parseFramed(byte[] data) throws org.apache.activemq.protobuf.InvalidProtocolBufferException {"); + indent(); + p("return new "+className+"().mergeFramed(data)"+postMergeProcessing+";"); + unindent(); + p("}"); + p(); + + p("public static "+className+" parseFramed(java.io.InputStream data) throws org.apache.activemq.protobuf.InvalidProtocolBufferException, java.io.IOException {"); + indent(); + p("return new "+className+"().mergeFramed(data)"+postMergeProcessing+";"); + unindent(); + p("}"); + p(); + } + + private void generateMethodEquals(MessageDescriptor m, String className) { + p("public boolean equals(Object obj) {"); + indent(); + p("if( obj==this )"); + p(" return true;"); + p(""); + p("if( obj==null || obj.getClass()!="+className+".class )"); + p(" return false;"); + p(""); + p("return equals(("+className+")obj);"); + unindent(); + p("}"); + p(""); + + p("public boolean equals("+className+" obj) {"); + indent(); + if( deferredDecode ) { + p("return toUnframedBuffer().equals(obj.toUnframedBuffer());"); + } else { + for (FieldDescriptor field : m.getFields().values()) { + String uname = uCamel(field.getName()); + String getterMethod="get"+uname+"()"; + String hasMethod = "has"+uname+"()"; + + if( field.getRule() == FieldDescriptor.REPEATED_RULE ) { + getterMethod = "get"+uname+"List()"; + } + + p("if ("+hasMethod+" ^ obj."+hasMethod+" ) "); + p(" return false;"); + + + + if( field.getRule() != FieldDescriptor.REPEATED_RULE && (field.isNumberType() || field.getType()==FieldDescriptor.BOOL_TYPE) ) { + p("if ("+hasMethod+" && ( "+getterMethod+"!=obj."+getterMethod+" ))"); + } else { + p("if ("+hasMethod+" && ( !"+getterMethod+".equals(obj."+getterMethod+") ))"); + } + p(" return false;"); + } + p("return true;"); + } + unindent(); + p("}"); + p(""); + p("public int hashCode() {"); + indent(); + int hc = className.hashCode(); + if( deferredDecode ) { + p("return "+hc+" ^ toUnframedBuffer().hashCode();"); + } else { + p("int rc="+hc+";"); + int counter=0; + for (FieldDescriptor field : m.getFields().values()) { + counter++; + + String uname = uCamel(field.getName()); + String getterMethod="get"+uname+"()"; + String hasMethod = "has"+uname+"()"; + + if( field.getRule() == FieldDescriptor.REPEATED_RULE ) { + getterMethod = "get"+uname+"List()"; + } + + p("if ("+hasMethod+") {"); + indent(); + + if( field.getRule() == FieldDescriptor.REPEATED_RULE ) { + p("rc ^= ( "+uname.hashCode()+"^"+getterMethod+".hashCode() );"); + } else if( field.isInteger32Type() ) { + p("rc ^= ( "+uname.hashCode()+"^"+getterMethod+" );"); + } else if( field.isInteger64Type() ) { + p("rc ^= ( "+uname.hashCode()+"^(new Long("+getterMethod+")).hashCode() );"); + } else if( field.getType()==FieldDescriptor.DOUBLE_TYPE ) { + p("rc ^= ( "+uname.hashCode()+"^(new Double("+getterMethod+")).hashCode() );"); + } else if( field.getType()==FieldDescriptor.FLOAT_TYPE ) { + p("rc ^= ( "+uname.hashCode()+"^(new Double("+getterMethod+")).hashCode() );"); + } else if( field.getType()==FieldDescriptor.BOOL_TYPE ) { + p("rc ^= ( "+uname.hashCode()+"^ ("+getterMethod+"? "+counter+":-"+counter+") );"); + } else { + p("rc ^= ( "+uname.hashCode()+"^"+getterMethod+".hashCode() );"); + } + + unindent(); + p("}"); + + } + p("return rc;"); + } + unindent(); + p("}"); + p(""); + } + + /** + * @param m + */ + private void generateMethodSerializedSize(MessageDescriptor m) { + p("public int serializedSizeUnframed() {"); + indent(); + if( deferredDecode ) { + p("if (encodedForm != null) {"); + indent(); + p("return encodedForm.length;"); + unindent(); + p("}"); + } + p("if (memoizedSerializedSize != -1)"); + p(" return memoizedSerializedSize;"); + p(); + p("int size = 0;"); + for (FieldDescriptor field : m.getFields().values()) { + + String uname = uCamel(field.getName()); + String getter="get"+uname+"()"; + String type = javaType(field); + p("if (has"+uname+"()) {"); + indent(); + + if( field.getRule() == FieldDescriptor.REPEATED_RULE ) { + p("for ("+type+" i : get"+uname+"List()) {"); + indent(); + getter = "i"; + } + + if( field.getType()==FieldDescriptor.STRING_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeStringSize("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.BYTES_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeBytesSize("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.BOOL_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeBoolSize("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.DOUBLE_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeDoubleSize("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.FLOAT_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeFloatSize("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.INT32_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeInt32Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.INT64_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeInt64Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.SINT32_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeSInt32Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.SINT64_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeSInt64Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.UINT32_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeUInt32Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.UINT64_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeUInt64Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.FIXED32_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeFixed32Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.FIXED64_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeFixed64Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.SFIXED32_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeSFixed32Size("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.SFIXED64_TYPE ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeSFixed64Size("+field.getTag()+", "+getter+");"); + } else if( field.getTypeDescriptor().isEnum() ) { + p("size += org.apache.activemq.protobuf.CodedOutputStream.computeEnumSize("+field.getTag()+", "+getter+".getNumber());"); + } else if ( field.getGroup()!=null ) { + p("size += computeGroupSize("+field.getTag()+", "+getter+");"); + } else { + p("size += computeMessageSize("+field.getTag()+", "+getter+");"); + } + if( field.getRule() == FieldDescriptor.REPEATED_RULE ) { + unindent(); + p("}"); + } + //TODO: finish this up. + unindent(); + p("}"); + + } + // TODO: handle unknown fields + // size += getUnknownFields().getSerializedSize();"); + p("memoizedSerializedSize = size;"); + p("return size;"); + unindent(); + p("}"); + p(); + } + + /** + * @param m + */ + private void generateMethodWriteTo(MessageDescriptor m) { + p("public void writeUnframed(org.apache.activemq.protobuf.CodedOutputStream output) throws java.io.IOException {"); + indent(); + + if( deferredDecode ) { + p("if (encodedForm == null) {"); + indent(); + p("int size = serializedSizeUnframed();"); + p("encodedForm = output.getNextBuffer(size);"); + p("org.apache.activemq.protobuf.CodedOutputStream original=null;"); + p("if( encodedForm == null ) {"); + indent(); + p("encodedForm = new org.apache.activemq.protobuf.Buffer(new byte[size]);"); + p("original = output;"); + p("output = new org.apache.activemq.protobuf.CodedOutputStream(encodedForm);"); + unindent(); + p("}"); + } + + + for (FieldDescriptor field : m.getFields().values()) { + String uname = uCamel(field.getName()); + String getter="get"+uname+"()"; + String type = javaType(field); + p("if (has"+uname+"()) {"); + indent(); + + if( field.getRule() == FieldDescriptor.REPEATED_RULE ) { + p("for ("+type+" i : get"+uname+"List()) {"); + indent(); + getter = "i"; + } + + if( field.getType()==FieldDescriptor.STRING_TYPE ) { + p("output.writeString("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.BYTES_TYPE ) { + p("output.writeBytes("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.BOOL_TYPE ) { + p("output.writeBool("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.DOUBLE_TYPE ) { + p("output.writeDouble("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.FLOAT_TYPE ) { + p("output.writeFloat("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.INT32_TYPE ) { + p("output.writeInt32("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.INT64_TYPE ) { + p("output.writeInt64("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.SINT32_TYPE ) { + p("output.writeSInt32("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.SINT64_TYPE ) { + p("output.writeSInt64("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.UINT32_TYPE ) { + p("output.writeUInt32("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.UINT64_TYPE ) { + p("output.writeUInt64("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.FIXED32_TYPE ) { + p("output.writeFixed32("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.FIXED64_TYPE ) { + p("output.writeFixed64("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.SFIXED32_TYPE ) { + p("output.writeSFixed32("+field.getTag()+", "+getter+");"); + } else if( field.getType()==FieldDescriptor.SFIXED64_TYPE ) { + p("output.writeSFixed64("+field.getTag()+", "+getter+");"); + } else if( field.getTypeDescriptor().isEnum() ) { + p("output.writeEnum("+field.getTag()+", "+getter+".getNumber());"); + } else if ( field.getGroup()!=null ) { + p("writeGroup(output, "+field.getTag()+", "+getter+");"); + } else { + p("writeMessage(output, "+field.getTag()+", "+getter+");"); + } + + if( field.getRule() == FieldDescriptor.REPEATED_RULE ) { + unindent(); + p("}"); + } + + unindent(); + p("}"); + } + + if( deferredDecode ) { + p("if( original !=null ) {"); + indent(); + p("output.checkNoSpaceLeft();"); + p("output = original;"); + p("output.writeRawBytes(encodedForm);"); + unindent(); + p("}"); + unindent(); + p("} else {"); + indent(); + p("output.writeRawBytes(encodedForm);"); + unindent(); + p("}"); + } + + unindent(); + p("}"); + p(); + } + + /** + * @param m + * @param className + */ + private void generateMethodMergeFromStream(MessageDescriptor m, String className) { + p("public "+className+" mergeUnframed(org.apache.activemq.protobuf.CodedInputStream input) throws java.io.IOException {"); + indent(); + { + p("while (true) {"); + indent(); + { + p("int tag = input.readTag();"); + p("if ((tag & 0x07) == 4) {"); + p(" return this;"); + p("}"); + + p("switch (tag) {"); + p("case 0:"); + p(" return this;"); + p("default: {"); + + p(" break;"); + p("}"); + + for (FieldDescriptor field : m.getFields().values()) { + String uname = uCamel(field.getName()); + String setter = "set" + uname; + boolean repeated = field.getRule() == FieldDescriptor.REPEATED_RULE; + if (repeated) { + setter = "get" + uname + "List().add"; + } + if (field.getType() == FieldDescriptor.STRING_TYPE) { + p("case " + + makeTag(field.getTag(), + WIRETYPE_LENGTH_DELIMITED) + ":"); + indent(); + p(setter + "(input.readString());"); + } else if (field.getType() == FieldDescriptor.BYTES_TYPE) { + p("case " + + makeTag(field.getTag(), + WIRETYPE_LENGTH_DELIMITED) + ":"); + indent(); + p(setter + "(input.readBytes());"); + } else if (field.getType() == FieldDescriptor.BOOL_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_VARINT) + + ":"); + indent(); + p(setter + "(input.readBool());"); + } else if (field.getType() == FieldDescriptor.DOUBLE_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_FIXED64) + + ":"); + indent(); + p(setter + "(input.readDouble());"); + } else if (field.getType() == FieldDescriptor.FLOAT_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_FIXED32) + + ":"); + indent(); + p(setter + "(input.readFloat());"); + } else if (field.getType() == FieldDescriptor.INT32_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_VARINT) + + ":"); + indent(); + p(setter + "(input.readInt32());"); + } else if (field.getType() == FieldDescriptor.INT64_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_VARINT) + + ":"); + indent(); + p(setter + "(input.readInt64());"); + } else if (field.getType() == FieldDescriptor.SINT32_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_VARINT) + + ":"); + indent(); + p(setter + "(input.readSInt32());"); + } else if (field.getType() == FieldDescriptor.SINT64_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_VARINT) + + ":"); + indent(); + p(setter + "(input.readSInt64());"); + } else if (field.getType() == FieldDescriptor.UINT32_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_VARINT) + + ":"); + indent(); + p(setter + "(input.readUInt32());"); + } else if (field.getType() == FieldDescriptor.UINT64_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_VARINT) + + ":"); + indent(); + p(setter + "(input.readUInt64());"); + } else if (field.getType() == FieldDescriptor.FIXED32_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_FIXED32) + + ":"); + indent(); + p(setter + "(input.readFixed32());"); + } else if (field.getType() == FieldDescriptor.FIXED64_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_FIXED64) + + ":"); + indent(); + p(setter + "(input.readFixed64());"); + } else if (field.getType() == FieldDescriptor.SFIXED32_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_FIXED32) + + ":"); + indent(); + p(setter + "(input.readSFixed32());"); + } else if (field.getType() == FieldDescriptor.SFIXED64_TYPE) { + p("case " + makeTag(field.getTag(), WIRETYPE_FIXED64) + + ":"); + indent(); + p(setter + "(input.readSFixed64());"); + } else if (field.getTypeDescriptor().isEnum()) { + p("case " + makeTag(field.getTag(), WIRETYPE_VARINT) + + ":"); + indent(); + String type = javaType(field); + p("{"); + indent(); + p("int t = input.readEnum();"); + p("" + type + " value = " + type + ".valueOf(t);"); + p("if( value !=null ) {"); + indent(); + p(setter + "(value);"); + unindent(); + p("}"); + // TODO: else store it as an known + + unindent(); + p("}"); + + } else if (field.getGroup() != null) { + p("case " + + makeTag(field.getTag(), WIRETYPE_START_GROUP) + + ":"); + indent(); + String type = javaType(field); + if (repeated) { + p(setter + "(readGroup(input, " + field.getTag() + + ", new " + type + "()));"); + } else { + p("if (has" + uname + "()) {"); + indent(); + p("readGroup(input, " + field.getTag() + ", get" + + uname + "());"); + unindent(); + p("} else {"); + indent(); + p(setter + "(readGroup(input, " + field.getTag() + + ",new " + type + "()));"); + unindent(); + p("}"); + } + p(""); + } else { + p("case " + + makeTag(field.getTag(), + WIRETYPE_LENGTH_DELIMITED) + ":"); + indent(); + String type = javaType(field); + if (repeated) { + p(setter + "(new " + type + + "().mergeFramed(input));"); + } else { + p("if (has" + uname + "()) {"); + indent(); + p("get" + uname + "().mergeFramed(input);"); + unindent(); + p("} else {"); + indent(); + p(setter + "(new " + type + + "().mergeFramed(input));"); + unindent(); + p("}"); + } + } + p("break;"); + unindent(); + } + p("}"); + } + unindent(); + p("}"); + } + unindent(); + p("}"); + } + + /** + * @param m + * @param className + */ + private void generateMethodMergeFromBean(MessageDescriptor m, String className) { + p("public "+className+" mergeFrom("+className+" other) {"); + indent(); + for (FieldDescriptor field : m.getFields().values()) { + String uname = uCamel(field.getName()); + p("if (other.has"+uname+"()) {"); + indent(); + + if( field.isScalarType() || field.getTypeDescriptor().isEnum() ) { + if( field.isRepeated() ) { + p("get"+uname+"List().addAll(other.get"+uname+"List());"); + } else { + p("set"+uname+"(other.get"+uname+"());"); + } + } else { + + String type = javaType(field); + // It's complex type... + if( field.isRepeated() ) { + p("for("+type+" element: other.get"+uname+"List() ) {"); + indent(); + p("get"+uname+"List().add(element.clone());"); + unindent(); + p("}"); + } else { + p("if (has"+uname+"()) {"); + indent(); + p("get"+uname+"().mergeFrom(other.get"+uname+"());"); + unindent(); + p("} else {"); + indent(); + p("set"+uname+"(other.get"+uname+"().clone());"); + unindent(); + p("}"); + } + } + unindent(); + p("}"); + } + p("return this;"); + unindent(); + p("}"); + p(); + } + + /** + * @param m + */ + private void generateMethodClear(MessageDescriptor m) { + p("public void clear() {"); + indent(); + p("super.clear();"); + for (FieldDescriptor field : m.getFields().values()) { + String uname = uCamel(field.getName()); + p("clear" + uname + "();"); + } + unindent(); + p("}"); + p(); + } + + private void generateMethodAssertInitialized(MessageDescriptor m, String className) { + + p("public java.util.ArrayList missingFields() {"); + indent(); + p("java.util.ArrayList missingFields = super.missingFields();"); + + for (FieldDescriptor field : m.getFields().values()) { + String uname = uCamel(field.getName()); + if( field.isRequired() ) { + p("if( !has" + uname + "() ) {"); + indent(); + p("missingFields.add(\""+field.getName()+"\");"); + unindent(); + p("}"); + } + } + + if( !deferredDecode ) { + for (FieldDescriptor field : m.getFields().values()) { + if( field.getTypeDescriptor()!=null && !field.getTypeDescriptor().isEnum()) { + String uname = uCamel(field.getName()); + p("if( has" + uname + "() ) {"); + indent(); + if( !field.isRepeated() ) { + p("try {"); + indent(); + p("get" + uname + "().assertInitialized();"); + unindent(); + p("} catch (org.apache.activemq.protobuf.UninitializedMessageException e){"); + indent(); + p("missingFields.addAll(prefix(e.getMissingFields(),\""+field.getName()+".\"));"); + unindent(); + p("}"); + } else { + String type = javaCollectionType(field); + p("java.util.List<"+type+"> l = get" + uname + "List();"); + p("for( int i=0; i < l.size(); i++ ) {"); + indent(); + p("try {"); + indent(); + p("l.get(i).assertInitialized();"); + unindent(); + p("} catch (org.apache.activemq.protobuf.UninitializedMessageException e){"); + indent(); + p("missingFields.addAll(prefix(e.getMissingFields(),\""+field.getName()+"[\"+i+\"]\"));"); + unindent(); + p("}"); + unindent(); + p("}"); + } + unindent(); + p("}"); + } + } + } + p("return missingFields;"); + unindent(); + p("}"); + p(); + } + + private void generateMethodToString(MessageDescriptor m) { + + p("public String toString() {"); + indent(); + p("return toString(new java.lang.StringBuilder(), \"\").toString();"); + unindent(); + p("}"); + p(); + + p("public java.lang.StringBuilder toString(java.lang.StringBuilder sb, String prefix) {"); + indent(); + + if( deferredDecode ) { + p("load();"); + } + for (FieldDescriptor field : m.getFields().values()) { + String uname = uCamel(field.getName()); + p("if( has" + uname + "() ) {"); + indent(); + if( field.isRepeated() ) { + String type = javaCollectionType(field); + p("java.util.List<"+type+"> l = get" + uname + "List();"); + p("for( int i=0; i < l.size(); i++ ) {"); + indent(); + if( field.getTypeDescriptor()!=null && !field.getTypeDescriptor().isEnum()) { + p("sb.append(prefix+\""+field.getName()+"[\"+i+\"] {\\n\");"); + p("l.get(i).toString(sb, prefix+\" \");"); + p("sb.append(prefix+\"}\\n\");"); + } else { + p("sb.append(prefix+\""+field.getName()+"[\"+i+\"]: \");"); + p("sb.append(l.get(i));"); + p("sb.append(\"\\n\");"); + } + unindent(); + p("}"); + } else { + if( field.getTypeDescriptor()!=null && !field.getTypeDescriptor().isEnum()) { + p("sb.append(prefix+\""+field.getName()+" {\\n\");"); + p("get" + uname + "().toString(sb, prefix+\" \");"); + p("sb.append(prefix+\"}\\n\");"); + } else { + p("sb.append(prefix+\""+field.getName()+": \");"); + p("sb.append(get" + uname + "());"); + p("sb.append(\"\\n\");"); + } + } + unindent(); + p("}"); + } + + + p("return sb;"); + unindent(); + p("}"); + p(); + + } + + /** + * @param field + * @param className + */ + private void generateFieldAccessor(FieldDescriptor field) { + + String lname = lCamel(field.getName()); + String uname = uCamel(field.getName()); + String type = field.getRule()==FieldDescriptor.REPEATED_RULE ? javaCollectionType(field):javaType(field); + String typeDefault = javaTypeDefault(field); + boolean primitive = field.getTypeDescriptor()==null || field.getTypeDescriptor().isEnum(); + boolean repeated = field.getRule()==FieldDescriptor.REPEATED_RULE; + + // Create the fields.. + p("// " + field.getRule() + " " + field.getType() + " " + field.getName() + " = " + field.getTag() + ";"); + + if( repeated ) { + p("private java.util.List<" + type + "> f_" + lname + ";"); + p(); + + // Create the field accessors + p("public boolean has" + uname + "() {"); + indent(); + if( deferredDecode ) { + p("load();"); + } + p("return this.f_" + lname + "!=null && !this.f_" + lname + ".isEmpty();"); + unindent(); + p("}"); + p(); + + p("public java.util.List<" + type + "> get" + uname + "List() {"); + indent(); + if( deferredDecode ) { + p("load();"); + } + p("if( this.f_" + lname + " == null ) {"); + indent(); + p("this.f_" + lname + " = new java.util.ArrayList<" + type + ">();"); + unindent(); + p("}"); + p("return this.f_" + lname + ";"); + unindent(); + p("}"); + p(); + + p("public T set" + uname + "List(java.util.List<" + type + "> " + lname + ") {"); + indent(); + p("loadAndClear();"); + p("this.f_" + lname + " = " + lname + ";"); + p("return (T)this;"); + unindent(); + p("}"); + p(); + + p("public int get" + uname + "Count() {"); + indent(); + if( deferredDecode ) { + p("load();"); + } + p("if( this.f_" + lname + " == null ) {"); + indent(); + p("return 0;"); + unindent(); + p("}"); + p("return this.f_" + lname + ".size();"); + unindent(); + p("}"); + p(); + + p("public " + type + " get" + uname + "(int index) {"); + indent(); + if( deferredDecode ) { + p("load();"); + } + p("if( this.f_" + lname + " == null ) {"); + indent(); + p("return null;"); + unindent(); + p("}"); + p("return this.f_" + lname + ".get(index);"); + unindent(); + p("}"); + p(); + + p("public T set" + uname + "(int index, " + type + " value) {"); + indent(); + p("loadAndClear();"); + p("get" + uname + "List().set(index, value);"); + p("return (T)this;"); + unindent(); + p("}"); + p(); + + p("public T add" + uname + "(" + type + " value) {"); + indent(); + p("loadAndClear();"); + p("get" + uname + "List().add(value);"); + p("return (T)this;"); + unindent(); + p("}"); + p(); + + p("public T addAll" + uname + "(java.lang.Iterable collection) {"); + indent(); + p("loadAndClear();"); + p("super.addAll(collection, get" + uname + "List());"); + p("return (T)this;"); + unindent(); + p("}"); + p(); + + p("public void clear" + uname + "() {"); + indent(); + p("loadAndClear();"); + p("this.f_" + lname + " = null;"); + unindent(); + p("}"); + p(); + + } else { + + p("private " + type + " f_" + lname + " = "+typeDefault+";"); + if (primitive) { + p("private boolean b_" + lname + ";"); + } + p(); + + // Create the field accessors + p("public boolean has" + uname + "() {"); + indent(); + if( deferredDecode ) { + p("load();"); + } + if (primitive) { + p("return this.b_" + lname + ";"); + } else { + p("return this.f_" + lname + "!=null;"); + } + unindent(); + p("}"); + p(); + + p("public " + type + " get" + uname + "() {"); + indent(); + if( deferredDecode ) { + p("load();"); + } + if( field.getTypeDescriptor()!=null && !field.getTypeDescriptor().isEnum()) { + p("if( this.f_" + lname + " == null ) {"); + indent(); + p("this.f_" + lname + " = new " + type + "();"); + unindent(); + p("}"); + } + p("return this.f_" + lname + ";"); + unindent(); + p("}"); + p(); + + p("public T set" + uname + "(" + type + " " + lname + ") {"); + indent(); + p("loadAndClear();"); + if (primitive) { + if( auto_clear_optional_fields && field.isOptional() ) { + if( field.isStringType() && !"null".equals(typeDefault)) { + p("this.b_" + lname + " = ("+lname+" != "+typeDefault+");"); + } else { + p("this.b_" + lname + " = ("+lname+" != "+typeDefault+");"); + } + } else { + p("this.b_" + lname + " = true;"); + } + } + p("this.f_" + lname + " = " + lname + ";"); + p("return (T)this;"); + unindent(); + p("}"); + p(); + + p("public void clear" + uname + "() {"); + indent(); + p("loadAndClear();"); + if (primitive) { + p("this.b_" + lname + " = false;"); + } + p("this.f_" + lname + " = " + typeDefault + ";"); + unindent(); + p("}"); + p(); + } + + } + + private String javaTypeDefault(FieldDescriptor field) { + OptionDescriptor defaultOption = field.getOptions().get("default"); + if( defaultOption!=null ) { + if( field.isStringType() ) { + return asJavaString(defaultOption.getValue()); + } else if( field.getType() == FieldDescriptor.BYTES_TYPE ) { + return "new org.apache.activemq.protobuf.Buffer("+asJavaString(defaultOption.getValue())+")"; + } else if( field.isInteger32Type() ) { + int v; + if( field.getType() == FieldDescriptor.UINT32_TYPE ) { + v = TextFormat.parseUInt32(defaultOption.getValue()); + } else { + v = TextFormat.parseInt32(defaultOption.getValue()); + } + return ""+v; + } else if( field.isInteger64Type() ) { + long v; + if( field.getType() == FieldDescriptor.UINT64_TYPE ) { + v = TextFormat.parseUInt64(defaultOption.getValue()); + } else { + v = TextFormat.parseInt64(defaultOption.getValue()); + } + return ""+v+"l"; + } else if( field.getType() == FieldDescriptor.DOUBLE_TYPE ) { + double v = Double.valueOf(defaultOption.getValue()); + return ""+v+"d"; + } else if( field.getType() == FieldDescriptor.FLOAT_TYPE ) { + float v = Float.valueOf(defaultOption.getValue()); + return ""+v+"f"; + } else if( field.getType() == FieldDescriptor.BOOL_TYPE ) { + boolean v = Boolean.valueOf(defaultOption.getValue()); + return ""+v; + } else if( field.getTypeDescriptor()!=null && field.getTypeDescriptor().isEnum() ) { + return javaType(field)+"."+defaultOption.getValue(); + } + return defaultOption.getValue(); + } else { + if( field.isNumberType() ) { + return "0"; + } + if( field.getType() == FieldDescriptor.BOOL_TYPE ) { + return "false"; + } + return "null"; + } + } + + static final char HEX_TABLE[] = new char[]{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + private String asJavaString(String value) { + StringBuilder sb = new StringBuilder(value.length()+2); + sb.append("\""); + for (int i = 0; i < value.length(); i++) { + + char b = value.charAt(i); + switch (b) { + // Java does not recognize \a or \v, apparently. + case '\b': sb.append("\\b" ); break; + case '\f': sb.append("\\f" ); break; + case '\n': sb.append("\\n" ); break; + case '\r': sb.append("\\r" ); break; + case '\t': sb.append("\\t" ); break; + case '\\': sb.append("\\\\"); break; + case '\'': sb.append("\\\'"); break; + case '"' : sb.append("\\\""); break; + default: + if (b >= 0x20 && b <'Z') { + sb.append((char) b); + } else { + sb.append("\\u"); + sb.append(HEX_TABLE[(b >>> 12) & 0x0F] ); + sb.append(HEX_TABLE[(b >>> 8) & 0x0F] ); + sb.append(HEX_TABLE[(b >>> 4) & 0x0F] ); + sb.append(HEX_TABLE[b & 0x0F] ); + } + break; + } + + } + sb.append("\""); + return sb.toString(); + } + + private void generateEnum(EnumDescriptor ed) { + String uname = uCamel(ed.getName()); + + String staticOption = "static "; + if( multipleFiles && ed.getParent()==null ) { + staticOption=""; + } + + // TODO Auto-generated method stub + p(); + p("public "+staticOption+"enum " +uname + " {"); + indent(); + + + p(); + int counter=0; + for (EnumFieldDescriptor field : ed.getFields().values()) { + boolean last = counter+1 == ed.getFields().size(); + p(field.getName()+"(\""+field.getName()+"\", "+field.getValue()+")"+(last?";":",")); + counter++; + } + p(); + p("private final String name;"); + p("private final int value;"); + p(); + p("private "+uname+"(String name, int value) {"); + p(" this.name = name;"); + p(" this.value = value;"); + p("}"); + p(); + p("public final int getNumber() {"); + p(" return value;"); + p("}"); + p(); + p("public final String toString() {"); + p(" return name;"); + p("}"); + p(); + p("public static "+uname+" valueOf(int value) {"); + p(" switch (value) {"); + + // It's possible to define multiple ENUM fields with the same value.. + // we only want to put the first one into the switch statement. + HashSet values = new HashSet(); + for (EnumFieldDescriptor field : ed.getFields().values()) { + if( !values.contains(field.getValue()) ) { + p(" case "+field.getValue()+":"); + p(" return "+field.getName()+";"); + values.add(field.getValue()); + } + + } + p(" default:"); + p(" return null;"); + p(" }"); + p("}"); + p(); + + + String createMessage = getOption(ed.getOptions(), "java_create_message", null); + if( "true".equals(createMessage) ) { + p("public org.apache.activemq.protobuf.Message createMessage() {"); + indent(); + p("switch (this) {"); + indent(); + for (EnumFieldDescriptor field : ed.getFields().values()) { + p("case "+field.getName()+":"); + String type = constantToUCamelCase(field.getName()); + p(" return new "+type+"();"); + } + p("default:"); + p(" return null;"); + unindent(); + p("}"); + unindent(); + p("}"); + p(); + } + + unindent(); + p("}"); + p(); + } + + + + private String javaCollectionType(FieldDescriptor field) { + if( field.isInteger32Type() ) { + return "java.lang.Integer"; + } + if( field.isInteger64Type() ) { + return "java.lang.Long"; + } + if( field.getType() == FieldDescriptor.DOUBLE_TYPE ) { + return "java.lang.Double"; + } + if( field.getType() == FieldDescriptor.FLOAT_TYPE ) { + return "java.lang.Float"; + } + if( field.getType() == FieldDescriptor.STRING_TYPE ) { + return "java.lang.String"; + } + if( field.getType() == FieldDescriptor.BYTES_TYPE ) { + return "org.apache.activemq.protobuf.Buffer"; + } + if( field.getType() == FieldDescriptor.BOOL_TYPE ) { + return "java.lang.Boolean"; + } + + TypeDescriptor descriptor = field.getTypeDescriptor(); + return javaType(descriptor); + } + + private String javaType(FieldDescriptor field) { + if( field.isInteger32Type() ) { + return "int"; + } + if( field.isInteger64Type() ) { + return "long"; + } + if( field.getType() == FieldDescriptor.DOUBLE_TYPE ) { + return "double"; + } + if( field.getType() == FieldDescriptor.FLOAT_TYPE ) { + return "float"; + } + if( field.getType() == FieldDescriptor.STRING_TYPE ) { + return "java.lang.String"; + } + if( field.getType() == FieldDescriptor.BYTES_TYPE ) { + return "org.apache.activemq.protobuf.Buffer"; + } + if( field.getType() == FieldDescriptor.BOOL_TYPE ) { + return "boolean"; + } + + TypeDescriptor descriptor = field.getTypeDescriptor(); + return javaType(descriptor); + } + + private String javaType(TypeDescriptor descriptor) { + ProtoDescriptor p = descriptor.getProtoDescriptor(); + if( p != proto ) { + // Try to keep it short.. + String othePackage = javaPackage(p); + if( equals(othePackage,javaPackage(proto) ) ) { + return javaClassName(p)+"."+descriptor.getQName(); + } + // Use the fully qualified class name. + return othePackage+"."+javaClassName(p)+"."+descriptor.getQName(); + } + return descriptor.getQName(); + } + + private boolean equals(String o1, String o2) { + if( o1==o2 ) + return true; + if( o1==null || o2==null ) + return false; + return o1.equals(o2); + } + + private String javaClassName(ProtoDescriptor proto) { + return getOption(proto.getOptions(), "java_outer_classname", uCamel(removeFileExtension(proto.getName()))); + } + + private boolean isMultipleFilesEnabled(ProtoDescriptor proto) { + return "true".equals(getOption(proto.getOptions(), "java_multiple_files", "false")); + } + + + private String javaPackage(ProtoDescriptor proto) { + String name = proto.getPackageName(); + if( name!=null ) { + name = name.replace('-', '.'); + name = name.replace('/', '.'); + } + return getOption(proto.getOptions(), "java_package", name); + } + + + // ---------------------------------------------------------------- + // Internal Helper methods + // ---------------------------------------------------------------- + + private void indent() { + indent++; + } + + private void unindent() { + indent--; + } + + private void p(String line) { + // Indent... + for (int i = 0; i < indent; i++) { + w.print(" "); + } + // Then print. + w.println(line); + } + + private void p() { + w.println(); + } + + private String getOption(Map options, String optionName, String defaultValue) { + OptionDescriptor optionDescriptor = options.get(optionName); + if (optionDescriptor == null) { + return defaultValue; + } + return optionDescriptor.getValue(); + } + + static private String removeFileExtension(String name) { + return name.replaceAll("\\..*", ""); + } + + static private String uCamel(String name) { + boolean upNext=true; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if( Character.isJavaIdentifierPart(c) && Character.isLetterOrDigit(c)) { + if( upNext ) { + c = Character.toUpperCase(c); + upNext=false; + } + sb.append(c); + } else { + upNext=true; + } + } + return sb.toString(); + } + + static private String lCamel(String name) { + if( name == null || name.length()<1 ) + return name; + String uCamel = uCamel(name); + return uCamel.substring(0,1).toLowerCase()+uCamel.substring(1); + } + + + private String constantToUCamelCase(String name) { + boolean upNext=true; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if( Character.isJavaIdentifierPart(c) && Character.isLetterOrDigit(c)) { + if( upNext ) { + c = Character.toUpperCase(c); + upNext=false; + } else { + c = Character.toLowerCase(c); + } + sb.append(c); + } else { + upNext=true; + } + } + return sb.toString(); + } + + + private String constantCase(String name) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if( i!=0 && Character.isUpperCase(c) ) { + sb.append("_"); + } + sb.append(Character.toUpperCase(c)); + } + return sb.toString(); + } + + public File getOut() { + return out; + } + + public void setOut(File outputDirectory) { + this.out = outputDirectory; + } + + public File[] getPath() { + return path; + } + + public void setPath(File[] path) { + this.path = path; + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/MessageDescriptor.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/MessageDescriptor.java new file mode 100644 index 0000000000..44ad091ce3 --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/MessageDescriptor.java @@ -0,0 +1,194 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + + +public class MessageDescriptor implements TypeDescriptor { + + private String name; + private ExtensionsDescriptor extensions; + private Map fields = new LinkedHashMap(); + private Map messages = new LinkedHashMap(); + private Map enums = new LinkedHashMap(); + private final ProtoDescriptor protoDescriptor; + private List extendsList = new ArrayList(); + private Map options = new LinkedHashMap(); + private List associatedEnumFieldDescriptors = new ArrayList(); + + private final MessageDescriptor parent; + private MessageDescriptor baseType; + + public MessageDescriptor(ProtoDescriptor protoDescriptor, MessageDescriptor parent) { + this.protoDescriptor = protoDescriptor; + this.parent = parent; + } + + public void validate(List errors) { + String baseName = getOption(getOptions(), "base_type", null); + if( baseName!=null ) { + if( baseType==null ) { + baseType = (MessageDescriptor) getType(baseName); + } + if( baseType == null ) { + baseType = (MessageDescriptor) getProtoDescriptor().getType(baseName); + } + if( baseType == null ) { + errors.add("base_type option not valid, type not found: "+baseName); + } + + // Assert that all the fields in the base type are defined in this message defintion too. + HashSet baseFieldNames = new HashSet(baseType.getFields().keySet()); + baseFieldNames.removeAll(getFields().keySet()); + + // Some fields were not defined in the sub class.. + if( !baseFieldNames.isEmpty() ) { + for (String fieldName : baseFieldNames) { + errors.add("base_type "+baseName+" field "+fieldName+" not defined in "+getName()); + } + } + } + + for (FieldDescriptor field : fields.values()) { + field.validate(errors); + } + for (EnumDescriptor o : enums.values()) { + o.validate(errors); + } + for (MessageDescriptor o : messages.values()) { + o.validate(errors); + } + } + + public String getOption(Map options, String optionName, String defaultValue) { + OptionDescriptor optionDescriptor = options.get(optionName); + if (optionDescriptor == null) { + return defaultValue; + } + return optionDescriptor.getValue(); + } + + public void setName(String name) { + this.name = name; + } + + public void setExtensions(ExtensionsDescriptor extensions) { + this.extensions = extensions; + } + + public void setExtends(List extendsList) { + this.extendsList = extendsList; + } + public List getExtends() { + return extendsList; + } + + public void setFields(Map fields) { + this.fields = fields; + } + + public void setMessages(Map messages) { + this.messages = messages; + } + + public void setEnums(Map enums) { + this.enums = enums; + } + + public String getName() { + return name; + } + + public String getQName() { + if( parent==null ) { + return name; + } else { + return parent.getQName()+"."+name; + } + } + + public ExtensionsDescriptor getExtensions() { + return extensions; + } + + public Map getFields() { + return fields; + } + + public Map getMessages() { + return messages; + } + + public Map getEnums() { + return enums; + } + + public ProtoDescriptor getProtoDescriptor() { + return protoDescriptor; + } + + public Map getOptions() { + return options; + } + + public void setOptions(Map options) { + this.options = options; + } + + public MessageDescriptor getParent() { + return parent; + } + + public TypeDescriptor getType(String t) { + for (MessageDescriptor o : messages.values()) { + if( t.equals(o.getName()) ) { + return o; + } + if( t.startsWith(o.getName()+".") ) { + return o.getType( t.substring(o.getName().length()+1) ); + } + } + for (EnumDescriptor o : enums.values()) { + if( t.equals(o.getName()) ) { + return o; + } + } + return null; + } + + public boolean isEnum() { + return false; + } + + public MessageDescriptor getBaseType() { + return baseType; + } + + public void associate(EnumFieldDescriptor desc) { + associatedEnumFieldDescriptors.add(desc); + } + + public List getAssociatedEnumFieldDescriptors() { + return associatedEnumFieldDescriptors; + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/MethodDescriptor.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/MethodDescriptor.java new file mode 100644 index 0000000000..4e4cd83bd5 --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/MethodDescriptor.java @@ -0,0 +1,58 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler; + +public class MethodDescriptor { + + private final ProtoDescriptor protoDescriptor; + private String name; + private String parameter; + private String returns; + + public MethodDescriptor(ProtoDescriptor protoDescriptor) { + this.protoDescriptor = protoDescriptor; + } + + public void setName(String name) { + this.name = name; + } + + public void setParameter(String parameter) { + this.parameter = parameter; + } + + public void setReturns(String returns) { + this.returns = returns; + } + + public ProtoDescriptor getProtoDescriptor() { + return protoDescriptor; + } + + public String getName() { + return name; + } + + public String getParameter() { + return parameter; + } + + public String getReturns() { + return returns; + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/OptionDescriptor.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/OptionDescriptor.java new file mode 100644 index 0000000000..198fff377c --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/OptionDescriptor.java @@ -0,0 +1,50 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler; + +import java.util.List; + +public class OptionDescriptor { + + private String name; + private String value; + + public OptionDescriptor() { + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } + + public void setName(String name) { + this.name = name; + } + + public void setValue(String value) { + this.value = value; + } + + public void validate(List errors) { + // TODO Auto-generated method stub + + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ParserSupport.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ParserSupport.java new file mode 100644 index 0000000000..5ad12859f8 --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ParserSupport.java @@ -0,0 +1,83 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler; + +import org.apache.activemq.protobuf.compiler.TextFormat.InvalidEscapeSequence; +import org.apache.activemq.protobuf.compiler.parser.ParseException; +import org.apache.activemq.protobuf.compiler.parser.Token; + +public class ParserSupport { + + public static String decodeString(Token token) throws ParseException { + +// StringBuilder sb = new StringBuilder(); +// for (int i = 1; i < value.length() - 1; i++) { +// char c = value.charAt(i); +// if (c == '\'') { +// if( i+1 < (value.length() - 1) ) { +// char e = value.charAt(i+1); +// switch(e) { +// case 'a': +// sb.append((char)0x07); +// break; +// case 'b': +// sb.append("\b"); +// break; +// case 'f': +// sb.append("\f"); +// break; +// case 'n': +// sb.append("\n"); +// break; +// case 'r': +// sb.append("\r"); +// break; +// case 't': +// sb.append("\t"); +// break; +// case 'v': +// sb.append((char)0x0b); +// break; +// case '\\': +// sb.append("\\"); +// break; +// case '\'': +// sb.append("'"); +// break; +// case '\"': +// sb.append("\""); +// break; +// default: +// sb.append(e); +// break; +// } +// } else { +// throw new RuntimeException("Invalid string litteral: "+value); +// } +// } +// sb.append(c); +// } +// return sb.toString(); + + try { + return TextFormat.unescapeText(token.image.substring(1, token.image.length()-1)); + } catch (InvalidEscapeSequence e) { + throw new ParseException("Invalid string litteral at line " + token.next.beginLine + ", column " + token.next.beginColumn+": "+e.getMessage()); + } + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ProtoDescriptor.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ProtoDescriptor.java new file mode 100644 index 0000000000..5dfeec9877 --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ProtoDescriptor.java @@ -0,0 +1,164 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class ProtoDescriptor { + + private String packageName; + private Map options = new LinkedHashMap(); + private Map messages = new LinkedHashMap(); + private Map enums = new LinkedHashMap(); + private List extendsList = new ArrayList(); + private Map services = new LinkedHashMap(); + List imports = new ArrayList(); + Map importProtoDescriptors = new LinkedHashMap(); + private String name; + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + public void setOptions(Map options) { + this.options = options; + } + + public void setMessages(Map messages) { + this.messages = messages; + } + + public void setEnums(Map enums) { + this.enums = enums; + } + + public void setExtends(List extendsList) { + this.extendsList = extendsList; + } + + public List getExtends() { + return extendsList; + } + + public String getPackageName() { + return packageName; + } + + public Map getOptions() { + return options; + } + + public Map getMessages() { + return messages; + } + + public Map getEnums() { + return enums; + } + + public void setServices(Map services) { + this.services = services; + } + + public Map getServices() { + return services; + } + + /** + * Checks for validation errors in the proto definition and fills them + * into the errors list. + * + * @return + */ + public void validate(List errors) { + for (ProtoDescriptor o : importProtoDescriptors.values()) { + o.validate(errors); + } + for (OptionDescriptor o : options.values()) { + o.validate(errors); + } + for (MessageDescriptor o : messages.values()) { + o.validate(errors); + } + for (EnumDescriptor o : enums.values()) { + o.validate(errors); + } + for (MessageDescriptor o : extendsList) { + o.validate(errors); + } + for (ServiceDescriptor o : services.values()) { + o.validate(errors); + } + } + + public List getImports() { + return imports; + } + + public void setImports(List imports) { + this.imports = imports; + } + + public Map getImportProtoDescriptors() { + return importProtoDescriptors; + } + + public void setImportProtoDescriptors(Map importProtoDescriptors) { + this.importProtoDescriptors = importProtoDescriptors; + } + + public TypeDescriptor getType(String type) { + for (MessageDescriptor o : messages.values()) { + if( type.equals(o.getName()) ) { + return o; + } + if( type.startsWith(o.getName()+".") ) { + return o.getType( type.substring(o.getName().length()+1) ); + } + } + for (EnumDescriptor o : enums.values()) { + if( type.equals(o.getName()) ) { + return o; + } + } + // Check to see if the type was qualified with the package name... + for (ProtoDescriptor o : importProtoDescriptors.values()) { + if( o.getPackageName()!=null && type.startsWith(o.getPackageName()+".") ) { + return o.getType( type.substring(o.getPackageName().length()+1) ); + } + } + for (ProtoDescriptor o : importProtoDescriptors.values()) { + TypeDescriptor rc = o.getType(type); + if (rc != null) { + return rc; + } + } + return null; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ProtoMojo.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ProtoMojo.java new file mode 100644 index 0000000000..41716bca8e --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ProtoMojo.java @@ -0,0 +1,110 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +import org.apache.activemq.protobuf.compiler.parser.ParseException; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.project.MavenProject; + +/** + * A Maven Mojo so that the Proto compiler can be used with maven. + * + * @goal compile + * @phase process-sources + */ +public class ProtoMojo extends AbstractMojo { + + /** + * The maven project. + * + * @parameter expression="${project}" + * @required + * @readonly + */ + protected MavenProject project; + + /** + * The directory where the proto files (*.proto) are + * located. + * + * @parameter expression="${sourceDirectory}" default-value="${basedir}/src/main/proto" + */ + private File sourceDirectory; + + /** + * The directory where the output files will be located. + * + * @parameter expression="${outputDirectory}" default-value="${project.build.directory}/generated-sources/proto" + */ + private File outputDirectory; + + /** + * The type of generator to run. + * + * @parameter default-value="default" + */ + private String type; + + public void execute() throws MojoExecutionException { + + File[] files = sourceDirectory.listFiles(new FileFilter() { + public boolean accept(File pathname) { + return pathname.getName().endsWith(".proto"); + } + }); + + if (files==null || files.length==0) { + getLog().warn("No proto files found in directory: " + sourceDirectory.getPath()); + return; + } + + List recFiles = Arrays.asList(files); + for (File file : recFiles) { + try { + getLog().info("Compiling: "+file.getPath()); + if( "default".equals(type) ) { + JavaGenerator generator = new JavaGenerator(); + generator.setOut(outputDirectory); + generator.compile(file); + } else if( "alt".equals(type) ) { + AltJavaGenerator generator = new AltJavaGenerator(); + generator.setOut(outputDirectory); + generator.compile(file); + } + } catch (CompilerException e) { + getLog().error("Protocol Buffer Compiler failed with the following error(s):"); + for (String error : e.getErrors() ) { + getLog().error(""); + getLog().error(error); + } + getLog().error(""); + throw new MojoExecutionException("Compile failed. For more details see error messages listed above.", e); + } + } + + this.project.addCompileSourceRoot(outputDirectory.getAbsolutePath()); + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ServiceDescriptor.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ServiceDescriptor.java new file mode 100644 index 0000000000..480b44fed6 --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/ServiceDescriptor.java @@ -0,0 +1,55 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler; + +import java.util.ArrayList; +import java.util.List; + +public class ServiceDescriptor { + + private final ProtoDescriptor protoDescriptor; + private List methods=new ArrayList(); + private String name; + + public ServiceDescriptor(ProtoDescriptor protoDescriptor) { + this.protoDescriptor = protoDescriptor; + } + + public void setName(String name) { + this.name = name; + } + + public void setMethods(List methods) { + this.methods = methods; + } + + public ProtoDescriptor getProtoDescriptor() { + return protoDescriptor; + } + + public List getMethods() { + return methods; + } + + public String getName() { + return name; + } + + public void validate(List errors) { + } + +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/TextFormat.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/TextFormat.java new file mode 100644 index 0000000000..f16d4dfb0a --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/TextFormat.java @@ -0,0 +1,767 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. +// http://code.google.com/p/protobuf/ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.apache.activemq.protobuf.compiler; + +import java.io.IOException; +import java.math.BigInteger; +import java.nio.CharBuffer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.activemq.protobuf.Buffer; +import org.apache.activemq.protobuf.UTF8Buffer; + +/** + * Provide ascii text parsing and formatting support for proto2 instances. + * The implementation largely follows google/protobuf/text_format.cc. + * + * HRC: I wish the original class was not package protected so we did not need + * to copy this file over. We need to request that the protobuf folks open + * this class up amoung a few others. + * + * @author wenboz@google.com Wenbo Zhu + * @author kenton@google.com Kenton Varda + */ +public final class TextFormat { + + /** Convert an unsigned 32-bit integer to a string. */ + private static String unsignedToString(int value) { + if (value >= 0) { + return Integer.toString(value); + } else { + return Long.toString(((long) value) & 0x00000000FFFFFFFFL); + } + } + + /** Convert an unsigned 64-bit integer to a string. */ + private static String unsignedToString(long value) { + if (value >= 0) { + return Long.toString(value); + } else { + // Pull off the most-significant bit so that BigInteger doesn't think + // the number is negative, then set it again using setBit(). + return BigInteger.valueOf(value & 0x7FFFFFFFFFFFFFFFL) + .setBit(63).toString(); + } + } + + // ================================================================= + // Parsing + + /** + * Represents a stream of tokens parsed from a {@code String}. + * + *

The Java standard library provides many classes that you might think + * would be useful for implementing this, but aren't. For example: + * + *

    + *
  • {@code java.io.StreamTokenizer}: This almost does what we want -- or, + * at least, something that would get us close to what we want -- except + * for one fatal flaw: It automatically un-escapes strings using Java + * escape sequences, which do not include all the escape sequences we + * need to support (e.g. '\x'). + *
  • {@code java.util.Scanner}: This seems like a great way at least to + * parse regular expressions out of a stream (so we wouldn't have to load + * the entire input into a single string before parsing). Sadly, + * {@code Scanner} requires that tokens be delimited with some delimiter. + * Thus, although the text "foo:" should parse to two tokens ("foo" and + * ":"), {@code Scanner} would recognize it only as a single token. + * Furthermore, {@code Scanner} provides no way to inspect the contents + * of delimiters, making it impossible to keep track of line and column + * numbers. + *
+ * + *

Luckily, Java's regular expression support does manage to be useful to + * us. (Barely: We need {@code Matcher.usePattern()}, which is new in + * Java 1.5.) So, we can use that, at least. Unfortunately, this implies + * that we need to have the entire input in one contiguous string. + */ + private static final class Tokenizer { + private final CharSequence text; + private final Matcher matcher; + private String currentToken; + + // The character index within this.text at which the current token begins. + private int pos = 0; + + // The line and column numbers of the current token. + private int line = 0; + private int column = 0; + + // The line and column numbers of the previous token (allows throwing + // errors *after* consuming). + private int previousLine = 0; + private int previousColumn = 0; + + private static Pattern WHITESPACE = + Pattern.compile("(\\s|(#.*$))+", Pattern.MULTILINE); + private static Pattern TOKEN = Pattern.compile( + "[a-zA-Z_][0-9a-zA-Z_+-]*|" + // an identifier + "[0-9+-][0-9a-zA-Z_.+-]*|" + // a number + "\"([^\"\n\\\\]|\\\\.)*(\"|\\\\?$)|" + // a double-quoted string + "\'([^\"\n\\\\]|\\\\.)*(\'|\\\\?$)", // a single-quoted string + Pattern.MULTILINE); + + private static Pattern DOUBLE_INFINITY = Pattern.compile( + "-?inf(inity)?", + Pattern.CASE_INSENSITIVE); + private static Pattern FLOAT_INFINITY = Pattern.compile( + "-?inf(inity)?f?", + Pattern.CASE_INSENSITIVE); + private static Pattern FLOAT_NAN = Pattern.compile( + "nanf?", + Pattern.CASE_INSENSITIVE); + + /** Construct a tokenizer that parses tokens from the given text. */ + public Tokenizer(CharSequence text) { + this.text = text; + this.matcher = WHITESPACE.matcher(text); + skipWhitespace(); + nextToken(); + } + + /** Are we at the end of the input? */ + public boolean atEnd() { + return currentToken.length() == 0; + } + + /** Advance to the next token. */ + public void nextToken() { + previousLine = line; + previousColumn = column; + + // Advance the line counter to the current position. + while (pos < matcher.regionStart()) { + if (text.charAt(pos) == '\n') { + ++line; + column = 0; + } else { + ++column; + } + ++pos; + } + + // Match the next token. + if (matcher.regionStart() == matcher.regionEnd()) { + // EOF + currentToken = ""; + } else { + matcher.usePattern(TOKEN); + if (matcher.lookingAt()) { + currentToken = matcher.group(); + matcher.region(matcher.end(), matcher.regionEnd()); + } else { + // Take one character. + currentToken = String.valueOf(text.charAt(pos)); + matcher.region(pos + 1, matcher.regionEnd()); + } + + skipWhitespace(); + } + } + + /** + * Skip over any whitespace so that the matcher region starts at the next + * token. + */ + private void skipWhitespace() { + matcher.usePattern(WHITESPACE); + if (matcher.lookingAt()) { + matcher.region(matcher.end(), matcher.regionEnd()); + } + } + + /** + * If the next token exactly matches {@code token}, consume it and return + * {@code true}. Otherwise, return {@code false} without doing anything. + */ + public boolean tryConsume(String token) { + if (currentToken.equals(token)) { + nextToken(); + return true; + } else { + return false; + } + } + + /** + * If the next token exactly matches {@code token}, consume it. Otherwise, + * throw a {@link ParseException}. + */ + public void consume(String token) throws ParseException { + if (!tryConsume(token)) { + throw parseException("Expected \"" + token + "\"."); + } + } + + /** + * Returns {@code true} if the next token is an integer, but does + * not consume it. + */ + public boolean lookingAtInteger() { + if (currentToken.length() == 0) { + return false; + } + + char c = currentToken.charAt(0); + return ('0' <= c && c <= '9') || + c == '-' || c == '+'; + } + + /** + * If the next token is an identifier, consume it and return its value. + * Otherwise, throw a {@link ParseException}. + */ + public String consumeIdentifier() throws ParseException { + for (int i = 0; i < currentToken.length(); i++) { + char c = currentToken.charAt(i); + if (('a' <= c && c <= 'z') || + ('A' <= c && c <= 'Z') || + ('0' <= c && c <= '9') || + (c == '_') || (c == '.')) { + // OK + } else { + throw parseException("Expected identifier."); + } + } + + String result = currentToken; + nextToken(); + return result; + } + + /** + * If the next token is a 32-bit signed integer, consume it and return its + * value. Otherwise, throw a {@link ParseException}. + */ + public int consumeInt32() throws ParseException { + try { + int result = parseInt32(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw integerParseException(e); + } + } + + /** + * If the next token is a 32-bit unsigned integer, consume it and return its + * value. Otherwise, throw a {@link ParseException}. + */ + public int consumeUInt32() throws ParseException { + try { + int result = parseUInt32(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw integerParseException(e); + } + } + + /** + * If the next token is a 64-bit signed integer, consume it and return its + * value. Otherwise, throw a {@link ParseException}. + */ + public long consumeInt64() throws ParseException { + try { + long result = parseInt64(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw integerParseException(e); + } + } + + /** + * If the next token is a 64-bit unsigned integer, consume it and return its + * value. Otherwise, throw a {@link ParseException}. + */ + public long consumeUInt64() throws ParseException { + try { + long result = parseUInt64(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw integerParseException(e); + } + } + + /** + * If the next token is a double, consume it and return its value. + * Otherwise, throw a {@link ParseException}. + */ + public double consumeDouble() throws ParseException { + // We need to parse infinity and nan separately because + // Double.parseDouble() does not accept "inf", "infinity", or "nan". + if (DOUBLE_INFINITY.matcher(currentToken).matches()) { + boolean negative = currentToken.startsWith("-"); + nextToken(); + return negative ? Double.NEGATIVE_INFINITY : Double.POSITIVE_INFINITY; + } + if (currentToken.equalsIgnoreCase("nan")) { + nextToken(); + return Double.NaN; + } + try { + double result = Double.parseDouble(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw floatParseException(e); + } + } + + /** + * If the next token is a float, consume it and return its value. + * Otherwise, throw a {@link ParseException}. + */ + public float consumeFloat() throws ParseException { + // We need to parse infinity and nan separately because + // Float.parseFloat() does not accept "inf", "infinity", or "nan". + if (FLOAT_INFINITY.matcher(currentToken).matches()) { + boolean negative = currentToken.startsWith("-"); + nextToken(); + return negative ? Float.NEGATIVE_INFINITY : Float.POSITIVE_INFINITY; + } + if (FLOAT_NAN.matcher(currentToken).matches()) { + nextToken(); + return Float.NaN; + } + try { + float result = Float.parseFloat(currentToken); + nextToken(); + return result; + } catch (NumberFormatException e) { + throw floatParseException(e); + } + } + + /** + * If the next token is a boolean, consume it and return its value. + * Otherwise, throw a {@link ParseException}. + */ + public boolean consumeBoolean() throws ParseException { + if (currentToken.equals("true")) { + nextToken(); + return true; + } else if (currentToken.equals("false")) { + nextToken(); + return false; + } else { + throw parseException("Expected \"true\" or \"false\"."); + } + } + + /** + * If the next token is a string, consume it and return its (unescaped) + * value. Otherwise, throw a {@link ParseException}. + */ + public String consumeString() throws ParseException { + return new UTF8Buffer(consumeBuffer()).toString(); + } + + /** + * If the next token is a string, consume it, unescape it as a + * {@link Buffer}, and return it. Otherwise, throw a + * {@link ParseException}. + */ + public Buffer consumeBuffer() throws ParseException { + char quote = currentToken.length() > 0 ? currentToken.charAt(0) : '\0'; + if (quote != '\"' && quote != '\'') { + throw parseException("Expected string."); + } + + if (currentToken.length() < 2 || + currentToken.charAt(currentToken.length() - 1) != quote) { + throw parseException("String missing ending quote."); + } + + try { + String escaped = currentToken.substring(1, currentToken.length() - 1); + Buffer result = unescapeBytes(escaped); + nextToken(); + return result; + } catch (InvalidEscapeSequence e) { + throw parseException(e.getMessage()); + } + } + + /** + * Returns a {@link ParseException} with the current line and column + * numbers in the description, suitable for throwing. + */ + public ParseException parseException(String description) { + // Note: People generally prefer one-based line and column numbers. + return new ParseException( + (line + 1) + ":" + (column + 1) + ": " + description); + } + + /** + * Returns a {@link ParseException} with the line and column numbers of + * the previous token in the description, suitable for throwing. + */ + public ParseException parseExceptionPreviousToken(String description) { + // Note: People generally prefer one-based line and column numbers. + return new ParseException( + (previousLine + 1) + ":" + (previousColumn + 1) + ": " + description); + } + + /** + * Constructs an appropriate {@link ParseException} for the given + * {@code NumberFormatException} when trying to parse an integer. + */ + private ParseException integerParseException(NumberFormatException e) { + return parseException("Couldn't parse integer: " + e.getMessage()); + } + + /** + * Constructs an appropriate {@link ParseException} for the given + * {@code NumberFormatException} when trying to parse a float or double. + */ + private ParseException floatParseException(NumberFormatException e) { + return parseException("Couldn't parse number: " + e.getMessage()); + } + } + + /** Thrown when parsing an invalid text format message. */ + public static class ParseException extends IOException { + public ParseException(String message) { + super(message); + } + } + + private static final int BUFFER_SIZE = 4096; + + // TODO(chrisn): See if working around java.io.Reader#read(CharBuffer) + // overhead is worthwhile + private static StringBuilder toStringBuilder(Readable input) + throws IOException { + StringBuilder text = new StringBuilder(); + CharBuffer buffer = CharBuffer.allocate(BUFFER_SIZE); + while (true) { + int n = input.read(buffer); + if (n == -1) { + break; + } + buffer.flip(); + text.append(buffer, 0, n); + } + return text; + } + + + // ================================================================= + // Utility functions + // + // Some of these methods are package-private because Descriptors.java uses + // them. + + /** + * Escapes bytes in the format used in protocol buffer text format, which + * is the same as the format used for C string literals. All bytes + * that are not printable 7-bit ASCII characters are escaped, as well as + * backslash, single-quote, and double-quote characters. Characters for + * which no defined short-hand escape sequence is defined will be escaped + * using 3-digit octal sequences. + */ + static String escapeBytes(Buffer input) { + StringBuilder builder = new StringBuilder(input.getLength()); + for (int i = 0; i < input.getLength(); i++) { + byte b = input.byteAt(i); + switch (b) { + // Java does not recognize \a or \v, apparently. + case 0x07: builder.append("\\a" ); break; + case '\b': builder.append("\\b" ); break; + case '\f': builder.append("\\f" ); break; + case '\n': builder.append("\\n" ); break; + case '\r': builder.append("\\r" ); break; + case '\t': builder.append("\\t" ); break; + case 0x0b: builder.append("\\v" ); break; + case '\\': builder.append("\\\\"); break; + case '\'': builder.append("\\\'"); break; + case '"' : builder.append("\\\""); break; + default: + if (b >= 0x20) { + builder.append((char) b); + } else { + builder.append('\\'); + builder.append((char) ('0' + ((b >>> 6) & 3))); + builder.append((char) ('0' + ((b >>> 3) & 7))); + builder.append((char) ('0' + (b & 7))); + } + break; + } + } + return builder.toString(); + } + + /** + * Un-escape a byte sequence as escaped using + * {@link #escapeBytes(Buffer)}. Two-digit hex escapes (starting with + * "\x") are also recognized. + */ + static Buffer unescapeBytes(CharSequence input) + throws InvalidEscapeSequence { + byte[] result = new byte[input.length()]; + int pos = 0; + for (int i = 0; i < input.length(); i++) { + char c = input.charAt(i); + if (c == '\\') { + if (i + 1 < input.length()) { + ++i; + c = input.charAt(i); + if (isOctal(c)) { + // Octal escape. + int code = digitValue(c); + if (i + 1 < input.length() && isOctal(input.charAt(i + 1))) { + ++i; + code = code * 8 + digitValue(input.charAt(i)); + } + if (i + 1 < input.length() && isOctal(input.charAt(i + 1))) { + ++i; + code = code * 8 + digitValue(input.charAt(i)); + } + result[pos++] = (byte)code; + } else { + switch (c) { + case 'a' : result[pos++] = 0x07; break; + case 'b' : result[pos++] = '\b'; break; + case 'f' : result[pos++] = '\f'; break; + case 'n' : result[pos++] = '\n'; break; + case 'r' : result[pos++] = '\r'; break; + case 't' : result[pos++] = '\t'; break; + case 'v' : result[pos++] = 0x0b; break; + case '\\': result[pos++] = '\\'; break; + case '\'': result[pos++] = '\''; break; + case '"' : result[pos++] = '\"'; break; + + case 'x': + // hex escape + int code = 0; + if (i + 1 < input.length() && isHex(input.charAt(i + 1))) { + ++i; + code = digitValue(input.charAt(i)); + } else { + throw new InvalidEscapeSequence( + "Invalid escape sequence: '\\x' with no digits"); + } + if (i + 1 < input.length() && isHex(input.charAt(i + 1))) { + ++i; + code = code * 16 + digitValue(input.charAt(i)); + } + result[pos++] = (byte)code; + break; + + default: + throw new InvalidEscapeSequence( + "Invalid escape sequence: '\\" + c + "'"); + } + } + } else { + throw new InvalidEscapeSequence( + "Invalid escape sequence: '\\' at end of string."); + } + } else { + result[pos++] = (byte)c; + } + } + + return new Buffer(result, 0, pos); + } + + /** + * Thrown by {@link TextFormat#unescapeBytes} and + * {@link TextFormat#unescapeText} when an invalid escape sequence is seen. + */ + static class InvalidEscapeSequence extends IOException { + public InvalidEscapeSequence(String description) { + super(description); + } + } + + /** + * Like {@link #escapeBytes(Buffer)}, but escapes a text string. + * Non-ASCII characters are first encoded as UTF-8, then each byte is escaped + * individually as a 3-digit octal escape. Yes, it's weird. + */ + static String escapeText(String input) { + return escapeBytes(new UTF8Buffer(input)); + } + + /** + * Un-escape a text string as escaped using {@link #escapeText(String)}. + * Two-digit hex escapes (starting with "\x") are also recognized. + */ + static String unescapeText(String input) throws InvalidEscapeSequence { + return new UTF8Buffer(unescapeBytes(input)).toString(); + } + + /** Is this an octal digit? */ + private static boolean isOctal(char c) { + return '0' <= c && c <= '7'; + } + + /** Is this a hex digit? */ + private static boolean isHex(char c) { + return ('0' <= c && c <= '9') || + ('a' <= c && c <= 'f') || + ('A' <= c && c <= 'F'); + } + + /** + * Interpret a character as a digit (in any base up to 36) and return the + * numeric value. This is like {@code Character.digit()} but we don't accept + * non-ASCII digits. + */ + private static int digitValue(char c) { + if ('0' <= c && c <= '9') { + return c - '0'; + } else if ('a' <= c && c <= 'z') { + return c - 'a' + 10; + } else { + return c - 'A' + 10; + } + } + + /** + * Parse a 32-bit signed integer from the text. Unlike the Java standard + * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" + * and "0" to signify hexidecimal and octal numbers, respectively. + */ + static int parseInt32(String text) throws NumberFormatException { + return (int) parseInteger(text, true, false); + } + + /** + * Parse a 32-bit unsigned integer from the text. Unlike the Java standard + * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" + * and "0" to signify hexidecimal and octal numbers, respectively. The + * result is coerced to a (signed) {@code int} when returned since Java has + * no unsigned integer type. + */ + static int parseUInt32(String text) throws NumberFormatException { + return (int) parseInteger(text, false, false); + } + + /** + * Parse a 64-bit signed integer from the text. Unlike the Java standard + * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" + * and "0" to signify hexidecimal and octal numbers, respectively. + */ + static long parseInt64(String text) throws NumberFormatException { + return parseInteger(text, true, true); + } + + /** + * Parse a 64-bit unsigned integer from the text. Unlike the Java standard + * {@code Integer.parseInt()}, this function recognizes the prefixes "0x" + * and "0" to signify hexidecimal and octal numbers, respectively. The + * result is coerced to a (signed) {@code long} when returned since Java has + * no unsigned long type. + */ + static long parseUInt64(String text) throws NumberFormatException { + return parseInteger(text, false, true); + } + + private static long parseInteger(String text, + boolean isSigned, + boolean isLong) + throws NumberFormatException { + int pos = 0; + + boolean negative = false; + if (text.startsWith("-", pos)) { + if (!isSigned) { + throw new NumberFormatException("Number must be positive: " + text); + } + ++pos; + negative = true; + } + + int radix = 10; + if (text.startsWith("0x", pos)) { + pos += 2; + radix = 16; + } else if (text.startsWith("0", pos)) { + radix = 8; + } + + String numberText = text.substring(pos); + + long result = 0; + if (numberText.length() < 16) { + // Can safely assume no overflow. + result = Long.parseLong(numberText, radix); + if (negative) { + result = -result; + } + + // Check bounds. + // No need to check for 64-bit numbers since they'd have to be 16 chars + // or longer to overflow. + if (!isLong) { + if (isSigned) { + if (result > Integer.MAX_VALUE || result < Integer.MIN_VALUE) { + throw new NumberFormatException( + "Number out of range for 32-bit signed integer: " + text); + } + } else { + if (result >= (1L << 32) || result < 0) { + throw new NumberFormatException( + "Number out of range for 32-bit unsigned integer: " + text); + } + } + } + } else { + BigInteger bigValue = new BigInteger(numberText, radix); + if (negative) { + bigValue = bigValue.negate(); + } + + // Check bounds. + if (!isLong) { + if (isSigned) { + if (bigValue.bitLength() > 31) { + throw new NumberFormatException( + "Number out of range for 32-bit signed integer: " + text); + } + } else { + if (bigValue.bitLength() > 32) { + throw new NumberFormatException( + "Number out of range for 32-bit unsigned integer: " + text); + } + } + } else { + if (isSigned) { + if (bigValue.bitLength() > 63) { + throw new NumberFormatException( + "Number out of range for 64-bit signed integer: " + text); + } + } else { + if (bigValue.bitLength() > 64) { + throw new NumberFormatException( + "Number out of range for 64-bit unsigned integer: " + text); + } + } + } + + result = bigValue.longValue(); + } + + return result; + } +} diff --git a/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/TypeDescriptor.java b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/TypeDescriptor.java new file mode 100644 index 0000000000..6e47738f21 --- /dev/null +++ b/activemq-protobuf/src/main/java/org/apache/activemq/protobuf/compiler/TypeDescriptor.java @@ -0,0 +1,30 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler; + +public interface TypeDescriptor { + public String getName(); + + public String getQName(); + + public ProtoDescriptor getProtoDescriptor(); + + public boolean isEnum(); + + public void associate(EnumFieldDescriptor desc); + +} diff --git a/activemq-protobuf/src/main/javacc/proto-parser.jj b/activemq-protobuf/src/main/javacc/proto-parser.jj new file mode 100644 index 0000000000..3064a765ce --- /dev/null +++ b/activemq-protobuf/src/main/javacc/proto-parser.jj @@ -0,0 +1,599 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +options { +STATIC=false; +} + +PARSER_BEGIN(ProtoParser) +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.activemq.protobuf.compiler.parser; + +import org.apache.activemq.protobuf.compiler.*; +import java.util.LinkedHashMap; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Iterator; +import java.io.File; +import java.io.FileReader; +import java.io.FileNotFoundException; +import java.io.IOException; + +/** + * This class is generated with JavaCC. Do not modify manually. + */ +public class ProtoParser { +} + +PARSER_END(ProtoParser) + +SKIP : +{ + " " +| "\t" +| "\n" +| "\r" +} + +SPECIAL_TOKEN : +{ + "//" : COMMENT +} + + SPECIAL_TOKEN : +{ + <("\n" | "\r" | "\r\n" | "|")> : DEFAULT +} + + MORE : +{ + <~[]> +} + +TOKEN : +{ + + | + | + | + | + | + | + | + | + | + | + | + | + | + | + | + + | + | + | + | + | + | + | + | + | + + | < INTEGER: ("-")? ( + (["l","L"])? + | (["l","L"])? + | (["l","L"])? + ) > + | < #DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* > + | < #HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ > + | < #OCTAL_LITERAL: "0" (["0"-"7"])* > + | < FLOAT: ("-")? ( + (["0"-"9"])+ "." (["0"-"9"])* ()? (["f","F","d","D"])? + | "." (["0"-"9"])+ ()? (["f","F","d","D"])? + | (["0"-"9"])+ (["f","F","d","D"])? + | (["0"-"9"])+ ()? ["f","F","d","D"] + )> + | < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ > + | + | +} + +ProtoDescriptor ProtoDescriptor() : +{ + ProtoDescriptor proto = new ProtoDescriptor(); + String packageName=null; + LinkedHashMap opts = new LinkedHashMap(); + LinkedHashMap messages = new LinkedHashMap(); + LinkedHashMap enums = new LinkedHashMap(); + ArrayList extendsList = new ArrayList(); + LinkedHashMap services = new LinkedHashMap(); + ArrayList imports = new ArrayList(); + + OptionDescriptor optionD; + MessageDescriptor messageD; + EnumDescriptor enumD; + ServiceDescriptor serviceD; + MessageDescriptor extendD; + String o; +} +{ + ( + packageName=PackageID() + | +