Skip to content

Commit cc4c48d

Browse files
committed
searchfilters: a framework to create sort and content filter objects
FilterContainer.java: ===================== This class is a container that keeps either content filters or sort filters organized. Sort/content filters ({@link FilterItem}s) are organized within {@link FilterGroup}s. FilterGroup.java: ================= This class represents a filter category/group. For example 'Sort order'. Its main purpose is to host a bunch of {@link FilterItem}s that belong to that group. Eg. 'Relevance', 'Views', 'Rating' FilterItem.java: ================ This class represents a single filter option. *More in detail:* For example youtube offers the filter group 'Sort order'. This group consists of filter options like 'Relevance', 'Views', 'Rating' etc. -> for each filter option a FilterItem has to be created. SearchFiltersBase.java: ======================= The base class for every service describing their {@link FilterItem}s, {@link FilterGroup}s, the relation between content filters and sort filters.
1 parent a822e91 commit cc4c48d

4 files changed

Lines changed: 442 additions & 0 deletions

File tree

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Created by evermind-zz 2022, licensed GNU GPL version 3 or later -->
2+
3+
package org.schabi.newpipe.extractor.search.filter;
4+
5+
import java.util.HashMap;
6+
import java.util.Map;
7+
8+
/**
9+
* This class is a container that keeps either content filters or sort filters organized.
10+
*
11+
* Sort/content filters ({@link FilterItem}s) are organized within {@link FilterGroup}s.
12+
*/
13+
public final class FilterContainer {
14+
15+
/**
16+
* Mark {@link FilterItem}'s and {@link FilterGroup}'s which identifier is not (yet) set.
17+
*/
18+
public static final int ITEM_IDENTIFIER_UNKNOWN = -1;
19+
20+
private final Map<Integer, FilterItem> idToFilterItem = new HashMap<>();
21+
private final FilterGroup[] filterGroups;
22+
23+
public FilterContainer(final FilterGroup[] filterGroups) {
24+
this.filterGroups = filterGroups;
25+
for (final FilterGroup group : filterGroups) {
26+
for (final FilterItem item : group.getFilterItems()) {
27+
idToFilterItem.put(item.getIdentifier(), item);
28+
}
29+
}
30+
}
31+
32+
/**
33+
* Quickly access a {@link FilterItem} that belongs to this {@link FilterContainer}.
34+
*
35+
* @param id the identifier of the {@link FilterItem}
36+
* @return
37+
*/
38+
public FilterItem getFilterItem(final int id) {
39+
return idToFilterItem.get(id);
40+
}
41+
42+
public FilterGroup[] getFilterGroups() {
43+
return filterGroups;
44+
}
45+
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// Created by evermind-zz 2022, licensed GNU GPL version 3 or later -->
2+
3+
package org.schabi.newpipe.extractor.search.filter;
4+
5+
import java.util.HashMap;
6+
import java.util.Map;
7+
8+
/**
9+
* This class represents a filter category/group. For example 'Sort order'.
10+
* <p>
11+
* Its main purpose is to host a bunch of {@link FilterItem}s that belong to that
12+
* group. Eg. 'Relevance', 'Views', 'Rating'
13+
*/
14+
public final class FilterGroup {
15+
16+
/**
17+
* {@link #getIdentifier()}
18+
*/
19+
private final int identifier;
20+
21+
/**
22+
* The name of the filter group that the user will see
23+
*/
24+
private final String groupName;
25+
26+
/**
27+
* Specify whether only one item can be selected in this group at a time.
28+
*/
29+
private final boolean onlyOneCheckable;
30+
31+
/**
32+
* Each group may have a default value that should be selected.
33+
* <p>
34+
* It should be set to the the {@link FilterItem}'s id. If there is no default option
35+
* it should be set to {@link FilterContainer#ITEM_IDENTIFIER_UNKNOWN}
36+
*/
37+
private final int defaultSelectedFilterId;
38+
39+
/**
40+
* The filter items that belong to this {@link FilterGroup}.
41+
*/
42+
private final FilterItem[] filterItems;
43+
44+
/**
45+
* {@link #getAllSortFilters()}.
46+
*/
47+
private final FilterContainer allSortFilters;
48+
49+
private FilterGroup(final int identifier,
50+
final String groupName,
51+
final boolean onlyOneCheckable,
52+
final int defaultSelectedFilterId,
53+
final FilterItem[] filterItems,
54+
final FilterContainer allSortFilters) {
55+
this.identifier = identifier;
56+
this.groupName = groupName;
57+
this.onlyOneCheckable = onlyOneCheckable;
58+
this.defaultSelectedFilterId = defaultSelectedFilterId;
59+
this.filterItems = filterItems;
60+
this.allSortFilters = allSortFilters;
61+
}
62+
63+
64+
/**
65+
* If this group is a content filter and has corresponding sort filters, this
66+
* {@link FilterContainer} contains all available sort filters for this group.
67+
*
68+
* @return may be null as not all {@link FilterGroup}s have sort filters.
69+
*/
70+
public FilterContainer getAllSortFilters() {
71+
return allSortFilters;
72+
}
73+
74+
/**
75+
* {@link FilterItem#getIdentifier()}
76+
*/
77+
public int getIdentifier() {
78+
return this.identifier;
79+
}
80+
81+
/**
82+
* {@link #groupName}
83+
*/
84+
public String getName() {
85+
return groupName;
86+
}
87+
88+
/**
89+
* {@link #defaultSelectedFilterId}
90+
*/
91+
public int getDefaultSelectedFilterId() {
92+
return defaultSelectedFilterId;
93+
}
94+
95+
/**
96+
* {@link #filterItems}
97+
*/
98+
public FilterItem[] getFilterItems() {
99+
return filterItems;
100+
}
101+
102+
/**
103+
* {@link #onlyOneCheckable}
104+
*/
105+
public boolean isOnlyOneCheckable() {
106+
return onlyOneCheckable;
107+
}
108+
109+
/**
110+
* Factory for building {@link FilterGroup}s.
111+
* <p>
112+
* Each service should only have one instance.
113+
* This is implemented in {@link SearchFiltersBase}
114+
*/
115+
public static class Factory {
116+
117+
/**
118+
* A map that has all {@link FilterItem}s that are relevant for one service. Eg. Youtube
119+
*/
120+
public final Map<Integer, FilterItem> filtersMap = new HashMap<>();
121+
122+
public Factory() {
123+
}
124+
125+
/**
126+
* Check if a {@link FilterItem} has a unique id.
127+
*
128+
* @param filterItems a map with the previously added {@link FilterItem}'s to compare with.
129+
* @param item the new {@link FilterItem} that should be added.
130+
*/
131+
void uniqueIdChecker(final Map<Integer, FilterItem> filterItems,
132+
final FilterItem item) {
133+
134+
if (item.getIdentifier() == FilterContainer.ITEM_IDENTIFIER_UNKNOWN
135+
&& !(item instanceof FilterItem.DividerItem)) {
136+
throw new RuntimeException("Filter ID "
137+
+ item.getIdentifier() + " aka FilterContainer.ITEM_IDENTIFIER_UNKNOWN"
138+
+ " for \"" + item.getName() + "\" not allowed");
139+
}
140+
141+
if (filterItems.containsKey(item.getIdentifier())) {
142+
final FilterItem storedItem = filterItems.get(item.getIdentifier());
143+
throw new RuntimeException("Filter ID "
144+
+ item.getIdentifier() + " for \"" + item.getName()
145+
+ "\" already taken from \"" + storedItem.getName() + "\"");
146+
}
147+
}
148+
149+
/**
150+
* Add a new {@link FilterItem} that is relevant to this service.
151+
* <p>
152+
* The {@link FilterItem}s are accessible by their id via {@link #getFilterForId(int)}
153+
*
154+
* @param filter the new {@link FilterItem} to be added to the factory.
155+
* @return the identifier of the {@link FilterItem}
156+
*/
157+
public int addFilterItem(final FilterItem filter) {
158+
uniqueIdChecker(filtersMap, filter);
159+
filtersMap.put(filter.getIdentifier(), filter);
160+
return filter.getIdentifier();
161+
}
162+
163+
public FilterGroup createFilterGroup(final int identifier,
164+
final String groupName,
165+
final boolean onlyOneCheckable,
166+
final int defaultSelectedFilterId,
167+
final FilterItem[] filterItems,
168+
final FilterContainer allSortFilters) {
169+
return new FilterGroup(identifier, groupName, onlyOneCheckable,
170+
defaultSelectedFilterId, filterItems, allSortFilters);
171+
}
172+
173+
/**
174+
* Get previously via {@link #addFilterItem(FilterItem)} added {@link FilterItem}.
175+
*
176+
* @param identifier the id of the desired {@link FilterItem}
177+
* @return the desired {@link FilterItem}
178+
*/
179+
public FilterItem getFilterForId(final int identifier) {
180+
return filtersMap.get(identifier);
181+
}
182+
}
183+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Created by evermind-zz 2022, licensed GNU GPL version 3 or later -->
2+
3+
package org.schabi.newpipe.extractor.search.filter;
4+
5+
/**
6+
* This class represents a single filter option.
7+
* <p>
8+
* <b>More in detail:</b>
9+
* For example youtube offers the filter group 'Sort order'. This group
10+
* consists of filter options like 'Relevance', 'Views', 'Rating' etc.
11+
* -> for each filter option a FilterItem has to be created.
12+
*/
13+
public class FilterItem {
14+
15+
/**
16+
* The name of the filter option, that will be visible to the user.
17+
*/
18+
private final String name;
19+
20+
/**
21+
* A sequential unique number identifier.
22+
*
23+
* <b>Note:</b>
24+
* - the uniqueness applies only to each service.
25+
* - Never reuse a previously unique number for another filter option/group
26+
* (Otherwise implementation in the client that may implement to store some user
27+
* specified defaults could have an undefined behaviour while loading).
28+
*/
29+
private final int identifier;
30+
31+
public FilterItem(final int identifier, final String name) {
32+
this.identifier = identifier;
33+
this.name = name;
34+
}
35+
36+
/**
37+
* @return {@link #identifier}
38+
*/
39+
public int getIdentifier() {
40+
return this.identifier;
41+
}
42+
43+
/**
44+
* @return {@link #name}
45+
*/
46+
public String getName() {
47+
return this.name;
48+
}
49+
50+
/**
51+
* This class is used to have a sub title divider between regular {@link FilterItem}s.
52+
*/
53+
public static class DividerItem extends FilterItem {
54+
55+
public DividerItem(final String name) {
56+
super(FilterContainer.ITEM_IDENTIFIER_UNKNOWN, name);
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)