package er.taggable;
import com.webobjects.eocontrol.EOEditingContext;
import com.webobjects.foundation.NSArray;
import er.extensions.eof.ERXGenericRecord;
import er.extensions.eof.ERXQ;
import er.taggable.model.ERTag;
/**
* ERTaggable provides a wrapper around a taggable EO, extending it with
* tagging-related methods.
*
* Typically you would provide a cover method from your EO to an
* instance of an ERTaggable:
*
* <pre><code>
* public class Person extends _Person {
* ...
* public ERTaggable<Person> taggable() {
* return ERTaggable.taggable(this);
* }
* }
* </code></pre>
*
* @author mschrag
*
* @param <T> the type of EO that is being wrapped
*/
public class ERTaggable<T extends ERXGenericRecord> {
private T _item;
private ERTaggableEntity<T> _entity;
/**
* Constructs an ERTaggable wrapper.
*
* @param entity the ERTaggableEntity that corresponds to this item's entity
* @param item the item to wrap
*/
protected ERTaggable(ERTaggableEntity<T> entity, T item) {
_entity = entity;
_item = item;
}
@Override
public int hashCode() {
return _item.hashCode();
}
@Override
public boolean equals(Object obj) {
return (obj instanceof ERTaggable && ((ERTaggable<?>)obj)._item.equals(_item));
}
/**
* A factory method for generating a taggable from an EO.
*
* @param <T> the type of the EO being wrapped
* @param eo the EO being wrapped
* @return a taggable wrapper around the EO
*/
public static <T extends ERXGenericRecord> ERTaggable<T> taggable(T eo) {
return ERTaggableEntity.taggableEntity(eo).taggable(eo);
}
/**
* Returns the tagged item that this is taggable is wrapping.
*
* @return the tagged item
*/
public T item() {
return _item;
}
/**
* Returns the taggable entity for this taggable.
*
* @return the taggable entity for this taggable
*/
public ERTaggableEntity<T> taggableEntity() {
return _entity;
}
/**
* Returns an array of ERTags associated with this item.
*
* @return an array of ERTags associated with this item
*/
@SuppressWarnings("unchecked")
public NSArray<ERTag> tags() {
String tagsRelationshipName = _entity.tagsRelationshipName();
return (NSArray<ERTag>) _item.valueForKeyPath(tagsRelationshipName);
}
/**
* Removes all of the tags associated with this item.
*/
public void clearTags() {
for (ERTag tag : tags().immutableClone()) {
removeTag(tag);
}
}
/**
* This method removes tags from the target object, by parsing the tags parameter
* into Tag object instances and removing them from the tag collection of the object if they exist.
*
* @param tags the tags to remove (String to tokenize, NSArray<String>, etc)
*/
public void removeTags(Object tags) {
NSArray<ERTag> erTags = tags();
NSArray<ERTag> matchingTags = ERXQ.filtered(erTags, ERTag.NAME.in(_entity.splitTagNames(tags)));
for (ERTag tag : matchingTags) {
removeTag(tag);
}
}
/**
* This method removes tags from the target object, by looking up the corresponding
* Tag object instances and removing them from the tag collection of the object if they exist.
*
* @param tagName the tag to remove (String to tokenize, NSArray<String>, etc)
*/
public void removeTagNamed(String tagName) {
NSArray<ERTag> erTags = tags();
NSArray<ERTag> matchingTags = ERXQ.filtered(erTags, ERTag.NAME.is(tagName));
for (ERTag tag : matchingTags) {
removeTag(tag);
}
}
/**
* This method applies tags to the target object, by looking up the corresponding
* Tag object instances and adding it to the tag collection of the object.
* If the tag name already exists in the tags table, it just adds a relationship
* to the existing tag record. If it doesn't exist, it then creates a new
* Tag record for it.
*
* This is equivalent to addTags(false, tags).
*
* @param tagName the tag name to add
*/
public void addTagNamed(String tagName) {
addTags(false, ERTag.escapeTagNamed(tagName));
}
/**
* This method applies tags to the target object, by parsing the tags parameter
* into Tag object instances and adding them to the tag collection of the object.
* If the tag name already exists in the tags table, it just adds a relationship
* to the existing tag record. If it doesn't exist, it then creates a new
* Tag record for it.
*
* This is equivalent to addTags(false, tags).
*
* @param tags the tags to add (String to tokenize, NSArray<String>, etc)
*/
public void addTags(Object tags) {
addTags(false, tags);
}
/**
* This method applies tags to the target object, by parsing the tags parameter
* into Tag object instances and adding them to the tag collection of the object.
* If the tag name already exists in the tags table, it just adds a relationship
* to the existing tag record. If it doesn't exist, it then creates a new
* Tag record for it.
*
* @param tags the tags to add (String to tokenize, NSArray<String>, etc)
* @param clear if true, existing tags will be removed first
*/
public void addTags(boolean clear, Object tags) {
// clear the collection if appropriate
if (clear) {
clearTags();
}
NSArray<ERTag> erTags = tags();
EOEditingContext editingContext = _item.editingContext();
// append the tag names to the collection
for (String tagName : _entity.splitTagNames(tags)) {
// ensure that tag names don't get duplicated
ERTag tag = _entity.fetchTagNamed(editingContext, tagName, true);
if (!erTags.containsObject(tag)) {
addTag(tag);
}
}
}
/**
* Adds the tag to this item. This is the single method that to override
* if you need to perform some additional operations.
*
* @param tag the tag to add
*/
protected void addTag(ERTag tag) {
_item.addObjectToBothSidesOfRelationshipWithKey(tag, _entity.tagsRelationshipName());
}
/**
* Removes the tag from this item. This is the single method that to override
* if you need to perform some additional operations.
*
* @param tag the tag to remove
*/
protected void removeTag(ERTag tag) {
_item.removeObjectFromBothSidesOfRelationshipWithKey(tag, _entity.tagsRelationshipName());
}
/**
* Clears the current tags collection and sets the tag names for this object.
* Equivalent of calling addTags(tags, true).
*
* @param tags the tags to add (String to tokenize, NSArray<String>, etc)
*/
public void setTags(Object tags) {
addTags(true, tags);
}
/**
* Returns an array of strings containing the tag names applied to this object.
*
* @return an array of strings containing the tag names applied to this object
*/
@SuppressWarnings("unchecked")
public NSArray<String> tagNames() {
return (NSArray<String>) tags().valueForKey("name");
}
/**
* Checks to see if this object has been tagged with the given tag name.
*
* @param tagName the tag name to check
* @return true if this eo is tagged with the given tag name, false otherwise
*/
public boolean isTaggedWith(String tagName) {
return ERXQ.filtered(tags(), ERTag.NAME.is(tagName)).count() > 0;
}
/**
* Checks to see if this object has been tagged with all the given tags.
*
* @param tags the tags to add (String to tokenize, NSArray<String>, etc)
* @return true if this eo is tagged with all of the given tag names, false otherwise
*/
public boolean isTaggedWithAll(Object tags) {
NSArray<String> tagNames = _entity.splitTagNames(tags);
return ERXQ.filtered(tags(), ERTag.NAME.in(tagNames)).count() == tagNames.count();
}
/**
* Checks to see if this object has been tagged with any of the given tags.
*
* @param tags the tags to add (String to tokenize, NSArray<String>, etc)
* @return true if this eo is tagged with any of the given tag names, false otherwise
*/
public boolean isTaggedWithAny(Object tags) {
NSArray<String> tagNames = _entity.splitTagNames(tags);
return ERXQ.filtered(tags(), ERTag.NAME.in(tagNames)).count() > 0;
}
// public NSArray<String> taggedRelated(ERTagOptions options) {
// return _entity.findRelatedTagged(this, options);
// }
}