Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Sven Gestegård Robertz
LabComm
Commits
ac1eb49e
Commit
ac1eb49e
authored
Jul 23, 2013
by
Sven Robertz
Browse files
a bit more polished example
parent
b0357d84
Changes
10
Hide whitespace changes
Inline
Side-by-side
examples/dynamic/README
View file @
ac1eb49e
This directory contains a
n embryo of an
example of how to
generate and compile
a labcomm endpoint on the fly.
This directory contains a
small
example of how to
generate and compile
a labcomm endpoint on the fly.
NB! There is currently no connection to th
e
S
tatic
Part.java
the script static.sh builds and runs a very simpl
e
s
tatic
labcomm demo
the runme.sh script builds and runs the TestLabCommGen, which illustrates the
the script dynamic.sh builds and runs an example where labcomm is generated and woven
togerther with user-defined handler code.
the test.sh script builds and runs the TestLabCommGen, which illustrates the
on-the-fly compilation to RAM, reading the labcomm declarations and handlers from file
The handlers declaration (in handlers.txt) is experimental, and has the following format:
...
...
examples/dynamic/dynamic.sh
0 → 100644
View file @
ac1eb49e
#dummy script to test the on-the-fly compilation
java
-jar
../../compiler/labComm.jar
--java
=
gen
--javapackage
=
gen simple.lc
javac
-cp
.:gen:../../lib/java/labcomm.jar gen/
*
.java
# compile static encoder and decoder
javac
-cp
.:gen:../../lib/java/labcomm.jar
test
/StaticEncoder.java
javac
-cp
.:gen:../../lib/java/labcomm.jar
test
/StaticDecoder.java
# compile dynamic part
javac
-cp
.:../../compiler/labComm.jar:../../lib/java/labcomm.jar:../../lib/tools/beaver.jar:../../lib/tools/beaver-rt.jar:../../lib/tools/jastadd2.jar:../../lib/tools/JFlex.jar:../../lib/tools/proj.jar
test
/DynamicPart.java
javac
test
/HandlerContext.java
# run static encoder
java
-cp
.:gen:../../lib/java//labcomm.jar test.StaticEncoder encoded_data
# run dynamic part
java
-cp
.:../../compiler/labComm.jar:../../lib/java/labcomm.jar:../../lib/tools/beaver.jar:../../lib/tools/beaver-rt.jar:../../lib/tools/jastadd2.jar:../../lib/tools/JFlex.jar:../../lib/tools/proj.jar test.DynamicPart simple.lc handlers2.txt encoded_data dynamic_out
# run static decoder
java
-cp
.:gen:../../lib/java//labcomm.jar test.StaticDecoder dynamic_out
examples/dynamic/handlers2.txt
View file @
ac1eb49e
foo:handler(foo value) {
test.HandlerContext ctx = (test.HandlerContext)context;
System.out.println("foo handler from handlers.txt");
System.out.println("foo handler from handlers
2
.txt");
System.out.println("using context "+ctx.str);
ctx.x = value.x;
ctx.y = value.y;
ctx.z = value.z;
ctx.x = value.x
+ 1000
;
ctx.y = value.y
+ 1000
;
ctx.z = value.z
+ 1000
;
System.out.println(value.x);
System.out.println(value.y);
System.out.println(value.z);
...
...
@@ -15,4 +15,6 @@ foo:handler(foo value) {
}###
bar:handler(int value) {
System.out.println("bar:"+value);
test.HandlerContext ctx = (test.HandlerContext)context;
ctx.bar = value + 1000;
}###
examples/dynamic/static.sh
0 → 100644
View file @
ac1eb49e
#dummy script to test the static part
# run labcomm compilation
java
-jar
../../compiler/labComm.jar
--java
=
gen
--javapackage
=
gen simple.lc
# compile example programs
javac
-cp
.:gen:../../lib/java/labcomm.jar
test
/StaticEncoder.java
javac
-cp
.:gen:../../lib/java/labcomm.jar
test
/StaticDecoder.java
# run example programs
java
-cp
.:gen:../../lib/java//labcomm.jar test.StaticEncoder encoded_data
java
-cp
.:gen:../../lib/java//labcomm.jar test.StaticDecoder encoded_data
examples/dynamic/
runme
.sh
→
examples/dynamic/
test
.sh
View file @
ac1eb49e
File moved
examples/dynamic/test/DynamicPart.java
View file @
ac1eb49e
package
test
;
import
java.io.ByteArrayInputStream
;
import
java.io.EOFException
;
import
java.io.FileInputStream
;
...
...
@@ -6,6 +7,7 @@ import java.io.FileOutputStream;
import
java.io.FileReader
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.lang.reflect.Constructor
;
import
java.lang.reflect.Field
;
import
java.lang.reflect.InvocationTargetException
;
import
java.lang.reflect.Method
;
...
...
@@ -25,19 +27,13 @@ import AST.LabCommScanner;
import
AST.Program
;
import
beaver.Parser.Exception
;
/** Simple test class that illustrates on-the-fly generation, compilation, and loading
* of labcomm handlers.
*
* It reads, modifies and writes the "foo" and "bar" sample types
*/
public
class
DynamicPart
{
private
static
final
String
SAMPLE_NAME_FOO
=
"foo"
;
private
static
final
String
SAMPLE_NAME_BAR
=
"bar"
;
/** A class representing the source code for one Labcomm handler */
static
final
String
handlerClassName
=
"HandlerContainer"
;
static
class
HandlerSrc
{
private
String
sampleName
;
private
String
param
;
...
...
@@ -57,15 +53,16 @@ public class DynamicPart {
}
}
public
void
doTest
(
InRAMCompiler
irc
,
String
inFile
,
String
outFile
)
{
System
.
out
.
println
(
"*** DynamicPart reading from "
+
inFile
);
decodeTest
(
irc
,
tmpFile
,
SAMPLE_NAME_FOO
,
SAMPLE_NAME_BAR
);
System
.
out
.
println
(
"*** DynamicPart writing to "
+
outFile
);
encodeTest
(
irc
,
tmpFile
);
public
void
doTest
(
InRAMCompiler
irc
,
String
iFile
,
String
oFile
)
{
HandlerContext
ctxt
=
new
HandlerContext
();
System
.
out
.
println
(
"*** reading file "
+
iFile
);
decodeTest
(
irc
,
ctxt
,
iFile
,
SAMPLE_NAME_FOO
,
SAMPLE_NAME_BAR
);
System
.
out
.
println
(
"*** writing "
+
oFile
);
encodeTest
(
irc
,
ctxt
,
oFile
);
}
/**
* @param args
(types.lc, handlers.txt, inputfile, outputfile)
* @param args
*/
public
static
void
main
(
String
[]
args
)
{
...
...
@@ -90,12 +87,11 @@ public class DynamicPart {
InRAMCompiler
irc
=
generateCode
(
labcommStr
,
handlers
);
if
(
irc
!=
null
)
{
String
i
n
File
=
args
[
2
];
String
o
ut
File
=
args
[
3
];
new
DynamicPart
().
doTest
(
irc
,
i
n
File
,
o
ut
File
);
String
iFile
=
args
[
2
];
String
oFile
=
args
[
3
];
new
DynamicPart
().
doTest
(
irc
,
iFile
,
oFile
);
}
}
public
static
void
generateHandlers
(
String
srcStr
,
HashMap
<
String
,
String
>
handlers
)
{
int
pos
=
0
;
while
(
pos
<
srcStr
.
length
())
{
...
...
@@ -217,6 +213,78 @@ public class DynamicPart {
}
System
.
out
.
println
(
"Generated labcomm code:"
);
InRAMCompiler
irc
=
new
InRAMCompilerJavax
(
"labcomm.generated"
,
TestLabcommGen
.
class
.
getClassLoader
());
StringBuilder
handlerClass
=
new
StringBuilder
();
StringBuilder
handlerMethods
=
new
StringBuilder
();
handlerClass
.
append
(
"package labcomm.generated;\n"
);
handlerClass
.
append
(
"import test.TestLabcommGen;\n"
);
handlerClass
.
append
(
"public class "
+
handlerClassName
+
" implements "
);
String
handlerAttributes
=
"Object context;\n"
;
String
handlerConstructor
=
"public "
+
handlerClassName
+
"(Object context){ this.context=context;}\n"
;
Iterator
<
String
>
i
=
genCode
.
keySet
().
iterator
();
try
{
while
(
i
.
hasNext
()){
final
String
sampleName
=
i
.
next
();
final
String
src
=
genCode
.
get
(
sampleName
);
handlerClass
.
append
(
sampleName
+
".Handler"
);
if
(
i
.
hasNext
())
{
handlerClass
.
append
(
", "
);
}
handlerMethods
.
append
(
handlers
.
get
(
sampleName
));
handlerMethods
.
append
(
"\n"
);
System
.
out
.
println
(
"***"
+
sampleName
+
"\n"
+
src
);
irc
.
compile
(
sampleName
,
src
);
// while iterating, compile the labcomm generated code
}
handlerClass
.
append
(
"{\n"
);
handlerClass
.
append
(
handlerAttributes
);
handlerClass
.
append
(
handlerConstructor
);
handlerClass
.
append
(
handlerMethods
.
toString
());
handlerClass
.
append
(
"}\n"
);
System
.
out
.
println
(
"-------------------------------------"
);
final
String
handlerSrc
=
handlerClass
.
toString
();
System
.
out
.
println
(
handlerSrc
);
irc
.
compile
(
handlerClassName
,
handlerSrc
);
// compile the generated handler class
}
catch
(
IllegalArgumentException
e
)
{
e
.
printStackTrace
();
}
catch
(
SecurityException
e
)
{
e
.
printStackTrace
();
}
catch
(
ClassNotFoundException
e
)
{
e
.
printStackTrace
();
}
catch
(
IllegalAccessException
e
)
{
e
.
printStackTrace
();
}
catch
(
InvocationTargetException
e
)
{
e
.
printStackTrace
();
}
catch
(
NoSuchMethodException
e
)
{
e
.
printStackTrace
();
}
System
.
out
.
println
(
"================================"
);
return
irc
;
}
/** generate labcomm code and compile handlers. Version for separate handler classes
*
* @param lcAST - the AST of the labcomm declaration
* @param handlers - a map <name, source> of handlers for the types in ast
* @return an InRAMCompiler object containing the generated clases
*/
private
static
InRAMCompiler
handleAstSeparate
(
Program
lcAST
,
HashMap
<
String
,
String
>
handlers
)
{
Map
<
String
,
String
>
genCode
=
new
HashMap
<
String
,
String
>();
try
{
lcAST
.
J_gen
(
genCode
,
"labcomm.generated"
);
}
catch
(
IOException
e
)
{
e
.
printStackTrace
();
}
System
.
out
.
println
(
"Generated labcomm code:"
);
InRAMCompiler
irc
=
new
InRAMCompilerJavax
(
"labcomm.generated"
,
null
);
Iterator
<
String
>
i
=
genCode
.
keySet
().
iterator
();
...
...
@@ -253,18 +321,19 @@ public class DynamicPart {
}
/** test method
*/
private
void
decodeTest
(
InRAMCompiler
irc
,
String
tmpFile
,
String
...
sampleNames
)
{
private
void
decodeTest
(
InRAMCompiler
irc
,
HandlerContext
ctxt
,
String
tmpFile
,
String
...
sampleNames
)
{
try
{
FileInputStream
in
=
new
FileInputStream
(
tmpFile
);
LabCommDecoderChannel
dec
=
new
LabCommDecoderChannel
(
in
);
Class
handlerClass
=
irc
.
load
(
handlerClassName
);
Constructor
hcc
=
handlerClass
.
getDeclaredConstructor
(
Object
.
class
);
Object
handler
=
hcc
.
newInstance
(
ctxt
);
for
(
String
sampleName
:
sampleNames
)
{
System
.
out
.
println
(
"registering handler for "
+
sampleName
);
Class
sampleClass
=
irc
.
load
(
sampleName
);
Class
handlerClass
=
irc
.
load
(
"gen_"
+
sampleName
+
"Handler"
);
Class
handlerInterface
=
irc
.
load
(
sampleName
+
"$Handler"
);
Object
handler
=
handlerClass
.
newInstance
();
Method
reg
=
sampleClass
.
getDeclaredMethod
(
"register"
,
LabCommDecoder
.
class
,
handlerInterface
);
reg
.
invoke
(
sampleClass
,
dec
,
handler
);
}
...
...
@@ -283,21 +352,21 @@ public class DynamicPart {
}
/** test encoding
*/
private
void
encodeTest
(
InRAMCompiler
irc
,
String
tmpFile
)
{
private
void
encodeTest
(
InRAMCompiler
irc
,
HandlerContext
ctxt
,
String
tmpFile
)
{
try
{
Class
fc
=
irc
.
load
(
SAMPLE_NAME_FOO
);
Class
bc
=
irc
.
load
(
SAMPLE_NAME_BAR
);
/* create sample class and instance objects */
Object
f
=
fc
.
newInstance
();
Field
x
=
fc
.
getDeclaredField
(
"x"
);
Field
y
=
fc
.
getDeclaredField
(
"y"
);
Field
z
=
fc
.
getDeclaredField
(
"z"
);
x
.
setInt
(
f
,
10
);
y
.
setInt
(
f
,
11
);
z
.
setInt
(
f
,
12
);
x
.
setInt
(
f
,
ctxt
.
x
);
y
.
setInt
(
f
,
ctxt
.
y
);
z
.
setInt
(
f
,
ctxt
.
z
);
FileOutputStream
out
=
new
FileOutputStream
(
tmpFile
);
LabCommEncoderChannel
enc
=
new
LabCommEncoderChannel
(
out
);
...
...
@@ -314,7 +383,7 @@ public class DynamicPart {
regBar
.
invoke
(
bc
,
enc
);
Method
doEncodeBar
=
bc
.
getDeclaredMethod
(
"encode"
,
LabCommEncoder
.
class
,
Integer
.
TYPE
);
doEncodeBar
.
invoke
(
bc
,
enc
,
42
);
doEncodeBar
.
invoke
(
bc
,
enc
,
ctxt
.
bar
);
out
.
close
();
}
catch
(
Throwable
e
)
{
...
...
@@ -325,6 +394,95 @@ public class DynamicPart {
/** dummy test creating instances of sample and handler, and calling handle*/
private
static
void
dummyTest
(
InRAMCompiler
irc
)
{
try
{
Class
hc
=
irc
.
load
(
handlerClassName
);
Constructor
hcc
=
hc
.
getDeclaredConstructor
(
Object
.
class
);
// Object h = hc.newInstance();
Object
h
=
hcc
.
newInstance
(
new
HandlerContext
());
Class
fc
=
irc
.
load
(
SAMPLE_NAME_FOO
);
Object
f
=
fc
.
newInstance
();
Field
x
=
fc
.
getDeclaredField
(
"x"
);
Field
y
=
fc
.
getDeclaredField
(
"y"
);
Field
z
=
fc
.
getDeclaredField
(
"z"
);
x
.
setInt
(
f
,
10
);
y
.
setInt
(
f
,
11
);
z
.
setInt
(
f
,
12
);
Method
m
;
try
{
m
=
hc
.
getDeclaredMethod
(
"handle_"
+
SAMPLE_NAME_FOO
,
fc
);
m
.
invoke
(
h
,
f
);
}
catch
(
SecurityException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
catch
(
NoSuchMethodException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
catch
(
IllegalArgumentException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
catch
(
InvocationTargetException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
}
catch
(
ClassNotFoundException
e
)
{
e
.
printStackTrace
();
}
catch
(
InstantiationException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
catch
(
IllegalAccessException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
catch
(
SecurityException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
catch
(
NoSuchFieldException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
catch
(
IllegalArgumentException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
catch
(
InvocationTargetException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
catch
(
NoSuchMethodException
e
)
{
// TODO Auto-generated catch block
e
.
printStackTrace
();
}
}
/** test method
*/
private
static
void
decodeTestSeparate
(
InRAMCompiler
irc
,
String
tmpFile
,
String
...
sampleNames
)
{
try
{
FileInputStream
in
=
new
FileInputStream
(
tmpFile
);
LabCommDecoderChannel
dec
=
new
LabCommDecoderChannel
(
in
);
for
(
String
sampleName
:
sampleNames
)
{
System
.
out
.
println
(
"registering handler for "
+
sampleName
);
Class
sampleClass
=
irc
.
load
(
sampleName
);
Class
handlerClass
=
irc
.
load
(
"gen_"
+
sampleName
+
"Handler"
);
Class
handlerInterface
=
irc
.
load
(
sampleName
+
"$Handler"
);
Object
handler
=
handlerClass
.
newInstance
();
Method
reg
=
sampleClass
.
getDeclaredMethod
(
"register"
,
LabCommDecoder
.
class
,
handlerInterface
);
reg
.
invoke
(
sampleClass
,
dec
,
handler
);
}
try
{
System
.
out
.
println
(
"*** decoding:"
);
dec
.
run
();
}
catch
(
EOFException
e
)
{
System
.
out
.
println
(
"*** reached EOF ***"
);
}
in
.
close
();
}
catch
(
Throwable
e
)
{
e
.
printStackTrace
();
}
}
/** dummy test creating instances of sample and handler, and calling handle*/
private
static
void
dummyTestSeparate
(
InRAMCompiler
irc
)
{
try
{
Class
hc
=
irc
.
load
(
"gen_"
+
SAMPLE_NAME_FOO
+
"Handler"
);
Object
h
=
hc
.
newInstance
();
...
...
examples/dynamic/test/HandlerContext.java
0 → 100644
View file @
ac1eb49e
package
test
;
public
class
HandlerContext
{
public
final
String
str
=
"HandlerContext"
;
public
int
x
,
y
,
z
,
bar
;
}
examples/dynamic/test/StaticDecoder.java
0 → 100644
View file @
ac1eb49e
package
test
;
import
gen.foo
;
import
gen.bar
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.io.InputStream
;
import
se.lth.control.labcomm.LabCommDecoderChannel
;
public
class
StaticDecoder
implements
foo
.
Handler
,
bar
.
Handler
{
LabCommDecoderChannel
decoder
;
public
StaticDecoder
(
InputStream
in
)
throws
Exception
{
decoder
=
new
LabCommDecoderChannel
(
in
);
foo
.
register
(
decoder
,
this
);
bar
.
register
(
decoder
,
this
);
}
public
void
run
()
throws
Exception
{
try
{
System
.
out
.
println
(
"Running decoder."
);
decoder
.
run
();
}
catch
(
java
.
io
.
EOFException
e
)
{
System
.
out
.
println
(
"Decoder reached end of file."
);
}
}
public
void
handle_foo
(
foo
d
)
throws
java
.
io
.
IOException
{
System
.
out
.
println
(
"Got foo, x="
+
d
.
x
+
", y="
+
d
.
y
+
", z="
+
d
.
z
);
}
public
void
handle_bar
(
int
d
)
throws
java
.
io
.
IOException
{
System
.
out
.
println
(
"Got bar: "
+
d
);
}
public
static
void
main
(
String
[]
arg
)
throws
Exception
{
FileInputStream
fis
=
new
FileInputStream
(
new
File
(
arg
[
0
]));
StaticDecoder
dec
=
new
StaticDecoder
(
fis
);
dec
.
run
();
fis
.
close
();
}
}
examples/dynamic/test/StaticEncoder.java
0 → 100644
View file @
ac1eb49e
package
test
;
import
java.io.File
;
import
java.io.FileOutputStream
;
import
java.io.OutputStream
;
import
se.lth.control.labcomm.LabCommEncoderChannel
;
import
gen.foo
;
import
gen.bar
;
public
class
StaticEncoder
{
LabCommEncoderChannel
encoder
;
public
StaticEncoder
(
OutputStream
out
)
throws
Exception
{
encoder
=
new
LabCommEncoderChannel
(
out
);
foo
.
register
(
encoder
);
bar
.
register
(
encoder
);
}
public
void
doEncode
()
throws
java
.
io
.
IOException
{
foo
f
=
new
foo
();
f
.
x
=
17
;
f
.
y
=
42
;
f
.
z
=
37
;
int
b
=
13
;
System
.
out
.
println
(
"Encoding foo, x="
+
f
.
x
+
", y="
+
f
.
y
+
", z="
+
f
.
z
);
foo
.
encode
(
encoder
,
f
);
System
.
out
.
println
(
"Encoding bar: "
+
b
);
bar
.
encode
(
encoder
,
b
);
}
public
static
void
main
(
String
[]
arg
)
throws
Exception
{
FileOutputStream
fos
=
new
FileOutputStream
(
arg
[
0
]);
StaticEncoder
enc
=
new
StaticEncoder
(
fos
);
enc
.
doEncode
();
fos
.
close
();
}
}
examples/dynamic/test/StaticPart.java
deleted
100644 → 0
View file @
b0357d84
package
test
;
import
java.io.File
;
import
java.io.FileOutputStream
;
import
java.io.OutputStream
;
import
se.lth.control.labcomm.LabCommEncoderChannel
;
import
java.io.FileInputStream
;
import
java.io.InputStream
;
import
se.lth.control.labcomm.LabCommDecoderChannel
;
public
class
StaticPart
{
/**
* Simple encoder
*/
public
static
class
Encoder
{
LabCommEncoderChannel
encoder
;
public
Encoder
(
OutputStream
out
)
throws
Exception
{
encoder
=
new
LabCommEncoderChannel
(
out
);
foo
.
register
(
encoder
);
bar
.
register
(
encoder
);
}
public
void
doEncode
()
throws
java
.
io
.
IOException
{
foo
f
=
new
foo
();
f
.
x
=
17
;
f
.
y
=
42
;
f
.
z
=
37
;
int
b
=
13
;
System
.
out
.
println
(
"Encoding foo, x="
+
f
.
x
+
", y="
+
f
.
y
+
", z="
+
f
.
z
);
foo
.
encode
(
encoder
,
f
);
System
.
out
.
println
(
"Encoding bar: "
+
b
);
bar
.
encode
(
encoder
,
b
);
}
}
public
static
class
Decoder
implements
foo
.
Handler
,
bar
.
Handler
{
LabCommDecoderChannel
decoder
;
public
Decoder
(
InputStream
in
)
throws
Exception
{
decoder
=
new
LabCommDecoderChannel
(
in
);
foo
.
register
(
decoder
,
this
);
bar
.
register
(
decoder
,
this
);
}
public
void
doDecode
()
throws
java
.
io
.
IOException
{
try
{
System
.
out
.
println
(
"Running decoder."
);
decoder
.
run
();
}
catch
(
java
.
io
.
EOFException
e
)
{
System
.
out
.
println
(
"Decoder reached end of file."
);
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
public
void
handle_foo
(
foo
d
)
throws
java
.
io
.
IOException
{
System
.
out
.
println
(
"Got foo, x="
+
d
.
x
+
", y="
+
d
.
y
+
", z="
+
d
.
z
);
}
public
void
handle_bar
(
int
d
)
throws
java
.
io
.
IOException
{
System
.
out
.
println
(
"Got bar: "
+
d
);
}
}
public
static
void
main
(
String
[]
arg
)
throws
Exception
{
FileOutputStream
fos
=
new
FileOutputStream
(
arg
[
0
]);
FileInputStream
fis
=
new
FileInputStream
(
new
File
(
arg
[
0
]));
Encoder
enc
=
new
Encoder
(
fos
);
Decoder
dec
=
new
Decoder
(
fis
);
enc
.
doEncode
();
dec
.
doDecode
();
fos
.
close
();
fis
.
close
();
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment