From 80a1c26722315278c1aaadd5495d3c323ec2224b Mon Sep 17 00:00:00 2001 From: "Shawn O. Pearce" Date: Sun, 18 Nov 2012 16:25:03 -0800 Subject: [PATCH] Add /changes/{id}/revisions/{commit}/drafts Draft comments can now be managed through the REST API endpoint: GET /changes/{id}/revisions/{commit}/drafts Returns all drafts on that commit by the caller, organized as an object mapping path name to a list of comment objects. DELETE /changes/{id}/revisions/{commit}/drafts/{id} Remove a draft comment. PUT /changes/{id}/revisions/{commit}/drafts/{id} Update the contents of a draft comment. This not only supports changing the text, but also moving the comment to a different line or to an entirely different file. PUT /changes/{id}/revisions/{commit}/drafts Create a new draft, with a new unique identifier returned. Change-Id: I53eb11138fac4b29623885d01c4451f51aa5ff31 --- .../extensions/restapi/RestCollection.java | 3 +- .../reviewdb/client/PatchLineComment.java | 4 + .../gerrit/server/change/CreateDraft.java | 70 ++++++++++++ .../gerrit/server/change/DeleteDraft.java | 47 ++++++++ .../gerrit/server/change/DraftResource.java | 62 +++++++++++ .../google/gerrit/server/change/Drafts.java | 83 ++++++++++++++ .../google/gerrit/server/change/GetDraft.java | 65 +++++++++++ .../gerrit/server/change/ListDrafts.java | 79 ++++++++++++++ .../google/gerrit/server/change/Module.java | 8 ++ .../gerrit/server/change/PostReview.java | 8 +- .../google/gerrit/server/change/PutDraft.java | 103 ++++++++++++++++++ 11 files changed, 525 insertions(+), 7 deletions(-) create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraft.java create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/change/DraftResource.java create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/change/Drafts.java create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraft.java create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/change/ListDrafts.java create mode 100644 gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/RestCollection.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/RestCollection.java index e874a52b97..56ff5558e9 100644 --- a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/RestCollection.java +++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/restapi/RestCollection.java @@ -67,8 +67,9 @@ public interface RestCollection

* * @return view to list the collection. * @throws ResourceNotFoundException if the collection cannot be listed. + * @throws AuthException if the collection requires authentication. */ - RestView

list() throws ResourceNotFoundException; + RestView

