From 617532592f5b825ec2a692b403bf8fd7dbff81da Mon Sep 17 00:00:00 2001 From: H4mes <123077808+H4mes@users.noreply.github.com> Date: Wed, 27 Mar 2024 07:45:42 +0800 Subject: [PATCH 1/3] Refactor Authors, Sources and Tags classes Added classes for author, source and tag. Added tag pill UI elements for authors, sources and tags. --- .../java/seedu/address/logic/Messages.java | 26 ++---- .../articlecommands/EditArticleCommand.java | 57 +++++++------ .../logic/parser/AddArticleCommandParser.java | 18 +++-- .../seedu/address/logic/parser/CliSyntax.java | 2 +- .../parser/EditArticleCommandParser.java | 51 ++++++------ .../address/logic/parser/ParserUtil.java | 35 ++++---- .../seedu/address/model/article/Article.java | 79 +++++++++++++++---- .../seedu/address/model/article/Author.java | 60 ++++++++++++++ .../seedu/address/model/article/Source.java | 60 ++++++++++++++ .../java/seedu/address/model/tag/Tag.java | 2 +- .../model/util/SampleArticleDataUtil.java | 38 ++++++++- .../address/storage/JsonAdaptedArticle.java | 79 +++++++++++++------ .../address/storage/JsonAdaptedAuthor.java | 46 +++++++++++ .../address/storage/JsonAdaptedSource.java | 46 +++++++++++ .../java/seedu/address/ui/ArticleCard.java | 26 +++--- src/main/resources/view/ArticleListCard.fxml | 8 +- src/main/resources/view/DarkTheme.css | 28 +++++++ 17 files changed, 516 insertions(+), 145 deletions(-) create mode 100644 src/main/java/seedu/address/model/article/Author.java create mode 100644 src/main/java/seedu/address/model/article/Source.java create mode 100644 src/main/java/seedu/address/storage/JsonAdaptedAuthor.java create mode 100644 src/main/java/seedu/address/storage/JsonAdaptedSource.java diff --git a/src/main/java/seedu/address/logic/Messages.java b/src/main/java/seedu/address/logic/Messages.java index 663923443aa..99e90c7e782 100644 --- a/src/main/java/seedu/address/logic/Messages.java +++ b/src/main/java/seedu/address/logic/Messages.java @@ -1,6 +1,5 @@ package seedu.address.logic; -import java.time.format.DateTimeFormatter; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -56,27 +55,16 @@ public static String format(Person person) { * Formats the {@code article} for display to the user. */ public static String format(Article article) { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"); final StringBuilder builder = new StringBuilder(); builder.append(article.getTitle()) - .append("; Authors: "); - for (int i = 0; i < article.getAuthors().length; i++) { - builder.append(article.getAuthors()[i]); - if (i < article.getAuthors().length - 1) { - builder.append(", "); - } - } - builder.append("; Publication Date: ") + .append("; Authors: ") + .append(article.getAuthors()) + .append("; Publication Date: ") .append(article.getPublicationDateAsString()) - .append("; Source: "); - for (int i = 0; i < article.getSources().length; i++) { - builder.append(article.getSources()[i]); - if (i < article.getSources().length - 1) { - builder.append(", "); - } - } - builder.append("; Category: ") - .append(article.getCategory()) + .append("; Sources: ") + .append(article.getSources()) + .append("; Tags: ") + .append(article.getTags()) .append("; Status: ") .append(article.getStatus()); return builder.toString(); diff --git a/src/main/java/seedu/address/logic/commands/articlecommands/EditArticleCommand.java b/src/main/java/seedu/address/logic/commands/articlecommands/EditArticleCommand.java index 1675a3d934b..0e315c92209 100644 --- a/src/main/java/seedu/address/logic/commands/articlecommands/EditArticleCommand.java +++ b/src/main/java/seedu/address/logic/commands/articlecommands/EditArticleCommand.java @@ -4,10 +4,12 @@ import static seedu.address.model.Model.PREDICATE_SHOW_ALL_ARTICLES; import java.time.LocalDateTime; -import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Set; import seedu.address.commons.core.index.Index; import seedu.address.commons.util.CollectionUtil; @@ -18,6 +20,9 @@ import seedu.address.model.Model; import seedu.address.model.article.Article; import seedu.address.model.article.Article.Status; +import seedu.address.model.article.Author; +import seedu.address.model.article.Source; +import seedu.address.model.tag.Tag; /** * Edits the details of an existing article in the article book. @@ -86,15 +91,15 @@ private static Article createEditedArticle(Article articleToEdit, EditArticleDes assert articleToEdit != null; String title = editArticleDescriptor.getTitle().orElse(articleToEdit.getTitle()); - String[] authors = editArticleDescriptor.getAuthors().orElse(articleToEdit.getAuthors()); + Set authors = editArticleDescriptor.getAuthors().orElse(articleToEdit.getAuthors()); LocalDateTime publicationDate = editArticleDescriptor.getPublicationDate() .orElse(articleToEdit.getPublicationDate()); - String[] sources = editArticleDescriptor.getSources().orElse(articleToEdit.getSources()); - String category = editArticleDescriptor.getCategory().orElse(articleToEdit.getCategory()); + Set sources = editArticleDescriptor.getSources().orElse(articleToEdit.getSources()); + Set tags = editArticleDescriptor.getTags().orElse(articleToEdit.getTags()); Status status = editArticleDescriptor.getStatus().orElse(articleToEdit.getStatus()); return new Article(title, authors, publicationDate, - sources, category, status); // Include all article attributes here. + sources, tags, status); // Include all article attributes here. } @Override @@ -128,10 +133,10 @@ public String toString() { public static class EditArticleDescriptor { private String title; - private String[] authors; + private Set authors; private LocalDateTime publicationDate; - private String[] sources; - private String category; + private Set sources; + private Set tags; private Status status; public EditArticleDescriptor() {} @@ -143,8 +148,8 @@ public EditArticleDescriptor(EditArticleDescriptor toCopy) { setTitle(toCopy.title); setAuthors(toCopy.authors); setPublicationDate(toCopy.publicationDate); - setSource(toCopy.sources); - setCategory(toCopy.category); + setSources(toCopy.sources); + setTags(toCopy.tags); setStatus(toCopy.status); } @@ -152,7 +157,7 @@ public EditArticleDescriptor(EditArticleDescriptor toCopy) { * Returns true if at least one field is edited. */ public boolean isAnyFieldEdited() { - return CollectionUtil.isAnyNonNull(title, authors, publicationDate, sources, category, status); + return CollectionUtil.isAnyNonNull(title, authors, publicationDate, sources, tags, status); } public void setTitle(String title) { @@ -163,12 +168,12 @@ public Optional getTitle() { return Optional.ofNullable(title); } - public void setAuthors(String[] authors) { - this.authors = authors; + public void setAuthors(Set authors) { + this.authors = (authors != null) ? new HashSet<>(authors) : null; } - public Optional getAuthors() { - return Optional.ofNullable(authors); + public Optional> getAuthors() { + return (authors != null) ? Optional.of(Collections.unmodifiableSet(authors)) : Optional.empty(); } public void setPublicationDate(LocalDateTime publicationDate) { @@ -179,20 +184,20 @@ public Optional getPublicationDate() { return Optional.ofNullable(publicationDate); } - public void setSource(String[] sources) { - this.sources = sources; + public void setSources(Set sources) { + this.sources = (sources != null) ? new HashSet<>(sources) : null; } - public Optional getSources() { - return Optional.ofNullable(sources); + public Optional> getSources() { + return (sources != null) ? Optional.of(Collections.unmodifiableSet(sources)) : Optional.empty(); } - public void setCategory(String category) { - this.category = category; + public void setTags(Set tags) { + this.tags = (tags != null) ? new HashSet<>(tags) : null; } - public Optional getCategory() { - return Optional.ofNullable(category); + public Optional> getTags() { + return (tags != null) ? Optional.of(Collections.unmodifiableSet(tags)) : Optional.empty(); } public void setStatus(Status status) { @@ -218,10 +223,10 @@ public boolean equals(Object other) { // Add more equality checks for article attributes below here. return Objects.equals(title, otherEditArticleDescriptor.title) - && Arrays.equals(authors, otherEditArticleDescriptor.authors) + && Objects.equals(authors, otherEditArticleDescriptor.authors) && Objects.equals(publicationDate, otherEditArticleDescriptor.publicationDate) - && Arrays.equals(sources, otherEditArticleDescriptor.sources) - && Objects.equals(category, otherEditArticleDescriptor.category) + && Objects.equals(sources, otherEditArticleDescriptor.sources) + && Objects.equals(tags, otherEditArticleDescriptor.tags) && Objects.equals(status, otherEditArticleDescriptor.status); } diff --git a/src/main/java/seedu/address/logic/parser/AddArticleCommandParser.java b/src/main/java/seedu/address/logic/parser/AddArticleCommandParser.java index 1257b480f29..be78b8c93b2 100644 --- a/src/main/java/seedu/address/logic/parser/AddArticleCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/AddArticleCommandParser.java @@ -1,19 +1,23 @@ package seedu.address.logic.parser; import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ARTICLETAG; import static seedu.address.logic.parser.CliSyntax.PREFIX_AUTHOR; -import static seedu.address.logic.parser.CliSyntax.PREFIX_CATEGORY; import static seedu.address.logic.parser.CliSyntax.PREFIX_PUBLICATION_DATE; import static seedu.address.logic.parser.CliSyntax.PREFIX_SOURCE; import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS; import static seedu.address.logic.parser.CliSyntax.PREFIX_TITLE; import java.time.LocalDateTime; +import java.util.Set; import java.util.stream.Stream; import seedu.address.logic.commands.articlecommands.AddArticleCommand; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.article.Article; +import seedu.address.model.article.Author; +import seedu.address.model.article.Source; +import seedu.address.model.tag.Tag; /** * Parses input arguments and creates a new AddArticleCommand object @@ -28,22 +32,22 @@ public class AddArticleCommandParser implements Parser { public AddArticleCommand parse(String args) throws ParseException { ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TITLE, PREFIX_AUTHOR, PREFIX_PUBLICATION_DATE, PREFIX_SOURCE, - PREFIX_CATEGORY, PREFIX_STATUS); + PREFIX_ARTICLETAG, PREFIX_STATUS); //Temporarily reinstate source requirement if (!arePrefixesPresent(argMultimap, PREFIX_TITLE, PREFIX_AUTHOR, PREFIX_PUBLICATION_DATE, PREFIX_SOURCE, - PREFIX_CATEGORY, PREFIX_STATUS) || !argMultimap.getPreamble().isEmpty()) { + PREFIX_ARTICLETAG, PREFIX_STATUS) || !argMultimap.getPreamble().isEmpty()) { throw new ParseException(String.format(MESSAGE_INVALID_COMMAND_FORMAT, AddArticleCommand.MESSAGE_USAGE)); } String title = ParserUtil.parseTitle(argMultimap.getValue(PREFIX_TITLE).get()); - String[] authorList = ParserUtil.parseAuthors(argMultimap.getAllValues(PREFIX_AUTHOR)); + Set authorList = ParserUtil.parseAuthors(argMultimap.getAllValues(PREFIX_AUTHOR)); LocalDateTime publicationDate = ParserUtil.parsePublicationDate(argMultimap.getValue(PREFIX_PUBLICATION_DATE) .get()); - String[] sourceList = ParserUtil.parseSources(argMultimap.getAllValues(PREFIX_SOURCE)); - String category = ParserUtil.parseCategory(argMultimap.getValue(PREFIX_CATEGORY).get()); + Set sourceList = ParserUtil.parseSources(argMultimap.getAllValues(PREFIX_SOURCE)); + Set tagList = ParserUtil.parseTags(argMultimap.getAllValues(PREFIX_ARTICLETAG)); Article.Status status = (Article.Status) ParserUtil.parseStatus(argMultimap.getValue(PREFIX_STATUS).get()); - Article article = new Article(title, authorList, publicationDate, sourceList, category, status); + Article article = new Article(title, authorList, publicationDate, sourceList, tagList, status); return new AddArticleCommand(article); } diff --git a/src/main/java/seedu/address/logic/parser/CliSyntax.java b/src/main/java/seedu/address/logic/parser/CliSyntax.java index c47c69ec8b8..3cda699e71d 100644 --- a/src/main/java/seedu/address/logic/parser/CliSyntax.java +++ b/src/main/java/seedu/address/logic/parser/CliSyntax.java @@ -16,7 +16,7 @@ public class CliSyntax { public static final Prefix PREFIX_AUTHOR = new Prefix("A/"); public static final Prefix PREFIX_PUBLICATION_DATE = new Prefix("D/"); public static final Prefix PREFIX_SOURCE = new Prefix("SRC/"); - public static final Prefix PREFIX_CATEGORY = new Prefix("C/"); + public static final Prefix PREFIX_ARTICLETAG = new Prefix("TAG/"); public static final Prefix PREFIX_STATUS = new Prefix("S/"); } diff --git a/src/main/java/seedu/address/logic/parser/EditArticleCommandParser.java b/src/main/java/seedu/address/logic/parser/EditArticleCommandParser.java index 165826cc1f0..f5405bbc4dc 100644 --- a/src/main/java/seedu/address/logic/parser/EditArticleCommandParser.java +++ b/src/main/java/seedu/address/logic/parser/EditArticleCommandParser.java @@ -2,22 +2,27 @@ import static java.util.Objects.requireNonNull; import static seedu.address.logic.Messages.MESSAGE_INVALID_COMMAND_FORMAT; +import static seedu.address.logic.parser.CliSyntax.PREFIX_ARTICLETAG; import static seedu.address.logic.parser.CliSyntax.PREFIX_AUTHOR; -import static seedu.address.logic.parser.CliSyntax.PREFIX_CATEGORY; import static seedu.address.logic.parser.CliSyntax.PREFIX_PUBLICATION_DATE; import static seedu.address.logic.parser.CliSyntax.PREFIX_SOURCE; import static seedu.address.logic.parser.CliSyntax.PREFIX_STATUS; +import static seedu.address.logic.parser.CliSyntax.PREFIX_TAG; import static seedu.address.logic.parser.CliSyntax.PREFIX_TITLE; +import java.util.Collection; import java.util.Collections; -import java.util.List; import java.util.Optional; +import java.util.Set; import seedu.address.commons.core.index.Index; import seedu.address.logic.commands.EditCommand; import seedu.address.logic.commands.articlecommands.EditArticleCommand; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.article.Article; +import seedu.address.model.article.Author; +import seedu.address.model.article.Source; +import seedu.address.model.tag.Tag; /** * Parses input arguments and creates a new EditArticleCommand object @@ -33,7 +38,7 @@ public EditArticleCommand parse(String args) throws ParseException { requireNonNull(args); ArgumentMultimap argMultimap = ArgumentTokenizer.tokenize(args, PREFIX_TITLE, PREFIX_AUTHOR, PREFIX_PUBLICATION_DATE, PREFIX_SOURCE, - PREFIX_CATEGORY, PREFIX_STATUS); + PREFIX_ARTICLETAG, PREFIX_STATUS); Index index; @@ -44,7 +49,7 @@ public EditArticleCommand parse(String args) throws ParseException { pe); } - argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_TITLE, PREFIX_PUBLICATION_DATE, PREFIX_CATEGORY, PREFIX_STATUS); + argMultimap.verifyNoDuplicatePrefixesFor(PREFIX_TITLE, PREFIX_PUBLICATION_DATE, PREFIX_STATUS); EditArticleCommand.EditArticleDescriptor editArticleDescriptor = new EditArticleCommand.EditArticleDescriptor(); @@ -55,16 +60,14 @@ public EditArticleCommand parse(String args) throws ParseException { editArticleDescriptor.setPublicationDate(ParserUtil.parsePublicationDate(argMultimap .getValue(PREFIX_PUBLICATION_DATE).get())); } - if (argMultimap.getValue(PREFIX_CATEGORY).isPresent()) { - editArticleDescriptor.setCategory(ParserUtil.parseCategory(argMultimap.getValue(PREFIX_CATEGORY).get())); - } if (argMultimap.getValue(PREFIX_STATUS).isPresent()) { editArticleDescriptor.setStatus((Article.Status) ParserUtil.parseStatus(argMultimap.getValue(PREFIX_STATUS) .get())); } parseAuthorsForEdit(argMultimap.getAllValues(PREFIX_AUTHOR)).ifPresent(editArticleDescriptor::setAuthors); - parseSourcesForEdit(argMultimap.getAllValues(PREFIX_SOURCE)).ifPresent(editArticleDescriptor::setSource); + parseSourcesForEdit(argMultimap.getAllValues(PREFIX_SOURCE)).ifPresent(editArticleDescriptor::setSources); + parseTagsForEdit(argMultimap.getAllValues(PREFIX_TAG)).ifPresent(editArticleDescriptor::setTags); if (!editArticleDescriptor.isAnyFieldEdited()) { throw new ParseException(EditCommand.MESSAGE_NOT_EDITED); @@ -73,30 +76,34 @@ public EditArticleCommand parse(String args) throws ParseException { return new EditArticleCommand(index, editArticleDescriptor); } - private Optional parseAuthorsForEdit(List authors) throws ParseException { + private Optional> parseAuthorsForEdit(Collection authors) throws ParseException { assert authors != null; - for (String author : authors) { - if (author.isEmpty()) { - return Optional.empty(); - } + if (authors.isEmpty()) { + return Optional.empty(); } - List authorSet = authors; - authorSet = authorSet.size() == 1 && authorSet.contains("") ? Collections.emptyList() : authorSet; + Collection authorSet = authors.size() == 1 && authors.contains("") ? Collections.emptySet() : authors; return Optional.of(ParserUtil.parseAuthors(authorSet)); } - private Optional parseSourcesForEdit(List sources) throws ParseException { + private Optional> parseSourcesForEdit(Collection sources) throws ParseException { assert sources != null; - for (String source : sources) { - if (source.isEmpty()) { - return Optional.empty(); - } + if (sources.isEmpty()) { + return Optional.empty(); } - List sourceSet = sources; - sourceSet = sourceSet.size() == 1 && sourceSet.contains("") ? Collections.emptyList() : sourceSet; + Collection sourceSet = sources.size() == 1 && sources.contains("") ? Collections.emptySet() : sources; return Optional.of(ParserUtil.parseSources(sourceSet)); } + private Optional> parseTagsForEdit(Collection tags) throws ParseException { + assert tags != null; + + if (tags.isEmpty()) { + return Optional.empty(); + } + Collection tagSet = tags.size() == 1 && tags.contains("") ? Collections.emptySet() : tags; + return Optional.of(ParserUtil.parseTags(tagSet)); + } + } diff --git a/src/main/java/seedu/address/logic/parser/ParserUtil.java b/src/main/java/seedu/address/logic/parser/ParserUtil.java index 265e74a70cf..fa5af537faa 100644 --- a/src/main/java/seedu/address/logic/parser/ParserUtil.java +++ b/src/main/java/seedu/address/logic/parser/ParserUtil.java @@ -11,13 +11,14 @@ import java.util.Collection; import java.util.Date; import java.util.HashSet; -import java.util.List; import java.util.Set; import seedu.address.commons.core.index.Index; import seedu.address.commons.util.StringUtil; import seedu.address.logic.parser.exceptions.ParseException; import seedu.address.model.article.Article; +import seedu.address.model.article.Author; +import seedu.address.model.article.Source; import seedu.address.model.person.Address; import seedu.address.model.person.Email; import seedu.address.model.person.Name; @@ -144,21 +145,23 @@ public static String parseTitle(String title) throws ParseException { /** * Parses a {@code String author} into a {@code Author}. */ - public static String parseAuthor(String author) throws ParseException { + public static Author parseAuthor(String author) throws ParseException { requireNonNull(author); String trimmedAuthor = author.trim(); - //removed the check for author validity - return trimmedAuthor; + if (!Tag.isValidTagName(trimmedAuthor)) { + throw new ParseException(Tag.MESSAGE_CONSTRAINTS); + } + return new Author(trimmedAuthor); } /** * Parses a {@code List authors} into a {@code String[]}. */ - public static String[] parseAuthors(List authors) throws ParseException { + public static Set parseAuthors(Collection authors) throws ParseException { requireNonNull(authors); - final String[] authorSet = new String[authors.size()]; - for (int i = 0; i < authors.size(); i++) { - authorSet[i] = parseAuthor(authors.get(i)); + final Set authorSet = new HashSet<>(); + for (String authorName : authors) { + authorSet.add(parseAuthor(authorName)); } return authorSet; } @@ -185,21 +188,23 @@ public static LocalDateTime parsePublicationDate(String publicationDate) throws /** * Parses a {@code String source} into a {@code Source}. */ - public static String parseSource(String source) throws ParseException { + public static Source parseSource(String source) throws ParseException { requireNonNull(source); String trimmedSource = source.trim(); - //removed the check for Source validity - return trimmedSource; + if (!Tag.isValidTagName(trimmedSource)) { + throw new ParseException(Tag.MESSAGE_CONSTRAINTS); + } + return new Source(trimmedSource); } /** * Parses a {@code List sources} into a {@code String[]}. */ - public static String[] parseSources(List sources) throws ParseException { + public static Set parseSources(Collection sources) throws ParseException { requireNonNull(sources); - final String[] sourceSet = new String[sources.size()]; - for (int i = 0; i < sources.size(); i++) { - sourceSet[i] = parseSource(sources.get(i)); + final Set sourceSet = new HashSet<>(); + for (String sourceName : sources) { + sourceSet.add(parseSource(sourceName)); } return sourceSet; } diff --git a/src/main/java/seedu/address/model/article/Article.java b/src/main/java/seedu/address/model/article/Article.java index 4a266a450fc..0e01466c04d 100644 --- a/src/main/java/seedu/address/model/article/Article.java +++ b/src/main/java/seedu/address/model/article/Article.java @@ -1,17 +1,26 @@ package seedu.address.model.article; +import static seedu.address.commons.util.CollectionUtil.requireAllNonNull; + import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +import seedu.address.commons.util.ToStringBuilder; +import seedu.address.model.tag.Tag; /** * Represents an article in the address book. */ public class Article { private final String title; - private final String[] authors; + private final Set authors = new HashSet<>(); private final LocalDateTime publicationDate; - private final String[] sources; - private final String category; + private final Set sources = new HashSet<>(); + private final Set tags = new HashSet<>(); /** * Enumeration of Status of an article. @@ -29,16 +38,17 @@ public enum Status { * @param authors the authors of the article. * @param publicationDate the date of publication. * @param sources the people interviewed. - * @param category the subject of the article. + * @param tags the subject of the article. * @param status the current status of the article. */ - public Article(String title, String[] authors, LocalDateTime publicationDate, - String[] sources, String category, Status status) { + public Article(String title, Set authors, LocalDateTime publicationDate, + Set sources, Set tags, Status status) { + requireAllNonNull(title, authors, publicationDate, sources, tags, status); this.title = title; - this.authors = authors; + this.authors.addAll(authors); this.publicationDate = publicationDate; - this.sources = sources; - this.category = category; + this.sources.addAll(sources); + this.tags.addAll(tags); this.status = status; } @@ -46,8 +56,8 @@ public String getTitle() { return this.title; } - public String[] getAuthors() { - return this.authors; + public Set getAuthors() { + return Collections.unmodifiableSet(authors); } public LocalDateTime getPublicationDate() { @@ -59,12 +69,12 @@ public String getPublicationDateAsString() { return this.publicationDate.format(formatter); } - public String[] getSources() { - return this.sources; + public Set getSources() { + return Collections.unmodifiableSet(sources); } - public String getCategory() { - return this.category; + public Set getTags() { + return Collections.unmodifiableSet(tags); } public Status getStatus() { @@ -84,7 +94,7 @@ public boolean isSameArticle(Article otherArticle) { /* * Same authors may have many drafts of same article. If it is not draft and has same title and authors, * consider it as same article - */ + */ } else if (otherArticle.getStatus() != Status.DRAFT && this.getStatus() != Status.DRAFT && otherArticle.getTitle().equals(this.title) && otherArticle.getAuthors() == this.authors) { return true; @@ -92,4 +102,41 @@ public boolean isSameArticle(Article otherArticle) { return false; } } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Article)) { + return false; + } + + Article otherArticle = (Article) other; + return title.equals(otherArticle.title) + && authors.equals(otherArticle.authors) + && sources.equals(otherArticle.sources) + && publicationDate.equals(otherArticle.publicationDate) + && tags.equals(otherArticle.tags) + && status.equals(otherArticle.status); + } + + @Override + public int hashCode() { + return Objects.hash(title, authors, publicationDate, sources, tags, status); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .add("title", title) + .add("authors", authors) + .add("publicationDate", publicationDate) + .add("sources", sources) + .add("tags", tags) + .add("status", status) + .toString(); + } } diff --git a/src/main/java/seedu/address/model/article/Author.java b/src/main/java/seedu/address/model/article/Author.java new file mode 100644 index 00000000000..7aefaa01577 --- /dev/null +++ b/src/main/java/seedu/address/model/article/Author.java @@ -0,0 +1,60 @@ +package seedu.address.model.article; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents an Author of an Article + */ +public class Author { + public static final String MESSAGE_CONSTRAINTS = "Author names should be alphanumeric"; + public static final String VALIDATION_REGEX = "[\\p{Alnum} ]+"; + + public final String authorName; + + /** + * Constructs a {@code authorName}. + * + * @param authorName A valid author name. + */ + public Author(String authorName) { + requireNonNull(authorName); + checkArgument(isValidAuthorName(authorName), MESSAGE_CONSTRAINTS); + this.authorName = authorName; + } + + /** + * Returns true if a given string is a valid author name. + * TODO: Map to a valid author name in the address book + */ + public static boolean isValidAuthorName(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Author)) { + return false; + } + + Author otherAuthor = (Author) other; + return authorName.equals(otherAuthor.authorName); + } + + @Override + public int hashCode() { + return authorName.hashCode(); + } + + /** + * Format state as text for viewing. + */ + public String toString() { + return '[' + authorName + ']'; + } +} diff --git a/src/main/java/seedu/address/model/article/Source.java b/src/main/java/seedu/address/model/article/Source.java new file mode 100644 index 00000000000..ec678899575 --- /dev/null +++ b/src/main/java/seedu/address/model/article/Source.java @@ -0,0 +1,60 @@ +package seedu.address.model.article; + +import static java.util.Objects.requireNonNull; +import static seedu.address.commons.util.AppUtil.checkArgument; + +/** + * Represents a Source (Contributor) to an Article + */ +public class Source { + public static final String MESSAGE_CONSTRAINTS = "Source names should be alphanumeric"; + public static final String VALIDATION_REGEX = "[\\p{Alnum} ]+"; + + public final String sourceName; + + /** + * Constructs a {@code sourceName}. + * + * @param sourceName A valid source name. + */ + public Source(String sourceName) { + requireNonNull(sourceName); + checkArgument(isValidSourceName(sourceName), MESSAGE_CONSTRAINTS); + this.sourceName = sourceName; + } + + /** + * Returns true if a given string is a valid source name. + * TODO: Map to a valid source name in the address book + */ + public static boolean isValidSourceName(String test) { + return test.matches(VALIDATION_REGEX); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + // instanceof handles nulls + if (!(other instanceof Source)) { + return false; + } + + Source otherSource = (Source) other; + return sourceName.equals(otherSource.sourceName); + } + + @Override + public int hashCode() { + return sourceName.hashCode(); + } + + /** + * Format state as text for viewing. + */ + public String toString() { + return '[' + sourceName + ']'; + } +} diff --git a/src/main/java/seedu/address/model/tag/Tag.java b/src/main/java/seedu/address/model/tag/Tag.java index f1a0d4e233b..ad0ffd35f00 100644 --- a/src/main/java/seedu/address/model/tag/Tag.java +++ b/src/main/java/seedu/address/model/tag/Tag.java @@ -10,7 +10,7 @@ public class Tag { public static final String MESSAGE_CONSTRAINTS = "Tags names should be alphanumeric"; - public static final String VALIDATION_REGEX = "\\p{Alnum}+"; + public static final String VALIDATION_REGEX = "[\\p{Alnum} ]+"; public final String tagName; diff --git a/src/main/java/seedu/address/model/util/SampleArticleDataUtil.java b/src/main/java/seedu/address/model/util/SampleArticleDataUtil.java index b00ac860e6a..fbe001596a4 100644 --- a/src/main/java/seedu/address/model/util/SampleArticleDataUtil.java +++ b/src/main/java/seedu/address/model/util/SampleArticleDataUtil.java @@ -1,11 +1,17 @@ package seedu.address.model.util; import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.Set; +import java.util.stream.Collectors; import seedu.address.model.ArticleBook; import seedu.address.model.ReadOnlyArticleBook; import seedu.address.model.article.Article; import seedu.address.model.article.Article.Status; +import seedu.address.model.article.Author; +import seedu.address.model.article.Source; +import seedu.address.model.tag.Tag; /** * Contains utility methods for populating {@code ArticleBook} with sample data. @@ -14,8 +20,9 @@ public class SampleArticleDataUtil { public static Article[] getSampleArticles() { return new Article[]{ - new Article("The epitome of pain and suffering by NUS CS students.", new String[] {"Alice", "Bob"}, - LocalDateTime.now(), new String[] {"NUS Computing Club"}, "Student Life", Status.PUBLISHED) + new Article("The epitome of pain and suffering by NUS CS students.", getAuthorSet("Alice", "Bob"), + LocalDateTime.now(), getSourceSet("NUS Computing Club"), getTagSet("Student Life"), + Status.PUBLISHED) }; } @@ -26,4 +33,31 @@ public static ReadOnlyArticleBook getSampleArticleBook() { } return sampleAb; } + + /** + * Returns an author set containing the list of strings given. + */ + public static Set getAuthorSet(String... strings) { + return Arrays.stream(strings) + .map(Author::new) + .collect(Collectors.toSet()); + } + + /** + * Returns a source set containing the list of strings given. + */ + public static Set getSourceSet(String... strings) { + return Arrays.stream(strings) + .map(Source::new) + .collect(Collectors.toSet()); + } + + /** + * Returns a tag set containing the list of strings given. + */ + public static Set getTagSet(String... strings) { + return Arrays.stream(strings) + .map(Tag::new) + .collect(Collectors.toSet()); + } } diff --git a/src/main/java/seedu/address/storage/JsonAdaptedArticle.java b/src/main/java/seedu/address/storage/JsonAdaptedArticle.java index 1d8524ba4b3..702c449bbf9 100644 --- a/src/main/java/seedu/address/storage/JsonAdaptedArticle.java +++ b/src/main/java/seedu/address/storage/JsonAdaptedArticle.java @@ -1,22 +1,31 @@ package seedu.address.storage; import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import seedu.address.commons.exceptions.IllegalValueException; import seedu.address.model.article.Article; +import seedu.address.model.article.Author; +import seedu.address.model.article.Source; +import seedu.address.model.tag.Tag; /** * Jackson-friendly version of {@link Article}. */ public class JsonAdaptedArticle { private final String title; - private final String[] authors; + private final List authors = new ArrayList<>(); + //Should be able to be null private final LocalDateTime publicationDate; - private final String[] sources; - private final String category; + private final List sources = new ArrayList<>(); + private final List tags = new ArrayList<>(); private final Article.Status status; /** @@ -26,19 +35,27 @@ public class JsonAdaptedArticle { * @param authors * @param publicationDate * @param sources - * @param category + * @param tags * @param status */ @JsonCreator - public JsonAdaptedArticle(@JsonProperty("title") String title, @JsonProperty("authors") String[] authors, + public JsonAdaptedArticle(@JsonProperty("title") String title, + @JsonProperty("authors") List authors, @JsonProperty("publicationDate") LocalDateTime publicationDate, - @JsonProperty("sources") String[] sources, @JsonProperty("category") String category, + @JsonProperty("sources") List sources, + @JsonProperty("tags") List tags, @JsonProperty("status") Article.Status status) { this.title = title; - this.authors = authors; + if (authors != null) { + this.authors.addAll(authors); + } this.publicationDate = publicationDate; - this.sources = sources; - this.category = category; + if (sources != null) { + this.sources.addAll(sources); + } + if (tags != null) { + this.tags.addAll(tags); + } this.status = status; } /** @@ -47,10 +64,16 @@ public JsonAdaptedArticle(@JsonProperty("title") String title, @JsonProperty("au */ public JsonAdaptedArticle(Article sourceArticle) { title = sourceArticle.getTitle(); - authors = sourceArticle.getAuthors(); + authors.addAll(sourceArticle.getAuthors().stream() + .map(JsonAdaptedAuthor::new) + .collect(Collectors.toList())); publicationDate = sourceArticle.getPublicationDate(); - sources = sourceArticle.getSources(); - category = sourceArticle.getCategory(); + sources.addAll(sourceArticle.getSources().stream() + .map(JsonAdaptedSource::new) + .collect(Collectors.toList())); + tags.addAll(sourceArticle.getTags().stream() + .map(JsonAdaptedTag::new) + .collect(Collectors.toList())); status = sourceArticle.getStatus(); } @@ -60,21 +83,33 @@ public JsonAdaptedArticle(Article sourceArticle) { * @throws IllegalValueException if data constraints are violated */ public Article toModelType() throws IllegalValueException { + final List articleTags = new ArrayList<>(); + for (JsonAdaptedTag tag : tags) { + articleTags.add(tag.toModelType()); + } + final List articleAuthors = new ArrayList<>(); + for (JsonAdaptedAuthor author : authors) { + articleAuthors.add(author.toModelType()); + } + final List articleSources = new ArrayList<>(); + for (JsonAdaptedSource source : sources) { + articleSources.add(source.toModelType()); + } + if (title == null) { throw new IllegalValueException("The title is missing"); } - if (authors == null || authors.length == 0) { - throw new IllegalValueException("The author[s] is/are invalid"); - } + + final Set modelAuthors = new HashSet<>(articleAuthors); + if (publicationDate == null) { throw new IllegalValueException("The publication date is invalid"); } - if (sources == null || sources.length == 0) { - throw new IllegalValueException("The source is invalid"); - } - if (category == null) { - throw new IllegalValueException("The categories are invalid"); - } - return new Article(title, authors, publicationDate, sources, category, status); + + final Set modelSources = new HashSet<>(articleSources); + + final Set modelTags = new HashSet<>(articleTags); + + return new Article(title, modelAuthors, publicationDate, modelSources, modelTags, status); } } diff --git a/src/main/java/seedu/address/storage/JsonAdaptedAuthor.java b/src/main/java/seedu/address/storage/JsonAdaptedAuthor.java new file mode 100644 index 00000000000..101c56f7fcd --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedAuthor.java @@ -0,0 +1,46 @@ +package seedu.address.storage; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.article.Author; + +/** + * Jackson-friendly version of {@link Author}. + */ +public class JsonAdaptedAuthor { + private final String authorName; + + /** + * Constructs a {@code JsonAdaptedAuthor} with the given {@code authorName}. + */ + @JsonCreator + public JsonAdaptedAuthor(String authorName) { + this.authorName = authorName; + } + + /** + * Converts a given {@code Author} into this class for Jackson use. + */ + public JsonAdaptedAuthor(Author source) { + authorName = source.authorName; + } + + @JsonValue + public String getAuthorName() { + return authorName; + } + + /** + * Converts this Jackson-friendly adapted author object into the model's {@code Author} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted author. + */ + public Author toModelType() throws IllegalValueException { + if (!Author.isValidAuthorName(authorName)) { + throw new IllegalValueException(Author.MESSAGE_CONSTRAINTS); + } + return new Author(authorName); + } +} diff --git a/src/main/java/seedu/address/storage/JsonAdaptedSource.java b/src/main/java/seedu/address/storage/JsonAdaptedSource.java new file mode 100644 index 00000000000..cc87e36834d --- /dev/null +++ b/src/main/java/seedu/address/storage/JsonAdaptedSource.java @@ -0,0 +1,46 @@ +package seedu.address.storage; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +import seedu.address.commons.exceptions.IllegalValueException; +import seedu.address.model.article.Source; + +/** + * Jackson-friendly version of {@link Source}. + */ +public class JsonAdaptedSource { + private final String sourceName; + + /** + * Constructs a {@code JsonAdaptedSource} with the given {@code sourceName}. + */ + @JsonCreator + public JsonAdaptedSource(String sourceName) { + this.sourceName = sourceName; + } + + /** + * Converts a given {@code Source} into this class for Jackson use. + */ + public JsonAdaptedSource(Source source) { + sourceName = source.sourceName; + } + + @JsonValue + public String getSourceName() { + return sourceName; + } + + /** + * Converts this Jackson-friendly adapted source object into the model's {@code Source} object. + * + * @throws IllegalValueException if there were any data constraints violated in the adapted source. + */ + public Source toModelType() throws IllegalValueException { + if (!Source.isValidSourceName(sourceName)) { + throw new IllegalValueException(Source.MESSAGE_CONSTRAINTS); + } + return new Source(sourceName); + } +} diff --git a/src/main/java/seedu/address/ui/ArticleCard.java b/src/main/java/seedu/address/ui/ArticleCard.java index 3e838a1b169..99f5d0e9851 100644 --- a/src/main/java/seedu/address/ui/ArticleCard.java +++ b/src/main/java/seedu/address/ui/ArticleCard.java @@ -1,6 +1,6 @@ package seedu.address.ui; -import java.util.Arrays; +import java.util.Comparator; import javafx.fxml.FXML; import javafx.scene.control.Label; @@ -10,7 +10,7 @@ import seedu.address.model.article.Article; /** - * An UI component that displays information of an {@code Article}. + * A UI component that displays information of an {@code Article}. */ public class ArticleCard extends UiPart { @@ -37,9 +37,9 @@ public class ArticleCard extends UiPart { @FXML private FlowPane sources; @FXML - private Label publicationDate; + private FlowPane tags; @FXML - private Label category; + private Label publicationDate; @FXML private Label status; @@ -52,14 +52,18 @@ public ArticleCard(Article article, int displayedIndex) { this.article = article; id.setText(displayedIndex + ". "); title.setText(article.getTitle()); - Arrays.stream(article.getAuthors()) - .sorted() - .forEach(author -> authors.getChildren().add(new Label(author))); - Arrays.stream(article.getSources()) - .sorted() - .forEach(source -> sources.getChildren().add(new Label(source))); + + article.getAuthors().stream() + .sorted(Comparator.comparing(author -> author.authorName)) + .forEach(author -> authors.getChildren().add(new Label(author.authorName))); + article.getSources().stream() + .sorted(Comparator.comparing(source -> source.sourceName)) + .forEach(source -> sources.getChildren().add(new Label(source.sourceName))); + article.getTags().stream() + .sorted(Comparator.comparing(tag -> tag.tagName)) + .forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + publicationDate.setText(article.getPublicationDateAsString()); - category.setText(article.getCategory()); status.setText(article.getStatus().toString()); } } diff --git a/src/main/resources/view/ArticleListCard.fxml b/src/main/resources/view/ArticleListCard.fxml index 606a05a61b9..2b9a73effa9 100644 --- a/src/main/resources/view/ArticleListCard.fxml +++ b/src/main/resources/view/ArticleListCard.fxml @@ -27,10 +27,12 @@