list() throws ResourceNotFoundException, AuthException; /** * Parse a path component into a resource handle. diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java index f28d597452..24f7bbabad 100644 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java +++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/PatchLineComment.java @@ -138,6 +138,10 @@ public final class PatchLineComment { return lineNbr; } + public void setLine(int line) { + lineNbr = line; + } + public Account.Id getAuthor() { return author; } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java new file mode 100644 index 0000000000..01e7e3b2c1 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java @@ -0,0 +1,70 @@ +// Copyright (C) 2012 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.change; + +import com.google.common.base.Strings; +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.ResourceConflictException; +import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.reviewdb.client.Patch; +import com.google.gerrit.reviewdb.client.PatchLineComment; +import com.google.gerrit.reviewdb.client.PatchLineComment.Status; +import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.server.ChangeUtil; +import com.google.gerrit.server.change.PutDraft.Input; +import com.google.inject.Inject; +import com.google.inject.Provider; + +import java.util.Collections; + +class CreateDraft implements RestModifyView { + private final Provider db; + + @Inject + CreateDraft(Provider db) { + this.db = db; + } + + @Override + public Class inputType() { + return Input.class; + } + + @Override + public Object apply(RevisionResource rsrc, Input in) throws AuthException, + BadRequestException, ResourceConflictException, Exception { + if (Strings.isNullOrEmpty(in.path)) { + throw new BadRequestException("path must be non-empty"); + } else if (in.message == null || in.message.trim().isEmpty()) { + throw new BadRequestException("message must be non-empty"); + } else if (in.line != null && in.line <= 0) { + throw new BadRequestException("line must be > 0"); + } + + PatchLineComment c = new PatchLineComment( + new PatchLineComment.Key( + new Patch.Key(rsrc.getPatchSet().getId(), in.path), + ChangeUtil.messageUUID(db.get())), + in.line != null ? in.line : 0, + rsrc.getAuthorId(), + null); + c.setStatus(Status.DRAFT); + c.setSide(in.side == GetDraft.Side.PARENT ? (short) 0 : (short) 1); + c.setMessage(in.message.trim()); + db.get().patchComments().insert(Collections.singleton(c)); + return new GetDraft.Comment(c); + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraft.java new file mode 100644 index 0000000000..af9b84670d --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DeleteDraft.java @@ -0,0 +1,47 @@ +// Copyright (C) 2012 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.change; + +import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.server.change.DeleteDraft.Input; +import com.google.gwtorm.server.OrmException; +import com.google.inject.Inject; +import com.google.inject.Provider; + +import java.util.Collections; + +class DeleteDraft implements RestModifyView { + static class Input { + } + + private final Provider db; + + @Inject + DeleteDraft(Provider db) { + this.db = db; + } + + @Override + public Class inputType() { + return Input.class; + } + + @Override + public Object apply(DraftResource rsrc, Input input) throws OrmException { + db.get().patchComments().delete(Collections.singleton(rsrc.getComment())); + return new Object(); + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftResource.java new file mode 100644 index 0000000000..bcd8902008 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/DraftResource.java @@ -0,0 +1,62 @@ +// Copyright (C) 2012 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.change; + +import com.google.gerrit.extensions.restapi.RestResource; +import com.google.gerrit.extensions.restapi.RestView; +import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.reviewdb.client.Change; +import com.google.gerrit.reviewdb.client.PatchLineComment; +import com.google.gerrit.reviewdb.client.PatchSet; +import com.google.gerrit.server.IdentifiedUser; +import com.google.gerrit.server.project.ChangeControl; +import com.google.inject.TypeLiteral; + +public class DraftResource implements RestResource { + public static final TypeLiteral> DRAFT_KIND = + new TypeLiteral>() {}; + + private final RevisionResource rev; + private final PatchLineComment comment; + + DraftResource(RevisionResource rev, PatchLineComment c) { + this.rev = rev; + this.comment = c; + } + + public ChangeControl getControl() { + return rev.getControl(); + } + + public Change getChange() { + return getControl().getChange(); + } + + public PatchSet getPatchSet() { + return rev.getPatchSet(); + } + + PatchLineComment getComment() { + return comment; + } + + String getId() { + return comment.getKey().get(); + } + + Account.Id getAuthorId() { + return ((IdentifiedUser) getControl().getCurrentUser()).getAccountId(); + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Drafts.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Drafts.java new file mode 100644 index 0000000000..83959a0d82 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Drafts.java @@ -0,0 +1,83 @@ +// Copyright (C) 2012 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.change; + +import com.google.gerrit.extensions.registration.DynamicMap; +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.ChildCollection; +import com.google.gerrit.extensions.restapi.ResourceNotFoundException; +import com.google.gerrit.extensions.restapi.RestView; +import com.google.gerrit.reviewdb.client.PatchLineComment; +import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.server.CurrentUser; +import com.google.gerrit.server.IdentifiedUser; +import com.google.gwtorm.server.OrmException; +import com.google.inject.Inject; +import com.google.inject.Provider; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; + +class Drafts implements ChildCollection { + private final DynamicMap> views; + private final Provider user; + private final Provider list; + private final Provider dbProvider; + + @Inject + Drafts(DynamicMap> views, + Provider user, + Provider list, + Provider dbProvider) { + this.views = views; + this.user = user; + this.list = list; + this.dbProvider = dbProvider; + } + + @Override + public DynamicMap> views() { + return views; + } + + @Override + public RestView list() throws AuthException { + checkIdentifiedUser(); + return list.get(); + } + + @Override + public DraftResource parse(RevisionResource rev, String id) + throws ResourceNotFoundException, UnsupportedEncodingException, + OrmException, AuthException { + checkIdentifiedUser(); + String uuid = URLDecoder.decode(id, "UTF-8"); + for (PatchLineComment c : dbProvider.get().patchComments() + .draftByPatchSetAuthor( + rev.getPatchSet().getId(), + rev.getAuthorId())) { + if (uuid.equals(c.getKey().get())) { + return new DraftResource(rev, c); + } + } + throw new ResourceNotFoundException(id); + } + + private void checkIdentifiedUser() throws AuthException { + if (!(user.get() instanceof IdentifiedUser)) { + throw new AuthException("drafts only available to authenticated users"); + } + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraft.java new file mode 100644 index 0000000000..fbfe1d5d7b --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraft.java @@ -0,0 +1,65 @@ +// Copyright (C) 2012 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.change; + +import com.google.common.base.Strings; +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.ResourceConflictException; +import com.google.gerrit.extensions.restapi.RestReadView; +import com.google.gerrit.reviewdb.client.PatchLineComment; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.sql.Timestamp; + +class GetDraft implements RestReadView { + @Override + public Object apply(DraftResource rsrc) throws AuthException, + BadRequestException, ResourceConflictException, Exception { + return new Comment(rsrc.getComment()); + } + + static enum Side { + PARENT, REVISION; + } + + static class Comment { + final String kind = "gerritcodereview#comment"; + String id; + String path; + Side side; + Integer line; + String message; + Timestamp updated; + + Comment(PatchLineComment c) { + try { + id = URLEncoder.encode(c.getKey().get(), "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("UTF-8 encoding not supported", e); + } + path = c.getKey().getParentKey().getFileName(); + if (c.getSide() == 0) { + side = Side.PARENT; + } + if (c.getLine() > 0) { + line = c.getLine(); + } + message = Strings.emptyToNull(c.getMessage()); + updated = c.getWrittenOn(); + } + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListDrafts.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListDrafts.java new file mode 100644 index 0000000000..208f271d1f --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListDrafts.java @@ -0,0 +1,79 @@ +// Copyright (C) 2012 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.change; + +import static com.google.common.base.Objects.firstNonNull; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.ResourceConflictException; +import com.google.gerrit.extensions.restapi.RestReadView; +import com.google.gerrit.reviewdb.client.PatchLineComment; +import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.server.change.GetDraft.Comment; +import com.google.gerrit.server.change.GetDraft.Side; +import com.google.inject.Inject; +import com.google.inject.Provider; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +class ListDrafts implements RestReadView { + private final Provider db; + + @Inject + ListDrafts(Provider db) { + this.db = db; + } + + @Override + public Object apply(RevisionResource rsrc) throws AuthException, + BadRequestException, ResourceConflictException, Exception { + Map> out = Maps.newTreeMap(); + for (PatchLineComment c : db.get().patchComments() + .draftByPatchSetAuthor( + rsrc.getPatchSet().getId(), + rsrc.getAuthorId())) { + Comment o = new Comment(c); + List list = out.get(o.path); + if (list == null) { + list = Lists.newArrayList(); + out.put(o.path, list); + } + list.add(o); + } + for (List list : out.values()) { + Collections.sort(list, new Comparator() { + @Override + public int compare(Comment a, Comment b) { + int c = firstNonNull(a.side, Side.REVISION).ordinal() + - firstNonNull(b.side, Side.REVISION).ordinal(); + if (c == 0) { + c = firstNonNull(a.line, 0) - firstNonNull(b.line, 0); + } + if (c == 0) { + c = a.id.compareTo(b.id); + } + return c; + } + }); + } + return out; + } +} diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java index f0e7e468da..279e925433 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java @@ -15,6 +15,7 @@ package com.google.gerrit.server.change; import static com.google.gerrit.server.change.ChangeResource.CHANGE_KIND; +import static com.google.gerrit.server.change.DraftResource.DRAFT_KIND; import static com.google.gerrit.server.change.ReviewerResource.REVIEWER_KIND; import static com.google.gerrit.server.change.RevisionResource.REVISION_KIND; @@ -26,6 +27,7 @@ public class Module extends RestApiModule { @Override protected void configure() { DynamicMap.mapOf(binder(), CHANGE_KIND); + DynamicMap.mapOf(binder(), DRAFT_KIND); DynamicMap.mapOf(binder(), REVIEWER_KIND); DynamicMap.mapOf(binder(), REVISION_KIND); @@ -41,6 +43,12 @@ public class Module extends RestApiModule { child(CHANGE_KIND, "revisions").to(Revisions.class); post(REVISION_KIND, "review").to(PostReview.class); + child(REVISION_KIND, "drafts").to(Drafts.class); + put(REVISION_KIND, "drafts").to(CreateDraft.class); + get(DRAFT_KIND).to(GetDraft.class); + put(DRAFT_KIND).to(PutDraft.class); + delete(DRAFT_KIND).to(DeleteDraft.class); + install(new FactoryModule() { @Override protected void configure() { diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java index 59fe465b9b..1735afc79c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java @@ -82,13 +82,9 @@ class PostReview implements RestModifyView { DELETE, PUBLISH, KEEP; } - static enum Side { - PARENT, REVISION; - } - static class Comment { String id; - Side side; + GetDraft.Side side; int line; String message; } @@ -273,7 +269,7 @@ class PostReview implements RestModifyView { } e.setStatus(PatchLineComment.Status.PUBLISHED); e.setWrittenOn(timestamp); - e.setSide(c.side == Side.PARENT ? (short) 0 : (short) 1); + e.setSide(c.side == GetDraft.Side.PARENT ? (short) 0 : (short) 1); e.setMessage(c.message); (create ? ins : upd).add(e); } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java new file mode 100644 index 0000000000..38b4da1285 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java @@ -0,0 +1,103 @@ +// Copyright (C) 2012 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.change; + +import com.google.gerrit.extensions.restapi.AuthException; +import com.google.gerrit.extensions.restapi.BadRequestException; +import com.google.gerrit.extensions.restapi.DefaultInput; +import com.google.gerrit.extensions.restapi.ResourceConflictException; +import com.google.gerrit.extensions.restapi.RestModifyView; +import com.google.gerrit.reviewdb.client.Patch; +import com.google.gerrit.reviewdb.client.PatchLineComment; +import com.google.gerrit.reviewdb.server.ReviewDb; +import com.google.gerrit.server.change.GetDraft.Side; +import com.google.gerrit.server.change.PutDraft.Input; +import com.google.inject.Inject; +import com.google.inject.Provider; + +import java.sql.Timestamp; +import java.util.Collections; + +class PutDraft implements RestModifyView { + static class Input { + String kind; + String id; + String path; + Side side; + Integer line; + Timestamp updated; // Accepted but ignored. + + @DefaultInput + String message; + } + + private final Provider db; + private final Provider delete; + + @Inject + PutDraft(Provider db, Provider delete) { + this.db = db; + this.delete = delete; + } + + @Override + public Class inputType() { + return Input.class; + } + + @Override + public Object apply(DraftResource rsrc, Input in) + throws AuthException, BadRequestException, ResourceConflictException, + Exception { + if (in == null || in.message == null || in.message.trim().isEmpty()) { + return delete.get().apply(rsrc, null); + } else if (in.kind != null && !"gerritcodereview#comment".equals(in.kind)) { + throw new BadRequestException("expected kind gerritcodereview#comment"); + } else if (in.line != null && in.line < 0) { + throw new BadRequestException("line must be >= 0"); + } + + PatchLineComment c = rsrc.getComment(); + if (in.path != null + && !in.path.equals(c.getKey().getParentKey().getFileName())) { + // Updating the path alters the primary key, which isn't possible. + // Delete then recreate the comment instead of an update. + db.get().patchComments().delete(Collections.singleton(c)); + c = update(new PatchLineComment( + new PatchLineComment.Key( + new Patch.Key(rsrc.getPatchSet().getId(), in.path), + c.getKey().get()), + c.getLine(), + rsrc.getAuthorId(), + c.getParentUuid()), in); + db.get().patchComments().insert(Collections.singleton(c)); + } else { + db.get().patchComments().update(Collections.singleton(update(c, in))); + } + return new GetDraft.Comment(c); + } + + private PatchLineComment update(PatchLineComment e, Input in) { + if (in.side != null) { + e.setSide(in.side == GetDraft.Side.PARENT ? (short) 0 : (short) 1); + } + if (in.line != null) { + e.setLine(in.line); + } + e.setMessage(in.message.trim()); + e.updated(); + return e; + } +}